【Android】跨進(jìn)程通信AIDL和messenger詳解

1.定義

我們都知道,Android應(yīng)用一旦啟動(dòng)就會生成一個(gè)以包名為名字的進(jìn)程,當(dāng)然我們還知道android中很多manager都是運(yùn)行在system server進(jìn)程中的,像AMS,PMS,WMS等,它們都是通過binder來進(jìn)程遠(yuǎn)程調(diào)用,說到這就不得不說多進(jìn)程之間的通信問題,進(jìn)程不像線程那樣,多線程可以共享內(nèi)存,而每個(gè)進(jìn)程的內(nèi)存都是獨(dú)立的,無法直接訪問,因此安卓提供了binder來進(jìn)行進(jìn)程間的通信。

在使用binder的過程中,android定義了一些更方便的進(jìn)程間通信的方式,像AIDL,Messenger等方式,今天我們不討論binder的原理,先來學(xué)習(xí)一下AIDL和Messenger的基本使用,廢話不多說,咱們切入正題。

2.AIDL在Android中的使用

AIDL的英文名叫做:Android Interface Definition Language,意思就是Android接口定義語言,它是用于定義服務(wù)器和客戶端通信接口的一種描述語言,可以用它來生成IPC的代碼,那么什么時(shí)候我們才需要用到AIDL呢,雖然表面上來說是進(jìn)程間通信就可以用,但是上面我們還提到了Messenger,它們分別適合什么場景呢?

只有允許不同應(yīng)用的客戶端用 IPC 方式訪問服務(wù),并且想要在服務(wù)中處理多線程時(shí),才有必要使用 AIDL。如果不需要執(zhí)行跨越不同應(yīng)用的并發(fā)IPC,就應(yīng)該通過實(shí)現(xiàn)一個(gè)Binder創(chuàng)建接口;或者如果想執(zhí)行IPC,但根本不需要處理多線程,則使用Messenger類來實(shí)現(xiàn)接口。

下面我們來看下AIDL的使用方式:

服務(wù)端代碼

首先我們定義一個(gè)場景,比如我們想獲取遠(yuǎn)程進(jìn)程的school名字和school列表,那我們就可以在遠(yuǎn)程進(jìn)程中也就是我們所說的服務(wù)端定義aidl文件,首先我們先定一個(gè)School實(shí)體類:

public class School implements Parcelable {

    private String schoolName;

    private String address;

    public School(String schoolName, String address) {
        this.schoolName = schoolName;
        this.address = address;
    }

    protected School(Parcel in) {
        schoolName = in.readString();
        address = in.readString();
    }

    public static final Creator<School> CREATOR = new Creator<School>() {
        @Override
        public School createFromParcel(Parcel in) {
            return new School(in);
        }

        @Override
        public School[] newArray(int size) {
            return new School[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(schoolName);
        dest.writeString(address);
    }

    public void readFromParcel(Parcel dest) {
        schoolName=dest.readString();
        address=dest.readString();
    }

    public String getSchoolName() {
        return schoolName;
    }

    public void setSchoolName(String schoolName) {
        this.schoolName = schoolName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

注意這里一定要實(shí)現(xiàn)Parcelable接口,因?yàn)閍idl傳遞對象要求必須要序列化對象,android studio工具不會提示readFromParcel方法,我們自己記得加上,然后我們給對象定義一個(gè)aidl文件,即School.aidl:

// ISchool.aidl
package com.wangkeke.aidldemo;
import com.wangkeke.aidldemo.School;
// Declare any non-default types here with import statements

parcelable School;

注意這里的parcelable首字母要小寫,import需要把School類導(dǎo)入,然后我們再定義上面所說的獲取學(xué)校名字,列表等功能的aidl文件,即SchoolControllerAIDL.aidl:

package com.wangkeke.aidldemo;
import com.wangkeke.aidldemo.School;

interface SchoolControllerAIDL {
    String getSchoolName(String schoolNo);
    List<School> getSchools();
}

同樣要注意導(dǎo)入相應(yīng)的包,接下來我們clean一下項(xiàng)目,就會在如下目錄下生成對應(yīng)的SchoolControllerAIDL類:


自動(dòng)生成的文件

aidl文件創(chuàng)建好了,當(dāng)要想和遠(yuǎn)程進(jìn)程交互,還需要借助service來bind遠(yuǎn)程服務(wù),下面我們創(chuàng)建一個(gè)AidlService:

public class AidlService extends Service {

    private List<School> getSchoolLists(){
        List<School> schoolList = new ArrayList<>();
        schoolList.add(new School("清華大學(xué)","北京"));
        schoolList.add(new School("北京大學(xué)","北京"));
        schoolList.add(new School("南京大學(xué)","南京"));
        schoolList.add(new School("上海復(fù)旦大學(xué)","上海"));
        return schoolList;
    }

    SchoolControllerAIDL.Stub mBinder = new SchoolControllerAIDL.Stub() {
        @Override
        public String getSchoolName(String schoolNo) throws RemoteException {

            return "服務(wù)器端的schoolname:清華大學(xué)";
        }

        @Override
        public List<School> getSchools() throws RemoteException {
            return getSchoolLists();
        }

    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

代碼很簡單,就是通過上面自動(dòng)生成的SchoolControllerAIDL類的Stub內(nèi)部類來創(chuàng)建一個(gè)binder對象,把它作為service中onBind方法的返回值即可,記得在AndroidManifest文件中定義:

<service android:name=".AidlService" />

客戶端代碼

下面我們就可以編寫客戶端的代碼了,我們都知道bindService需要傳遞一個(gè)ServiceConnection對象,我們來定義一下:

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isConnection = true;
            schoolAIDL = SchoolControllerAIDL.Stub.asInterface(service);

            try {
                schoolAIDL.registerListener(lisener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isConnection = false;
        }
    };

然后在onCreate中bind服務(wù):

Intent intent = new Intent(this,AidlService.class);
bindService(intent, mConnection ,Context.BIND_AUTO_CREATE);

一旦執(zhí)行上面的bindService代碼,ServiceConnection中的onServiceConnected方法就會被調(diào)用,此時(shí)表明ServiceConnection已經(jīng)連接成功,客戶端可以訪問服務(wù)端的方法了,現(xiàn)在我們調(diào)用一下之前獲取學(xué)校和學(xué)校列表的方法試試:

    btnRemote.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(null != schoolAIDL){
                    try {
                        String schoolName = schoolAIDL.getSchoolName("STU1002");
                        Toast.makeText(ThirdProcessActivity.this, ""+schoolName, Toast.LENGTH_SHORT).show();

                        List<School> listSchools = schoolAIDL.getSchools();

                        StringBuilder sb = new StringBuilder();

                        for (int i = 0; i < listSchools.size(); i++) {
                            sb.append("學(xué)校名字:"+listSchools.get(i).getSchoolName()+"   學(xué)校地址:"+listSchools.get(i).getAddress()+"\n");
                        }
                        tvShow.setText(sb.toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

運(yùn)行結(jié)果如下:

aidl運(yùn)行結(jié)果

注意:我們上面的例子并沒有在兩個(gè)應(yīng)用中模擬進(jìn)程間通信,而是在同一app中,只是跳轉(zhuǎn)的activity配置了process屬性,讓activity運(yùn)行在了新進(jìn)程中,實(shí)際上和不同應(yīng)用的進(jìn)程間通信是一個(gè)意思。

    <activity android:name=".ThirdProcessActivity"
            android:process=":other"
            />

另外還要注意如果新啟動(dòng)的activity運(yùn)行在新進(jìn)程中,那么Application會在新啟動(dòng)Activity的適合再次調(diào)用,我們來驗(yàn)證一下這個(gè)結(jié)論,修改下application中的代碼:

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();


        int pid = android.os.Process.myPid();
        Log.e("wangkeke", "MyApplication is oncreate====="+"pid="+pid);
        String processNameString = "";
        ActivityManager mActivityManager = (ActivityManager)this.getSystemService(getApplicationContext().ACTIVITY_SERVICE);
        for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {
            if (appProcess.pid == pid) {
                processNameString = appProcess.processName;
            }
        }
        if("com.wangkeke.aidldemo".equals(processNameString)){
            Log.e("wangkeke", "當(dāng)前進(jìn)程為:"+processNameString+"-----主進(jìn)程(服務(wù)端進(jìn)程)");
        }else{
            Log.e("wangkeke", "當(dāng)前進(jìn)程為:"+processNameString+"-----clent進(jìn)程(客戶端進(jìn)程)");
        }

    }
}

然后我們運(yùn)行并且跳轉(zhuǎn)新進(jìn)程activity,結(jié)果如下:


application打印日志結(jié)果

正如我們預(yù)料的那樣,application確實(shí)執(zhí)行了兩次,不過解決方式也正如上面打印的日志那樣,根據(jù)進(jìn)程名判斷當(dāng)前執(zhí)行的是哪一個(gè)Application,進(jìn)行相應(yīng)的處理即可。

插播一條:服務(wù)端和客戶端進(jìn)行遠(yuǎn)程通信,傳遞的數(shù)據(jù)必須是aidl支持的,aidl支持如下數(shù)據(jù)類型:基本數(shù)據(jù)類型,String,CharSequence,List,Map(集合中的所有元素必須是aidl支持的類型),實(shí)現(xiàn)了Parcelable接口的對象。

雖然我們上面實(shí)現(xiàn)了客戶端和服務(wù)端的通信,但是大家可能也發(fā)現(xiàn)了,這tm也太簡單了吧,調(diào)用個(gè)方法就完事了,我客戶端想要回調(diào)怎么辦呢,各位別著急,android自然也是提供了客戶端回調(diào)的處理類,RemoteCallbackList類,在AIDL中客戶端向服務(wù)端注冊一個(gè)回調(diào)方法時(shí),服務(wù)端要考慮客戶端是否意外退出(客戶端因?yàn)殄e(cuò)誤應(yīng)用Crash,或者被Kill掉了),服務(wù)端還不知道,此時(shí)去回調(diào)客戶端,出現(xiàn)錯(cuò)誤。

那么該如何使用呢,其實(shí)也很簡單,我們在上面的例子中稍微改動(dòng)下,首先我們在服務(wù)端定義一個(gè)OnConnectSuccessLisener.aidl,它用來告知服務(wù)端的連接狀態(tài),并提供一個(gè)像客戶端發(fā)送數(shù)據(jù)的方法:

// OnConnectSuccessLisener.aidl
package com.wangkeke.aidldemo;

// Declare any non-default types here with import statements

interface OnConnectSuccessLisener {
    void onServiceConnected();

    void onServiceDisConnected();

    void sendMsgToClient(String msg);
}

然后我們修改下SchoolControllerAIDL.aidl類,增加注冊回調(diào)和反注冊回調(diào)的方法:

// SchoolControllerAIDL.aidl
package com.wangkeke.aidldemo;
import com.wangkeke.aidldemo.School;
import com.wangkeke.aidldemo.OnConnectSuccessLisener;

// Declare any non-default types here with import statements

interface SchoolControllerAIDL {

    String getSchoolName(String schoolNo);

    List<School> getSchools();

    void registerListener(OnConnectSuccessLisener listener);

    void unregisterListener(OnConnectSuccessLisener listener);

}

服務(wù)端的AidlService也要稍作改動(dòng):

public class AidlService extends Service {

    private RemoteCallbackList<OnConnectSuccessLisener> mListener = new RemoteCallbackList<>();

    private List<School> getSchoolLists(){
        List<School> schoolList = new ArrayList<>();
        schoolList.add(new School("清華大學(xué)","北京"));
        schoolList.add(new School("北京大學(xué)","北京"));
        schoolList.add(new School("南京大學(xué)","南京"));
        schoolList.add(new School("上海復(fù)旦大學(xué)","上海"));
        return schoolList;
    }

    SchoolControllerAIDL.Stub mBinder = new SchoolControllerAIDL.Stub() {
        @Override
        public String getSchoolName(String schoolNo) throws RemoteException {

            return "服務(wù)器端的schoolname:清華大學(xué)";
        }

        @Override
        public List<School> getSchools() throws RemoteException {
            return getSchoolLists();
        }

        @Override
        public void registerListener(OnConnectSuccessLisener listener) throws RemoteException {
            listener.onServiceConnected();
            mListener.register(listener);
            checkCameraState();
        }

        @Override
        public void unregisterListener(OnConnectSuccessLisener listener) throws RemoteException {
            listener.onServiceDisConnected();
            mListener.unregister(listener);
        }
    };

    private void checkCameraState() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //模擬耗時(shí)任務(wù)
                    Thread.sleep(3000);
                    final int N = mListener.beginBroadcast();
                    for (int i = 0; i < N; i++) {
                        OnConnectSuccessLisener successLisener = mListener.getBroadcastItem(i);
                        successLisener.sendMsgToClient("我是服務(wù)端處理完耗時(shí)任務(wù)后,給客戶端發(fā)送的值");
                    }
                    mListener.finishBroadcast();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

客戶端代碼如下:

/**
 * 我是客戶端
 */
public class ThirdProcessActivity extends AppCompatActivity {

    private boolean isConnection = false;

    private Button btnRemote;

    private TextView tvShow;

    OnConnectSuccessLisener lisener = new OnConnectSuccessLisener.Stub() {
        @Override
        public void onServiceConnected() throws RemoteException {
            Log.e("wangkeke","連接成功");
        }

        @Override
        public void onServiceDisConnected() throws RemoteException {
            Log.e("wangkeke","斷開連接");
        }

        @Override
        public void sendMsgToClient(String msg) throws RemoteException {
            Log.e("wangkeke","客戶端收到消息:"+msg);
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isConnection = true;
            schoolAIDL = SchoolControllerAIDL.Stub.asInterface(service);

            try {
                schoolAIDL.registerListener(lisener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isConnection = false;
        }
    };
    private SchoolControllerAIDL schoolAIDL;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third_process);

        Intent intent = new Intent(this,AidlService.class);
        bindService(intent, mConnection ,Context.BIND_AUTO_CREATE);

        btnRemote = findViewById(R.id.btn_remote);
        tvShow = findViewById(R.id.tv_show);
        btnRemote.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(null != schoolAIDL){
                    try {
                        String schoolName = schoolAIDL.getSchoolName("STU1002");
                        Toast.makeText(ThirdProcessActivity.this, ""+schoolName, Toast.LENGTH_SHORT).show();

                        List<School> listSchools = schoolAIDL.getSchools();

                        StringBuilder sb = new StringBuilder();

                        for (int i = 0; i < listSchools.size(); i++) {
                            sb.append("學(xué)校名字:"+listSchools.get(i).getSchoolName()+"   學(xué)校地址:"+listSchools.get(i).getAddress()+"\n");
                        }
                        tvShow.setText(sb.toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        try {
            if(isConnection && schoolAIDL.asBinder().isBinderAlive()){
                schoolAIDL.unregisterListener(lisener);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        unbindService(mConnection);
    }
}

我們在bindService之后,onServiceConnected建立連接,通過registerListener方法客戶端注冊了回調(diào)方法,此時(shí)服務(wù)端接收到后會處理一個(gè)耗時(shí)任務(wù),然后通過RemoteCallbackList的register方法,把listener存儲起來,此時(shí)我們運(yùn)行,結(jié)果如下:


客戶端注冊回調(diào)

可以看到我們在啟動(dòng)客戶端之后,3秒后會自動(dòng)回調(diào)給客戶端數(shù)據(jù),其實(shí)到這里我們好像就可以通過存儲registerListener(OnConnectSuccessLisener listener)的參數(shù)OnConnectSuccessLisener,可以在服務(wù)端任何地方給客戶端發(fā)消息,前提是客戶端還在運(yùn)行,比如把定義一個(gè)static類型的變量把listener存起來,不過這種方式很不優(yōu)雅,不知道大家有好的方式?jīng)],歡迎留言討論(當(dāng)然也可以通過客戶端不斷的輪訓(xùn)來獲取服務(wù)端的某個(gè)狀態(tài)后再返回需要的數(shù)據(jù))。

注意:RemoteCallbackList是用于專門添加和刪除跨進(jìn)程listener的類,它內(nèi)部維護(hù)了一個(gè)mCallbacks,以客戶端和服務(wù)端建立的IBinder為key,所以添加和移除callback變得簡單明了:

ArrayMap<IBinder, Callback> mCallbacks
            = new ArrayMap<IBinder, Callback>();

我們通過它的register和unregister方法來實(shí)時(shí)添加和刪除callback,保證了回調(diào)的正確性!

另外,無論是客戶端訪問服務(wù)端的方法還是服務(wù)端訪問客戶端,發(fā)起方在已知知對方是耗時(shí)操作的情況下,一定不要在主線程發(fā)起調(diào)用,因?yàn)榘l(fā)起方發(fā)起遠(yuǎn)程請求之后,當(dāng)前線程會進(jìn)入掛起狀態(tài),只有對方響應(yīng)之后才會重新恢復(fù)執(zhí)行,所以為了避免在主線程進(jìn)行耗時(shí)操作,需開啟子線程發(fā)起遠(yuǎn)程操作。

3.aidl生成代碼分析

我們來看下咱們上面的例子中根據(jù)aidl文件自動(dòng)生成的SchoolControllerAIDL類,來了解下它的實(shí)現(xiàn)原理:

public interface SchoolControllerAIDL extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.wangkeke.aidldemo.SchoolControllerAIDL {
        private static final java.lang.String DESCRIPTOR = "com.wangkeke.aidldemo.SchoolControllerAIDL";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.wangkeke.aidldemo.SchoolControllerAIDL interface,
         * generating a proxy if needed.
         */
        public static com.wangkeke.aidldemo.SchoolControllerAIDL asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.wangkeke.aidldemo.SchoolControllerAIDL))) {
                return ((com.wangkeke.aidldemo.SchoolControllerAIDL) iin);
            }
            return new com.wangkeke.aidldemo.SchoolControllerAIDL.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getSchoolName: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _result = this.getSchoolName(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_getSchools: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.wangkeke.aidldemo.School> _result = this.getSchools();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_registerListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.wangkeke.aidldemo.OnConnectSuccessLisener _arg0;
                    _arg0 = com.wangkeke.aidldemo.OnConnectSuccessLisener.Stub.asInterface(data.readStrongBinder());
                    this.registerListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_unregisterListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.wangkeke.aidldemo.OnConnectSuccessLisener _arg0;
                    _arg0 = com.wangkeke.aidldemo.OnConnectSuccessLisener.Stub.asInterface(data.readStrongBinder());
                    this.unregisterListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.wangkeke.aidldemo.SchoolControllerAIDL {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.lang.String getSchoolName(java.lang.String schoolNo) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(schoolNo);
                    mRemote.transact(Stub.TRANSACTION_getSchoolName, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public java.util.List<com.wangkeke.aidldemo.School> getSchools() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.wangkeke.aidldemo.School> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getSchools, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.wangkeke.aidldemo.School.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void registerListener(com.wangkeke.aidldemo.OnConnectSuccessLisener listener) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void unregisterListener(com.wangkeke.aidldemo.OnConnectSuccessLisener listener) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getSchoolName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getSchools = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    }

    public java.lang.String getSchoolName(java.lang.String schoolNo) throws android.os.RemoteException;

    public java.util.List<com.wangkeke.aidldemo.School> getSchools() throws android.os.RemoteException;

    public void registerListener(com.wangkeke.aidldemo.OnConnectSuccessLisener listener) throws android.os.RemoteException;

    public void unregisterListener(com.wangkeke.aidldemo.OnConnectSuccessLisener listener) throws android.os.RemoteException;
}

可以看到SchoolControllerAIDL本身是個(gè)接口,繼承了IInterface接口,來看下IInterface的定義:

public interface IInterface
{
    public IBinder asBinder();
}

我們來分別介紹一下SchoolControllerAIDL接口中幾個(gè)關(guān)鍵的方法和內(nèi)部類,首先看到定義了一個(gè)DESCRIPTOR常量,它是Binder的唯一標(biāo)識,asBinder()方法返回了當(dāng)前的binder對象,asInterface方法的作用是用于將服務(wù)端的binder對象轉(zhuǎn)化成客戶端所需的AIDL接口類型的對象,并且可以看到,如果客戶端和服務(wù)端在一個(gè)進(jìn)程,那么直接返回服務(wù)端的Stub對象本身,否則返回封裝后的Stub.Proxy代理對象;onTransact方法的作用是根據(jù)客戶端的code來判斷客戶端請求的目標(biāo)方法是什么,然后根據(jù)Parcel中的序列化數(shù)據(jù),data存儲了裝載數(shù)據(jù),接著從data中取出目標(biāo)方法所需的參數(shù),然后執(zhí)行目標(biāo)方法,之后把函數(shù)返回值寫入到reply中。

注意,如果onTransact方法返回false,客戶端會請求失敗,我們在時(shí)機(jī)應(yīng)用中肯定不希望隨便一個(gè)進(jìn)程都能夠連接我們的遠(yuǎn)程服務(wù),因此可以用onTransact的返回值的特性來做權(quán)限驗(yàn)證。

接下來看一下內(nèi)部類Proxy,它實(shí)現(xiàn)了SchoolControllerAIDL接口,它運(yùn)行在客戶端,它也有一個(gè)asBinder方法,用來返回遠(yuǎn)程binder對象,用來進(jìn)行遠(yuǎn)程調(diào)用,客戶端調(diào)用服務(wù)端的方法的時(shí)候,以getSchoolName為例,首先創(chuàng)建輸入型Parcel對象_data,輸出型Parcel對象_reply,以及返回值_result,然后把參數(shù)信息寫入到_data中,就是通過它的transact方法來執(zhí)行遠(yuǎn)程操作的,transact內(nèi)部調(diào)用了onTransact方法,注意,當(dāng)調(diào)用transact的時(shí)候,當(dāng)前線程會掛起,然后調(diào)用服務(wù)端的onTransact方法,直到onTransact執(zhí)行結(jié)束返回后,當(dāng)前線程繼續(xù)執(zhí)行,從_reply中取出執(zhí)行結(jié)果,賦值給result并返回。

圖解流程:
客戶端-服務(wù)器流程

注意:當(dāng)客戶端發(fā)起遠(yuǎn)程請求時(shí),由于當(dāng)前線程會被掛起直到服務(wù)器進(jìn)程返回?cái)?shù)據(jù),如果遠(yuǎn)程方法很耗時(shí),那么注意不能在Ui線程發(fā)起遠(yuǎn)程請求;另外服務(wù)端的binder方法運(yùn)行在binder線程池中,耗時(shí)操作也無需開啟新線程,因?yàn)槠浔旧砭褪且粋€(gè)子線程了。

4.linkToDeath和unlinkToDeath

這倆方法是binder類比較重要的方法,因?yàn)閎inder運(yùn)行在服務(wù)端進(jìn)程中,如果服務(wù)端進(jìn)程異常終止,那么這時(shí)候binder連接就會斷開,導(dǎo)致我們的遠(yuǎn)程請求失敗,而且對于客戶端來說,并不知道binder連接已經(jīng)斷開,對于這個(gè)問題,binder提供了linkToDeath和unlinkToDeath方法來解決。

通過linkToDeath可以給binder設(shè)置一個(gè)死亡代理,當(dāng)binder死亡時(shí),就會收到通知,可以重新發(fā)起連接請求從而恢復(fù),具體用法如下:

    //定義死亡代理對象
    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if(null != schoolAIDL){
                schoolAIDL.asBinder().unlinkToDeath(deathRecipient,0);
                schoolAIDL = null;
                //重新綁定service
                Intent intent = new Intent(ThirdProcessActivity.this,AidlService.class);
                bindService(intent, mConnection ,Context.BIND_AUTO_CREATE);
            }
        }
    };

在ServiceConnection的onServiceConnected方法中給binder設(shè)置死亡代理:

private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isConnection = true;
            schoolAIDL = SchoolControllerAIDL.Stub.asInterface(service);
            try {
                //設(shè)置死亡代理
                service.linkToDeath(deathRecipient,0);
                schoolAIDL.registerListener(lisener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isConnection = false;
        }
    };

此時(shí)當(dāng)服務(wù)端死亡的時(shí)候,客戶端就會收到通知,然后就可以自己處理相應(yīng)的邏輯了。

另外關(guān)于AIDL中定向tag的in,out,inout的理解和作用,大家可以看下這篇文章,你真的理解AIDL中的in,out,inout么?

6.Messenger的使用

Messenger同樣是android提供的一種IPC通信方式,它是通過在進(jìn)程間傳遞Message對象,通過Message的setData方法,傳遞Bundle對象,Bundle傳遞的數(shù)據(jù)必須實(shí)現(xiàn)Parcelable接口,Messener是串行工作的,不存在并發(fā)問題。

其實(shí)Messenger的底層實(shí)現(xiàn)就是AIDL,通過它的構(gòu)造方法就會發(fā)現(xiàn)其中的端倪:

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

它其實(shí)是對AIDL的封裝,讓我們不再需要編寫aidl文件,就可以實(shí)現(xiàn)進(jìn)程間通信。下面來具體看下它的用法,首先看下服務(wù)端代碼:

public class MessengerService extends Service {

    public static Messenger clientMessenger;

    public static final int MSG_FROM_CLIENT = 0;
    public static final int MSG_FROM_SERVICE = 1;
    // mServiceHandler
    private static Handler mServiceHandler = new Handler(){
        public void handleMessage(Message msgFromClient) {
            switch (msgFromClient.what) {
                case MSG_FROM_CLIENT:
                    //拿到客戶端發(fā)來的消息
                    Log.e("wangkeke", "服務(wù)器接收到的消息:"+msgFromClient.getData().getString("client_msg"));
                    //拿到客戶端的mClientMessenger
                    Messenger mClientMessenger = msgFromClient.replyTo;
                    clientMessenger = mClientMessenger;
                    Message msgFromService = Message.obtain(null,MSG_FROM_SERVICE);
                    Bundle bundle = new Bundle();
                    bundle.putString("service_msg", "這里是服務(wù)器,收到客戶端請求");
                    msgFromService.setData(bundle);
                    try {
                        //調(diào)用mClientMessenger.send將消息發(fā)送給客戶端
                        mClientMessenger.send(msgFromService);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        };
    };
    // mServiceMessenger關(guān)聯(lián)mServiceHandler
    private Messenger mServiceMessenger = new Messenger(mServiceHandler);

    @Override
    public IBinder onBind(Intent intent) {
        //將IBinder傳給客戶端,客戶端通過new Messenger(IBinder)拿到mServiceMessenger;
        return mServiceMessenger.getBinder();
    }

}

首先創(chuàng)建一個(gè)Messenger對象mServiceMessenger,它需要一個(gè)handler作為接收客戶端消息的載體,在Service的onBind中返回服務(wù)端Messenger的getBinder()即可。

再來看下客戶端的代碼:

    //客戶端用來接收服務(wù)端消息的Handler
    private static Handler mClientHandler = new Handler(){
        public void handleMessage(Message msgFromService) {
            switch (msgFromService.what) {
                case MSG_FROM_SERVICE:
                    Log.e("wangkeke", "客戶端:"+msgFromService.getData().getString("service_msg"));
                    break;
            }
        };
    };
    //客戶端Messenger對象
    private Messenger mClientMessenger = new Messenger(mClientHandler);

   
    conn = new ServiceConnection() {
            @Override
            public void onServiceDisconnected(ComponentName name) {

            }

            @Override
            public void onServiceConnected(ComponentName name, IBinder iBinder) {
                Log.e("wangkeke", "onServiceConnected handler 當(dāng)前進(jìn)程id:"+android.os.Process.myPid()+ "     當(dāng)前線程id:"+android.os.Process.myTid());

                //拿到服務(wù)器傳給客戶端的IBinder,創(chuàng)建服務(wù)端的Messenger
                mServiceMessenger = new Messenger(iBinder);
                //實(shí)例化一個(gè)Message
                Message msgFromClient = Message.obtain(null, MSG_FROM_CLIENT);
                Bundle bundle = new Bundle();
                bundle.putString("client_msg", "已經(jīng)和服務(wù)器建立連接了");
                msgFromClient.setData(bundle);
                //將mClientMessenger帶到服務(wù)器去
                msgFromClient.replyTo = mClientMessenger;
                try {
                    //調(diào)用mServiceMessenger.send將消息發(fā)送的服務(wù)器
                    mServiceMessenger.send(msgFromClient);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };
        //綁定Service
        Intent intent = new Intent(this,MessengerService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);

        //客戶端給服務(wù)端主動(dòng)發(fā)消息
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //新建message對象
                Message msgFromClient = Message.obtain(null,MSG_FROM_CLIENT);
                //傳值
                Bundle bundle = new Bundle();
                bundle.putString("client_msg", "我是另一進(jìn)程的客戶端發(fā)來的消息");
                msgFromClient.setData(bundle);
                //把客戶端的Messenger傳遞給服務(wù)端,使用雙向通信
                msgFromClient.replyTo = mClientMessenger;
                try {
                    //通過服務(wù)端的Messenger發(fā)送消息
                    mServiceMessenger.send(msgFromClient);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

運(yùn)行結(jié)果如下:


Messenger服務(wù)端和客戶端發(fā)消息

可以看到使用Messenger發(fā)送消息確實(shí)很方便,而且無需我們再編寫aidl文件,通過message消息直接發(fā)送即可,但是它也有明顯的缺點(diǎn),要想服務(wù)端響應(yīng)的話,需要在客戶端也創(chuàng)建一個(gè)Messenger,通過reply傳遞到服務(wù)端。

注意點(diǎn):
1.Messenger一次只能處理一個(gè)請求(串行)/AIDL一次可以處理多個(gè)請求(并行);
2.Messenger不支持RPC,只能通過message傳遞消息/AIDL支持RPC;
3.Messenger使用簡單,輕量級,不需要?jiǎng)?chuàng)建AIDL文件/AIDL使用復(fù)雜,需要?jiǎng)?chuàng)建AIDL文件;

AIDL和Messenger比較.png

參考鏈接:
1.Android使用Messenger實(shí)現(xiàn)進(jìn)程間通信
2.Android開發(fā)藝術(shù)探索
3.你真的理解AIDL中的in,out,inout么?
4.Messenger與AIDL的區(qū)別、優(yōu)缺點(diǎn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容