轉載請注意:http://blog.csdn.net/wjzj000/article/details/77931540
本菜開源的一個自己寫的Demo,希望能給Androider們有所幫助,水準有限,見諒見諒…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的優秀架構于一體,全部拆離不含任何額外的庫導入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)
寫在前面
今天在看公司項目的時候,發現了一個有意思的效果。app中有一個鎖屏的效果,殺死app後,鎖屏仍然存在,說明鎖屏運作在另一個程序中。
然後厚顔無恥的占用了安卓組boss的工作的時間,讓他給講了一下程序間通訊的原理。boss很有人類靈魂工程師的天賦,了解到我沒涉及到c層的東西,是以并沒有深到c層,而是在應用層,展開了一頓靈魂深處的縫縫補補…
那麼這篇部落格存在的意義就是在boss講授的基礎上增加自己的了解并記錄下來。
開始
Messenger程序間通訊
我們先看一段官方文檔對Messenger介紹:
如需讓接口跨不同的程序工作,則可使用 Messenger 為服務建立接口。服務可以這種方式定義對應于不同類型 Message 對象的 Handler。此 Handler 是 Messenger 的基礎,後者随後可與用戶端分享一個 IBinder,進而讓用戶端能利用 Message 對象向服務發送指令。此外,用戶端還可定義自有 Messenger,以便服務回傳消息。
這是執行程序間通信 (IPC) 的最簡單方法,因為 Messenger 會在單一線程中建立包含所有請求的隊列,這樣您就不必對服務進行線程安全設計。
https://developer.android.google.cn/guide/components/bound-services.html
簡單叙述一下效果:就是Activity的程序向另一個程序的Service傳遞一個自定義的類,類内部包含一個x,一個y,由另一個程序的Service計算完畢後回傳給我們的Activity。
官方文檔是Activity給遠端的Service發送消息,這裡稍作修改,變成Service給Activity發送消息:
雙向互通的思路很簡單:既然Activity向Service通信是通過Service回調過來的Messenger,那麼我們就在Activity中得到Service的Messenger時,通過這個Messenger把在Activity端生成的Messenger,send過去。這樣Service就可以通過Activity的Messenger向我們的Activity發送消息了。
Tips:
Messenger使用Message進行傳遞消息,Message如果涉及到跨程序,那麼攜帶的資料必須得失Parcelable類型的,是以Serializable是不行的(Can’t marshal non-Parcelable objects across processes.)。
是以String就不能以直接使用Message進行跨程序。這裡我們可以使用Bundle做資料傳遞。
public class RemoteService extends Service {
static final int ACTIVITY_MESSENGER = ;
static final String SEND_CONTENT = "send_content";
private Messenger activityMessenger;
//建構Messenger,并用來響應綁定元件發送的消息。
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ACTIVITY_MESSENGER:
if (activityMessenger==null){
//得到綁定元件發過來的它的Messenger,有了這個Messenger,服務就可以向綁定元件發送消息了。
activityMessenger= msg.replyTo;
Message message=Message.obtain();
message.what=;
//此處僅僅發送一個String,但是String不是Parcelable對象,不能使用Message進行跨程序,是以這裡用Bundle進行處理。
Bundle bundle=new Bundle();
bundle.putString(SEND_CONTENT,"我是來自服務程序的...");
message.setData(bundle);
try {
activityMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
default:
super.handleMessage(msg);
}
}
}
//聲明一個自己的Messenger,用于給綁定服務的元件發送消息。
final Messenger mMessenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
//回調ServiceConnection,讓綁定服務的元件得到Messenger對象
return mMessenger.getBinder();
}
}
//用于建構元件的Messenger,并響應服務發送過來的消息
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what==){
tvShow.setText(msg.getData().getString(RemoteService.SEND_CONTENT));
}
}
};
private Messenger messenger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShow= (TextView) findViewById(R.id.tv_show);
tvShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//建構自己的Messenger對象,通過Service回調過來的Messenger把自己的Messenger發送給Service,使Service擁有給自己發送消息的能力。
Messenger activityMessenger=new Messenger(handler);
Message message=Message.obtain();
message.what=RemoteService.ACTIVITY_MESSENGER;
message.replyTo=activityMessenger;
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//擷取Service端的Messenger對象
messenger = new Messenger(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
messenger = null;
}
};
Intent toSerivce = new Intent(this, RemoteService.class);
bindService(toSerivce,conn, Context.BIND_AUTO_CREATE);
}
接下來讓我們看一看AIDL方式的程序通訊。
AIDL程序間通訊
簡單叙述一下效果:就是Activity的程序向另一個程序的Service傳遞一個自定義的類,類内部包含一個x,一個y,由另一個程序的Service計算完畢後回傳給我們的Activity。
首先我們先new一個AIDL檔案:

![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1TPB10dBpWY2AnMkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DN0cDOxcTNwETMxkDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
有幾個tips:
1、要注意包名問題,需要保持和主項目一直,是以為了不出問題,我們還是使用系統的new AIDL的方式,交給系統去處理吧。
2、如果我們需要用到非基礎類型,也就是一些類的時候,我們同樣要聲明同名的AIDL檔案,并且不是使用inteface,而是parcelable(下文會有舉例)。這裡有個先後順序的問題:如果先聲明了class,那麼聲明AIDL的時候系統會提示換個名字。解決方法:1、先聲明AIDL,在聲明class。2、随便給AIDL起個名字,然後再改成class的同名。
生成的第一個AIDL檔案:
package com.yang.myapplication;
//這個就是我們使用的對應class的AIDL,雖然是同一個包下,但也要導入!!
import com.yang.myapplication.RemoteBean;
interface IMyAdd {
//注意這裡的in,沒有的話無法編譯通過。
int add(in RemoteBean bean);
}
第二個AIDL,也就是我們的同名class的AIDL:
package com.yang.myapplication;
parcelable RemoteBean;
然後是我們的class的RemoteBean,因為涉及到程序間通訊,是以我們要對這個類進行序列化:Parcelable(Android特有的序列化方式,性能好,相對實作比較複雜,但是也無所謂的,AS會提示自動都生成) / Serializable(Java):
public class RemoteBean implements Parcelable{
public int x;
public int y;
public RemoteBean(){
}
protected RemoteBean(Parcel in) {
x = in.readInt();
y = in.readInt();
}
public static final Creator<RemoteBean> CREATOR = new Creator<RemoteBean>() {
@Override
public RemoteBean createFromParcel(Parcel in) {
return new RemoteBean(in);
}
@Override
public RemoteBean[] newArray(int size) {
return new RemoteBean[size];
}
};
@Override
public int describeContents() {
return ;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(x);
parcel.writeInt(y);
}
}
定義一個Service:
注冊Service設定另一個程序運作:
<service android:name=".RemoteService"
android:process=":remote"/>
public class RemoteService extends Service {
private IBinder iBinder = new MyAddStub();
@Nullable
@Override
public IBinder onBind(Intent intent) {
//傳回我們的Stub對象
return iBinder;
}
//注意這裡我們繼承了AIDL檔案編譯後生成的同名class對象的一個内部類Stub(具體作用會在詳解時展開)
private static final class MyAddStub extends IMyAdd.Stub {
@Override
public int add(RemoteBean bean) throws RemoteException {
return bean.x + bean.y;
}
}
}
Activity中使用:
這裡随便給一個View設定一個監聽事件:
tvShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
RemoteBean bean=new RemoteBean();
bean.x=;
bean.y=;
tvShow.setText("遠端程序提供的服務:"+iMyAdd.add(bean));
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
ServiceConnection conn=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//注意!這裡是程序間通訊和非程序間通訊的差別
iMyAdd = IMyAdd.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
iMyAdd=null;
}
};
Intent toSerivce = new Intent(this, RemoteService.class);
bindService(toSerivce,conn, Context.BIND_AUTO_CREATE);
到這裡,我們這個程序間通訊的代碼套路就完成了。接下來的部分,就開始分析程序間通訊的原理。但是由于篇幅過長,解析篇的内容将在下部分中展開。
尾聲
最後希望各位看官可以star我的GitHub,三叩九拜,滿地打滾求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp##