• Android Binder机制详解:手写IPC通信


    想要掌握一样东西,最好的方式就是阅读理解它的源码。想要掌握Android Binder,最好的方式就是写一个AIDL文件,然后查看其生成的代码。本文的思路也是来自于此。

    简介

    Binder是Android常用的一种进程间通信方式。当然,不使用Binder,你还可以使用Socket甚至文件来进行通信。

    通常Android上的进程间通信,指的就是远程Service的调用。

    开始

    新建测试工程

    打开Android Studio新建IPCClient和IPCServer两个app工程。

    假设我们要做这样一件事情:

    1. Client向Server发起一个请求:请告诉我1+2等于多少

    2. Server将答案返回给Client

    创建远程Service

    IPCServer新建ManualCalculatorService作为远程Service。

    远程Server需要重写onBind。

    public class ManualCalculatorService extends Service
    {
        @Nullable
        @Override
        public IBinder onBind(Intent intent)
        {
            return new Binder()
            {
                @Override
                protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException
                {
                    return super.onTransact(code, data, reply, flags);
                }
            };
        }
    }
    

    然后在AndroidManifest中注册这个Service。

    <service android:name=".ManualCalculatorService"
             android:exported="true"
             android:process=":manualremote"/>
    

    android:exported="true"表示这个Service对外是暴露的。

    android:process=":manualremote"表示这个Service的运行进程的名称

    一个Service要作为远程Service被其他Client调用,上面两个缺一不可。

    创建Client

    Client调用bindService即可和远程Service建立联系。

    Intent intent = new Intent();
    intent.setComponent(new ComponentName("cn.zmy.ipcserver", "cn.zmy.ipcserver.ManualCalculatorService"));
    bindService(intent, new ServiceConnection()
    {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {
    	
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name)
        {
    
        }
    }, Context.BIND_AUTO_CREATE);
    

    至此,两个项目大体代码结构已经完成。

    Client调用Server

    Client可以通过onServiceConnected中的IBinder类型的service参数来调用远程Service。

    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    
    data.writeInt(1);
    data.writeInt(2);
    try
    {
        service.transact(1000, data, reply, 0);
    }
    catch (RemoteException e)
    {
        e.printStackTrace();
    }
    
    int result = reply.readInt();
    data.recycle();
    reply.recycle();
    Toast.makeText(MainActivity.this, "" + result, Toast.LENGTH_SHORT).show();
    

    代码很简单,最关键的是这一句:

    service.transact(1000, data, reply, 0);

    第一个参数,1000。这是我随便写的个数字,你可以写2000,3000都没得问题。(实际项目中通常使用常量定义,这里主要为了方便演示)

    第二个参数,data。表示我想要传递给Server的数据。

    第三个参数,reply。Server会把结果写入这个参数。

    第四个参数,0。这个参数只有两个可选值:0和IBinder.FLAG_ONEWAY

    0表示这是一个双向的IPC调用,也就是Client向Server发起请求后,Server也会答复Client。
    IBinder.FLAG_ONEWA表示这是一个单向IPC调用,也就是Client向Server发起请求后,会直接返回,不接受Server的答复。

    Server处理Client请求

    Client通过transact请求Server之后,Server可以在onTransact接收到Client的请求。

    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return new Binder()
        {
            @Override
            protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException
            {
                switch (code)
                {
                    case 1000:
                    {
                        int num1 = data.readInt();
                        int num2 = data.readInt();
                        reply.writeInt(num1 + num2);
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }
        };
    }
    

    data中读出数据,然后将结果写入reply中。整个过程就这样。

    运行

    先后安装Server和Client程序,Client中就可以看到结果。

    Demo

    项目代码:

    https://github.com/a3349384/IPCDemo

    原理分析

    所谓原理分析就是追本溯源,接下来我们看一下Client的请求是如何一步步到达Server的

    IBinder

    回到Client调用Server的代码:

    bindService(intent, new ServiceConnection(){
          @Override
          public void onServiceConnected(ComponentName name, IBinder service)
          {
    			...
          }
    
          @Override
          public void onServiceDisconnected(ComponentName name)
          {
    
          }
    }, Context.BIND_AUTO_CREATE);
    

    关键在于这个IBinder,Client是通过IBinder.transact()将请求发给Server的。

    这里的IBinder实际上是个BinderProxy对象。(我怎么知道的?打断点,打日志啊。。。)

    BinderProxy处于{framework}/core/java/android/os/Binder.java中。

    final class BinderProxy implements IBinder {
        private long mObject;
    	
        public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    		...
            return transactNative(code, data, reply, flags);
        }
    	
    	public native boolean transactNative(int code, Parcel data, Parcel reply,
                int flags) throws RemoteException;
    	...
    }
    

    Client调用BindProxy类的transact方法,实际逻辑还是交给transactNative方法处理的。

    接下来找到transactNative的代码。

    代码在{framework}/core/jni/android_util_Binder.cpp中

    static const JNINativeMethod gBinderProxyMethods[] = {
    	...
        {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact}
    	...
    };
    

    可以看的transactNative是动态注册的。找到android_os_BinderProxy_transact方法,看看它的代码。

    JNI方法注册分为静态注册和动态注册,感兴趣的朋友可以自行搜索了解。

    static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
            jint code, jobject dataObj, jobject replyObj, jint flags)
    {
        IBinder* target = (IBinder*)
            env->GetLongField(obj, gBinderProxyOffsets.mObject);
     
        status_t err = target->transact(code, *data, reply, flags);
    
        if (err == NO_ERROR) {
            return JNI_TRUE;
        } else if (err == UNKNOWN_TRANSACTION) {
            return JNI_FALSE;
        }
    }
    

    可以看到,里面又调用了target的transact方法,将请求发送出去。

    target是通过反射获取BinderProxy类的mObject对象得到的。

    final class BinderProxy implements IBinder {    
        private long mObject;
    }
    

    long是怎么被强转为IBinder的?

    实际上这里的long mObject保存的是IBinder的指针。指针的大小和long的大小都是一样的,都是4个字节。

    而名为target的这个IBinder实际上就是Server中onBind返回的这个Binder:

    public class ManualCalculatorService extends Service
    {
        @Nullable
        @Override
        public IBinder onBind(Intent intent)
        {
            return new Binder()
            {
    			...
            };
        }
    }
    

    到这里,我们就差不多明白了。BinderProxy之所以叫BinderProxy,它代理的就是Server中onBind返回的Binder。

    而Client经过一层层的调用,最终调用了Server中返回的Binder对象的transact方法。
    我们看一下这个方法:

    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
    	...
        boolean r = onTransact(code, data, reply, flags);
    	...
        return r;
    }
    

    这个方法实际上调用了onTransact方法进行具体的逻辑处理。这也是为什么我们可以在onTransact中处理Client请求的原因。

    结尾

    关于target是怎么来的?

    target是通过反射获取BinderProxy类的mObject对象得到的。

    mObject保存了server中IBinder的指针。

    那么这个指针又是哪里来的?

    这里不得不提到另外一个类:ServiceManager

    该类在{framework}/core/java/android/os/ServiceManager.java中

    感兴趣的朋友可以阅读它的代码。

    这里简单说一下:ServiceManager通过map保存了Service和IBinder的关系。也就是通过Service的名称就可以获取到这个Service的IBinder。

    参考链接:https://www.zhoumingyao.cn/Android笔记/Android-Binder机制详解:手写IPC通信/

  • 相关阅读:
    Android设备驱动安装
    昨晚摆乌龙了
    生活
    SQLSTATE=57019
    IBM项目六
    不吸烟了
    清明短假
    『ExtJS』使用中需要注意的一些事(持续更新)
    『Spring.Net』IoC 容器
    『Spring.Net』为什么使用?
  • 原文地址:https://www.cnblogs.com/DoNetCoder/p/7280481.html
Copyright © 2020-2023  润新知