天天看點

從Service中了解程序間通信Messenger / AIDL(上)代碼套路寫在前面開始尾聲

轉載請注意: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檔案:



從Service中了解程式間通信Messenger / AIDL(上)代碼套路寫在前面開始尾聲

有幾個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##