• AIDL跨进程通信


    一、IPC机制

     IPC(Inter-Process Communication),即进程间通信。此技术并非Android独创,其他系统也存在IPC机制。比如,Linux系统有:socket,pipe,signal,trace等。Android虽然继承了Linux内核,但是几乎没有使用这些,取而代之的是大量使用Binder机制,主要原因是其通信效率和安全性更高。

    序列化,当我们通过Intent或Binder传输数据的时候需要对数据进行序列化。可以通过Serialiazable或Parcelable接口完成序列化:

    • Serialiazable Java提供的接口,使用简单,内存开销大,适用于数据持久化或网络传输。
    • Parcelable Android提供的接口,使用复杂,内存开销小,适用于内存间数据传输。

    Android主要的IPC方式:

    • Bundle
    • 文件共享
    • Messenger
    • AIDL
    • ContentProvider

     Bundle本身实现了Parcelable接口,只需要先将数据放入Bundle,再通过Intent就可以方便地在进程间传输。然而Intent机制传输效率低,大多用于应用层的功能整合。

     Binder带来更高效率的进程间通信,主要用于系统层的功能整合。Messenger、AIDL、ContentProvider底层都是基于Binder机制。


    二、Binder原理

    Binder是Android提供的一种效率更高、更安全的基于C/S架构的IPC通信机制,其本质也是调用系统底层的内存共享实现。

    从进程角度来看Binder进程间通信原理:

    binder

    在Android系统中

    • 用户空间彼此不能共享
    • 内核空间可以共享
    • 用户空间进程通过open/ioctl等方式与内核空间通信
    • Client与Server通信的实现,是由Binder驱动在内核空间完成

     在android中,有很多Service都是通过binder来通信的。这里要引入另一个重要的角色:ServiceManager。ServiceManager负责管理Server所提供的服务,同时响应Client的请求并为之分配相应的服务。

     可以把ServiceManager(以下简称SM)比作通讯站,Client和Server是电话用户,Server首先要向通讯站注册号码,当Client拨打号码时,通讯站首先检索是否有该号码,如果有则转接给Client,否则不响应。

     需要注意的是,Client一般认为是数据发送方,Server是数据接收方,两者并非固定不变的。如果Server在收到数据后主动向Client方发送数据,则两者身份互换。

    Binder通信的完整流程如下:

    Binder流程

    • Server向ServiceManager注册服务
    • Client向ServiceManager申请服务
    • SM作为守护进程,处理Client端请求,并管理所有Server端服务
    • BinderDriver位于Kernel层,是一切运作的基础

    (1)Server向SM注册服务

    Server在自己的进程向Binder驱动申请创建某服务Service的Binder实体。

    Binder驱动收到申请后创建该Service的Binder实体和引用,并将该Service的名字和引用发送给SM。

    SM收到数据后,提取该Service的名字和引用,存入查询表。

    这时如果有Client向SM申请该Service服务,SM从查询表查找该Service的引用,并发送给Client。

    注意:SM保存的只是Service的引用,Service的Binder实体位于Binder驱动中。我们将实体称为本地对象,引用称为远程对象。

    (2)Client从SM获取Service的远程对象

      上面说了,Client进程向SM发送消息获取Service引用,进而调用Server进程的Service服务,从而实现了Client与Server进程间通信。

    那么,问题来了,Client和SM本身就是两个不同的进程。当Client向SM申请服务的时候,SM实际上扮演了Server进程的角色。也就是说,我们要利用Client和SM进程间通信来完成Client和Server进程间通信。这岂不是很荒谬么,Client和Server之间可以通过SM来协调,那Client和SM之间又怎么协调呢?问题似乎变成了先有鸡还是先有蛋的悖论。

    或许凡人会纠结先有鸡还是先有蛋,但上帝不会。因为上帝可以创造,他随便创建一个鸡或者蛋,问题不就解决了。Google的程序员就是Android的上帝,果然,他们创造了一只“鸡”:当Binder驱动启动后,SM会通过BINDER_SET_CONTEXT_MGR命令将自己注册为SM,此时,Binder驱动会创建一个SM的Binder实体。而且,这个Binder实体不需要生成引用,他们和Client和Server达成了协议:handle值为0的引用就是指向SM。所以,Client和Server不需要再借助第三者就能自己生成SM的引用。也就是说,只要Binder驱动启动后,Client就可以跨进程向SM申请服务,Server也可以跨进程向SM注册服务。

    详细的进程间通信流程如下:

    1. Binder驱动启动,并生成SM的Binder实例
    2. Server端通过handle为0的引用向SM注册自己的Service名字和引用
    3. Client端通过handle为0的引用向SM申请访问某个Service
    4. SM通过名字查询对应的Service引用,并返回给Client端,如果有更多的Client访问该Service,则每个Client都会获得这个Service的引用
    5. Client端通过Service引用向Server端发送数据,此时Server端也获得了Client引用
    6. Server端通过Client引用向Client端返回数据

    至此,一个完整的C/S通信架构建成了!


    三、AIDL概述

     上面讲的Binder进程间通信原理已经普遍存在于Android系统层,比如SystemServer中的PowerManagerServiceActivityManagerService DisplayManagerService、PackageManagerService等都是通过Binder机制向外提供跨进程服务的。

    以上都是系统级服务,如何在应用层通过Binder机制实现自己的IPC通信呢?

    答案是AIDL,AIDL是Binder机制向外提供的接口,是Java层Binder实现的便利工具。

    AIDL全称为Android接口定义语言,是一种IDL语言,它可以生成一段代码,通过预先定义的接口达到两个进程内部通信(IPC)的目的。比如一个进程中的Activity想访问另一个进程中Service,就可以通过AIDL生成代码来实现两者之间的数据交互。

    从整体上来看看AIDL在Android系统进程间通讯的位置:

    IPC

    Android系统IPC架构中:

    • 最底层依然调用Kernel层的内存共享
    • Binder作为Android提供的最主要IPC机制
    • AIDL是Binder的Java层实现
    • Messager对AIDL进行封装,且是线程安全的
    • Intent、ContentProvider、BroadcastReceiver是对Binder更高层级的封装

    四、AIDL实例

    下面通过一个简单的AIDL实例,来验证Android的Binder机制。

    创建两个app:Client和Server。Client端界面可以输入两个数值a和b,点击“计算”按钮,调用Server端的SumService服务计算a+b,并将结果返回给Client端显示。

    (1)创建Server项目

     其中ISumServiceInterface.aidl是声明的aidl接口,编译后会在build目录下自动生成对应的ISumServiceInterface.java类。

    // ISumServiceInterface.aidl
    package com.example.server;
    
    // Declare any non-default types here with import statements
    interface ISumServiceInterface {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, String aString);
    
        //此处声明对外提供的服务方法
        int sum(int a, int b);
    }

    SumService是本地服务类,sum方法的真正执行者。

    public class SumService extends Service {
        @Nullable
        public static final String TAG = "aidl";
        @Override
        public IBinder onBind(Intent intent) {
            Log.i(TAG, "server端:onBind");
            return iBinder;
        }
    
        //ISumServiceInterface.Stub是AIDL自动生成的静态内部类,iBinder将返回给client端
        private IBinder iBinder = new ISumServiceInterface.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
    
            }
    
            @Override
            public int sum(int a, int b) throws RemoteException {
                Log.e(TAG,"server端:收到Client端请求:a=" + a + ",b=" + b );
                return a + b;
            }
        };
    }

    AndroidManifest中配置SumService

           <service android:name=".SumService"
                android:process=":remote"
                android:exported="true">
                <intent-filter>
                    <action android:name="com.example.server.sum_service"/> <!--client端绑定服务时需使用相同的action-->
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </service>

    (2)创建Client项目

    ISumServiceInterface.aidl和server端是一样的(注意包名也必须一样),编译后同样自动生成ISumServiceInterface.java类。

    MainActivity允许client端用户输入两个数值,然后远程调用server端的sum方法,最后将server端返回的结果显示在client端。

    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "aidl";
        private EditText a = null;
        private EditText b = null;
        private TextView display;
        private Button sum;
        private Button bind;
        private ISumServiceInterface sumServiceInterface;
        private ServiceConnection conn = new ServiceConnection() {
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.i(TAG, "client端:服务已绑定!");
                //这里调用Stub.asInterface实际上获得了server端ISumServiceInterface.Stub的远程对象
                sumServiceInterface = ISumServiceInterface.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.i(TAG, "client端:服务已断开!");
                sumServiceInterface = null;
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            a = findViewById(R.id.a);
            b = findViewById(R.id.b);
            sum = findViewById(R.id.sum);
            bind = findViewById(R.id.bind);
            display = findViewById(R.id.display);
            sum.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        int n1 = Integer.parseInt(a.getText().toString().trim());
                        int n2 = Integer.parseInt(b.getText().toString().trim());
    
                        //sumServiceInterface是server端ISumServiceInterface.Stub的远程对象
                        //通过它就可以调用server端的sum方法
                        int result = sumServiceInterface.sum(n1, n2);
                        Log.i(TAG, "client端:收到server端返回结果:" + result);
                        display.setText("结果:" + result);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            bind.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    bindService();
                }
            });
        }
    
        private void bindService() {
            Log.i(TAG, "client端:开始绑定SumService...");
            Intent intent = new Intent();
            //这里的Action和Server端配置的必须保持一致
            intent.setAction("com.example.server.sum_service");
            //要求显示调用,指定包名
            intent.setComponent(new ComponentName("com.example.server", "com.example.server.SumService"));
            //绑定并自动创建服务
            bindService(intent, conn, Context.BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(conn);
        }
    }

    (3)安装并运行

    安装client和server,运行client输入55和45,点击BIND,点击SUM,结果显示:

    client

    Log打印如下:

    I aidl    : client端:开始绑定SumService...
    I aidl    : server端:onBind
    I aidl    : client端:服务已绑定!
    E aidl    : server端:收到Client端请求:a=55,b=45
    I aidl    : client端:开始绑定SumService...
    I aidl    : server端:onBind
    I aidl    : client端:服务已绑定!
    E aidl    : server端:收到Client端请求:a=55,b=45
    I aidl    : client端:收到server端返回结果:100

    可以看出,成功从client端调用server端sum方法并返回结果。


    五、AIDL底层逻辑

    从上面的实例已经知道如何使用AIDL,下面分析一下AIDL真正的底层逻辑,看它是如何使用Binder机制实现IPC通信的。

    当我们声明ISumServiceInterface.aidl并编译后,自动生成了ISumServiceInterface.java类。这个类才是真正实现Java层与Binder驱动交互的。

    下面详细分析ISumServiceInterface.java代码(删减部分冗余代码):

    public interface ISumServiceInterface extends android.os.IInterface {
      /**
       * Local-side IPC implementation stub class.
       */
      public static abstract class Stub extends android.os.Binder implements com.example.server.ISumServiceInterface {
    
        //DESCRIPTOR是Binder服务的唯一标志,一般用完全类名表示
        private static final java.lang.String DESCRIPTOR = "com.example.server.ISumServiceInterface";
    
        /**
         * Construct the stub at attach it to the interface.
         * Stub构造函数,在SumService的onBind()时实例化并作为IBinder返回给client
         */
        public Stub() {
          //表示将Stub实例绑定为ISumServiceInterface的本地实现,此后,queryLocalInterface(DESCRIPTOR)将返回此本地实现,而不是null
          this.attachInterface(this, DESCRIPTOR);
        }
    
        /**
         * Cast an IBinder object into an com.example.server.ISumServiceInterface interface,
         * generating a proxy if needed.
         * 这个方法在client端调用,用于将SumService返回的IBinder转化成ISumServiceInterface实例,分两种情况:
         * 1.client有ISumServiceInterface的本地对象,那就直接返回,也就不需要IPC了
         * 2.本例中Stub构造函数是在Server端SumService的onBind()时调用的,client端没有本地对象,
         * 所以需要生成并返回一个Proxy,即远程对象,然后,远程对象和本地对象通过transact方法通信(由Binder驱动实现)
         */
        public static com.example.server.ISumServiceInterface asInterface(android.os.IBinder obj) {
          if ((obj == null)) {
            return null;
          }
          android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
          //判断是否有本地实现,有,则返回本地对象,不需要IPC
          if (((iin != null) && (iin instanceof com.example.server.ISumServiceInterface))) {
            return ((com.example.server.ISumServiceInterface) iin);
          }
          //没有,则生成并返回Proxy,即远程对象,然后,远程对象和本地对象通过transact方法通信(由Binder驱动实现)
          return new com.example.server.ISumServiceInterface.Stub.Proxy(obj);
        }
    
        @Override
        public android.os.IBinder asBinder() {
          return this;
        }
    
        /**
         * 这是本地对象接收数据的方法,当远程对象发起请求后,经过Binder驱动的封装处理,最终会调到server端的这个方法
         *
         * @param code  server端通过code来确定client端请求的目标方法,比如TRANSACTION_sum表示调用client端希望调用server端的sum方法
         * @param data  方法参数的封装,比如,data.readInt()得到参数a,再次调用data.readInt()得到参数b
         * @param reply 结果返回的地方,比如,sum方法得到a+b=100,就将100写入replay,replay通过Binder驱动的传送,返回给正在挂起等待结果的client端
         * @param flags 一般没什么卵用
         * @return
         * @throws android.os.RemoteException
         */
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
          java.lang.String descriptor = DESCRIPTOR;
          switch (code) {
            //client希望调用sum方法
            case TRANSACTION_sum: {
              data.enforceInterface(descriptor);
              int _arg0;
              _arg0 = data.readInt(); //读取参数a
              int _arg1;
              _arg1 = data.readInt(); //读取参数b
              int _result = this.sum(_arg0, _arg1); //调用本地对象的sum方法,这个this很明白了吧,就是Server端在SumService的onBind()时实例化的那个Stub
              reply.writeNoException();
              reply.writeInt(_result); //结果写入reply,经Binder驱动传送给正在挂起等待的client
              return true;
            }
          }
          return true;
        }
    
        //Proxy是远程对象,通过transact向Binder驱动发送数据,并最终通过onTransact发送给Server端
        private static class Proxy implements com.example.server.ISumServiceInterface {
          private android.os.IBinder mRemote;
    
          /**
           * Proxy构造函数,client端调用
           *
           * @param remote 指明是哪个Binder的代理,这里的remote是Server端SumServcie的onBind()方法返回的IBinder,即Server端的Stub
           * @return
           */
          Proxy(android.os.IBinder remote) {
            mRemote = remote;
          }
    
          @Override
          public android.os.IBinder asBinder() {
            return mRemote;
          }
    
          public java.lang.String getInterfaceDescriptor() {
            return DESCRIPTOR;
          }
    
          @Override
          //client端调用远程对象的sum方法,最终会调到server端的sum方法
          public int sum(int a, int b) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            int _result;
            try {
              _data.writeInterfaceToken(DESCRIPTOR); //指明向哪个Binder请求服务
              _data.writeInt(a);  //写入参数a
              _data.writeInt(b);  //写入参数b
              boolean _status = mRemote.transact(Stub.TRANSACTION_sum, _data, _reply, 0); //通过transact向server端发起TRANSACTION_sum请求,即sum方法
              if (!_status && getDefaultImpl() != null) {
                return getDefaultImpl().sum(a, b);
              }
              _reply.readException();
              _result = _reply.readInt(); //读取server端返回的结果,至此,一个完整的IPC通信结束。
            } finally {
              _reply.recycle();
              _data.recycle();
            }
            return _result;
          }
    
          public static com.example.server.ISumServiceInterface sDefaultImpl;
        }
    
        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_sum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
      }
    }

    分析ISumServiceInterface.java类,实际上主要包含Stub和Proxy两个静态内部类。

    一个完整的AIDL通信流程如下:

    1. Client端绑定Server端的SumService服务
    2. Server端的SumService返回IBinder,即ISumServiceInterface.Stub对象
    3. Client端调用ISumServiceInterface.Stub.onInterface(IBinder ibinder)方法,得到ISumServiceInterface.Stub.Proxy对象
    4. Client端调用Proxy的sum()方法,Proxy的sum()方法会调用transact()方法通过Binder驱动回调到Server端的onTransact()方法
    5. Client端挂起
    6. Server端通过onTransact()方法读取方法名和参数,进而回调本地Stub实例的sum方法
    7. Server端将sum()方法结果写入reply
    8. Client端读取reply结果

    有两个需要注意的地方:

    1. 因为Client端调用Proxy的sum方法后会挂起,所以如果预计server端是耗时操作,Client端应该做好异步操作
    2. 因为Server端的onTransact()方法运行在Binder线程池,所以即使sum()方法是耗时操作,也不用另起线程

    至此,AIDL的底层逻辑分析完毕!

    作者:观雪听涛
    本站所有原创内容基于知识共享-署名-非商业性使用4.0国际许可协议发布,欢迎转载引用,但必须保留署名和出处
  • 相关阅读:
    从零开始学android开发-通过WebService获取今日天气情况
    android常见错误-E/AndroidRuntime(13678): java.lang.NoClassDefFoundError:
    java 使用相对路径读取文件
    冒泡排序
    快速排序
    为什么使用抽象类?有什么好处?
    为什么用 抽象类,接口
    String.valueOf()
    Python 资源
    文本相似度-BM25算法
  • 原文地址:https://www.cnblogs.com/not2/p/14685579.html
Copyright © 2020-2023  润新知