【Android】跨进程通信AIDL和messenger详解

1.定义

我们都知道,Android应用一旦启动就会生成一个以包名为名字的进程,当然我们还知道android中很多manager都是运行在system server进程中的,像AMS,PMS,WMS等,它们都是通过binder来进程远程调用,说到这就不得不说多进程之间的通信问题,进程不像线程那样,多线程可以共享内存,而每个进程的内存都是独立的,无法直接访问,因此安卓提供了binder来进行进程间的通信。

在使用binder的过程中,android定义了一些更方便的进程间通信的方式,像AIDL,Messenger等方式,今天我们不讨论binder的原理,先来学习一下AIDL和Messenger的基本使用,废话不多说,咱们切入正题。

2.AIDL在Android中的使用

AIDL的英文名叫做:Android Interface Definition Language,意思就是Android接口定义语言,它是用于定义服务器和客户端通信接口的一种描述语言,可以用它来生成IPC的代码,那么什么时候我们才需要用到AIDL呢,虽然表面上来说是进程间通信就可以用,但是上面我们还提到了Messenger,它们分别适合什么场景呢?

只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。如果不需要执行跨越不同应用的并发IPC,就应该通过实现一个Binder创建接口;或者如果想执行IPC,但根本不需要处理多线程,则使用Messenger类来实现接口。

下面我们来看下AIDL的使用方式:

服务端代码

首先我们定义一个场景,比如我们想获取远程进程的school名字和school列表,那我们就可以在远程进程中也就是我们所说的服务端定义aidl文件,首先我们先定一个School实体类:

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;
    }
}

注意这里一定要实现Parcelable接口,因为aidl传递对象要求必须要序列化对象,android studio工具不会提示readFromParcel方法,我们自己记得加上,然后我们给对象定义一个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类导入,然后我们再定义上面所说的获取学校名字,列表等功能的aidl文件,即SchoolControllerAIDL.aidl:

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

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

同样要注意导入相应的包,接下来我们clean一下项目,就会在如下目录下生成对应的SchoolControllerAIDL类:


自动生成的文件

aidl文件创建好了,当要想和远程进程交互,还需要借助service来bind远程服务,下面我们创建一个AidlService:

public class AidlService extends Service {

    private List<School> getSchoolLists(){
        List<School> schoolList = new ArrayList<>();
        schoolList.add(new School("清华大学","北京"));
        schoolList.add(new School("北京大学","北京"));
        schoolList.add(new School("南京大学","南京"));
        schoolList.add(new School("上海复旦大学","上海"));
        return schoolList;
    }

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

            return "服务器端的schoolname:清华大学";
        }

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

    };

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

代码很简单,就是通过上面自动生成的SchoolControllerAIDL类的Stub内部类来创建一个binder对象,把它作为service中onBind方法的返回值即可,记得在AndroidManifest文件中定义:

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

客户端代码

下面我们就可以编写客户端的代码了,我们都知道bindService需要传递一个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服务:

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

一旦执行上面的bindService代码,ServiceConnection中的onServiceConnected方法就会被调用,此时表明ServiceConnection已经连接成功,客户端可以访问服务端的方法了,现在我们调用一下之前获取学校和学校列表的方法试试:

    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("学校名字:"+listSchools.get(i).getSchoolName()+"   学校地址:"+listSchools.get(i).getAddress()+"\n");
                        }
                        tvShow.setText(sb.toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

运行结果如下:

aidl运行结果

注意:我们上面的例子并没有在两个应用中模拟进程间通信,而是在同一app中,只是跳转的activity配置了process属性,让activity运行在了新进程中,实际上和不同应用的进程间通信是一个意思。

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

另外还要注意如果新启动的activity运行在新进程中,那么Application会在新启动Activity的适合再次调用,我们来验证一下这个结论,修改下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", "当前进程为:"+processNameString+"-----主进程(服务端进程)");
        }else{
            Log.e("wangkeke", "当前进程为:"+processNameString+"-----clent进程(客户端进程)");
        }

    }
}

然后我们运行并且跳转新进程activity,结果如下:


application打印日志结果

正如我们预料的那样,application确实执行了两次,不过解决方式也正如上面打印的日志那样,根据进程名判断当前执行的是哪一个Application,进行相应的处理即可。

插播一条:服务端和客户端进行远程通信,传递的数据必须是aidl支持的,aidl支持如下数据类型:基本数据类型,String,CharSequence,List,Map(集合中的所有元素必须是aidl支持的类型),实现了Parcelable接口的对象。

虽然我们上面实现了客户端和服务端的通信,但是大家可能也发现了,这tm也太简单了吧,调用个方法就完事了,我客户端想要回调怎么办呢,各位别着急,android自然也是提供了客户端回调的处理类,RemoteCallbackList类,在AIDL中客户端向服务端注册一个回调方法时,服务端要考虑客户端是否意外退出(客户端因为错误应用Crash,或者被Kill掉了),服务端还不知道,此时去回调客户端,出现错误。

那么该如何使用呢,其实也很简单,我们在上面的例子中稍微改动下,首先我们在服务端定义一个OnConnectSuccessLisener.aidl,它用来告知服务端的连接状态,并提供一个像客户端发送数据的方法:

// 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类,增加注册回调和反注册回调的方法:

// 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);

}

服务端的AidlService也要稍作改动:

public class AidlService extends Service {

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

    private List<School> getSchoolLists(){
        List<School> schoolList = new ArrayList<>();
        schoolList.add(new School("清华大学","北京"));
        schoolList.add(new School("北京大学","北京"));
        schoolList.add(new School("南京大学","南京"));
        schoolList.add(new School("上海复旦大学","上海"));
        return schoolList;
    }

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

            return "服务器端的schoolname:清华大学";
        }

        @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 {
                    //模拟耗时任务
                    Thread.sleep(3000);
                    final int N = mListener.beginBroadcast();
                    for (int i = 0; i < N; i++) {
                        OnConnectSuccessLisener successLisener = mListener.getBroadcastItem(i);
                        successLisener.sendMsgToClient("我是服务端处理完耗时任务后,给客户端发送的值");
                    }
                    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("学校名字:"+listSchools.get(i).getSchoolName()+"   学校地址:"+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方法客户端注册了回调方法,此时服务端接收到后会处理一个耗时任务,然后通过RemoteCallbackList的register方法,把listener存储起来,此时我们运行,结果如下:


客户端注册回调

可以看到我们在启动客户端之后,3秒后会自动回调给客户端数据,其实到这里我们好像就可以通过存储registerListener(OnConnectSuccessLisener listener)的参数OnConnectSuccessLisener,可以在服务端任何地方给客户端发消息,前提是客户端还在运行,比如把定义一个static类型的变量把listener存起来,不过这种方式很不优雅,不知道大家有好的方式没,欢迎留言讨论(当然也可以通过客户端不断的轮训来获取服务端的某个状态后再返回需要的数据)。

注意:RemoteCallbackList是用于专门添加和删除跨进程listener的类,它内部维护了一个mCallbacks,以客户端和服务端建立的IBinder为key,所以添加和移除callback变得简单明了:

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

我们通过它的register和unregister方法来实时添加和删除callback,保证了回调的正确性!

另外,无论是客户端访问服务端的方法还是服务端访问客户端,发起方在已知知对方是耗时操作的情况下,一定不要在主线程发起调用,因为发起方发起远程请求之后,当前线程会进入挂起状态,只有对方响应之后才会重新恢复执行,所以为了避免在主线程进行耗时操作,需开启子线程发起远程操作。

3.aidl生成代码分析

我们来看下咱们上面的例子中根据aidl文件自动生成的SchoolControllerAIDL类,来了解下它的实现原理:

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本身是个接口,继承了IInterface接口,来看下IInterface的定义:

public interface IInterface
{
    public IBinder asBinder();
}

我们来分别介绍一下SchoolControllerAIDL接口中几个关键的方法和内部类,首先看到定义了一个DESCRIPTOR常量,它是Binder的唯一标识,asBinder()方法返回了当前的binder对象,asInterface方法的作用是用于将服务端的binder对象转化成客户端所需的AIDL接口类型的对象,并且可以看到,如果客户端和服务端在一个进程,那么直接返回服务端的Stub对象本身,否则返回封装后的Stub.Proxy代理对象;onTransact方法的作用是根据客户端的code来判断客户端请求的目标方法是什么,然后根据Parcel中的序列化数据,data存储了装载数据,接着从data中取出目标方法所需的参数,然后执行目标方法,之后把函数返回值写入到reply中。

注意,如果onTransact方法返回false,客户端会请求失败,我们在时机应用中肯定不希望随便一个进程都能够连接我们的远程服务,因此可以用onTransact的返回值的特性来做权限验证。

接下来看一下内部类Proxy,它实现了SchoolControllerAIDL接口,它运行在客户端,它也有一个asBinder方法,用来返回远程binder对象,用来进行远程调用,客户端调用服务端的方法的时候,以getSchoolName为例,首先创建输入型Parcel对象_data,输出型Parcel对象_reply,以及返回值_result,然后把参数信息写入到_data中,就是通过它的transact方法来执行远程操作的,transact内部调用了onTransact方法,注意,当调用transact的时候,当前线程会挂起,然后调用服务端的onTransact方法,直到onTransact执行结束返回后,当前线程继续执行,从_reply中取出执行结果,赋值给result并返回。

图解流程:
客户端-服务器流程

注意:当客户端发起远程请求时,由于当前线程会被挂起直到服务器进程返回数据,如果远程方法很耗时,那么注意不能在Ui线程发起远程请求;另外服务端的binder方法运行在binder线程池中,耗时操作也无需开启新线程,因为其本身就是一个子线程了。

4.linkToDeath和unlinkToDeath

这俩方法是binder类比较重要的方法,因为binder运行在服务端进程中,如果服务端进程异常终止,那么这时候binder连接就会断开,导致我们的远程请求失败,而且对于客户端来说,并不知道binder连接已经断开,对于这个问题,binder提供了linkToDeath和unlinkToDeath方法来解决。

通过linkToDeath可以给binder设置一个死亡代理,当binder死亡时,就会收到通知,可以重新发起连接请求从而恢复,具体用法如下:

    //定义死亡代理对象
    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设置死亡代理:

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

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

此时当服务端死亡的时候,客户端就会收到通知,然后就可以自己处理相应的逻辑了。

另外关于AIDL中定向tag的in,out,inout的理解和作用,大家可以看下这篇文章,你真的理解AIDL中的in,out,inout么?

6.Messenger的使用

Messenger同样是android提供的一种IPC通信方式,它是通过在进程间传递Message对象,通过Message的setData方法,传递Bundle对象,Bundle传递的数据必须实现Parcelable接口,Messener是串行工作的,不存在并发问题。

其实Messenger的底层实现就是AIDL,通过它的构造方法就会发现其中的端倪:

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

它其实是对AIDL的封装,让我们不再需要编写aidl文件,就可以实现进程间通信。下面来具体看下它的用法,首先看下服务端代码:

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:
                    //拿到客户端发来的消息
                    Log.e("wangkeke", "服务器接收到的消息:"+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", "这里是服务器,收到客户端请求");
                    msgFromService.setData(bundle);
                    try {
                        //调用mClientMessenger.send将消息发送给客户端
                        mClientMessenger.send(msgFromService);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        };
    };
    // mServiceMessenger关联mServiceHandler
    private Messenger mServiceMessenger = new Messenger(mServiceHandler);

    @Override
    public IBinder onBind(Intent intent) {
        //将IBinder传给客户端,客户端通过new Messenger(IBinder)拿到mServiceMessenger;
        return mServiceMessenger.getBinder();
    }

}

首先创建一个Messenger对象mServiceMessenger,它需要一个handler作为接收客户端消息的载体,在Service的onBind中返回服务端Messenger的getBinder()即可。

再来看下客户端的代码:

    //客户端用来接收服务端消息的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 当前进程id:"+android.os.Process.myPid()+ "     当前线程id:"+android.os.Process.myTid());

                //拿到服务器传给客户端的IBinder,创建服务端的Messenger
                mServiceMessenger = new Messenger(iBinder);
                //实例化一个Message
                Message msgFromClient = Message.obtain(null, MSG_FROM_CLIENT);
                Bundle bundle = new Bundle();
                bundle.putString("client_msg", "已经和服务器建立连接了");
                msgFromClient.setData(bundle);
                //将mClientMessenger带到服务器去
                msgFromClient.replyTo = mClientMessenger;
                try {
                    //调用mServiceMessenger.send将消息发送的服务器
                    mServiceMessenger.send(msgFromClient);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };
        //绑定Service
        Intent intent = new Intent(this,MessengerService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);

        //客户端给服务端主动发消息
        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", "我是另一进程的客户端发来的消息");
                msgFromClient.setData(bundle);
                //把客户端的Messenger传递给服务端,使用双向通信
                msgFromClient.replyTo = mClientMessenger;
                try {
                    //通过服务端的Messenger发送消息
                    mServiceMessenger.send(msgFromClient);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

运行结果如下:


Messenger服务端和客户端发消息

可以看到使用Messenger发送消息确实很方便,而且无需我们再编写aidl文件,通过message消息直接发送即可,但是它也有明显的缺点,要想服务端响应的话,需要在客户端也创建一个Messenger,通过reply传递到服务端。

注意点:
1.Messenger一次只能处理一个请求(串行)/AIDL一次可以处理多个请求(并行);
2.Messenger不支持RPC,只能通过message传递消息/AIDL支持RPC;
3.Messenger使用简单,轻量级,不需要创建AIDL文件/AIDL使用复杂,需要创建AIDL文件;

AIDL和Messenger比较.png

参考链接:
1.Android使用Messenger实现进程间通信
2.Android开发艺术探索
3.你真的理解AIDL中的in,out,inout么?
4.Messenger与AIDL的区别、优缺点

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容