Android进程间通信(一)——Binder机制和AIDL的理解

Android 进程间通信

为什么要去理解Android的进程间通信机制

对于Android开发工程师来说,如果不去理解进程间通信机制也可以使用系统提供的API完成应用开发,但如果想要达到更高的层级,那么就不能简单只会调用API。无论是工作中遇到一些疑难问题,还是想要学习源码的一些功能实现,或者是想要提升APP的性能等,这些工作都需要我们去看系统的源码,而系统的源码中进程间通信无处不在,如果不理解进程间通信机制,那么很难看懂系统源码,而且容易迷失在大量的代码中。

Android 进程间通信机制

为什么使用Binder作为Android进程间通信机制

Android Bander设计与实现 - 设计篇 这篇文章写得很好了。主要是为了弥补Linux中其他进程间通信方式得性能和安全性不足。当然Binder机制也并非是谷歌为了Android原创技术,Binder机制源于OpenBinder,OpenBinder要早于Android系统出现。所以如果想要立即Android得进程间通信,主要就是理解Binder机制。

Binder进程间通信基本框架

在Android中,2个应用或者进程之间的通信都需要经过Binder代理,二者不能直接通信,同样APP在使用系统服务时也需要跨进程通信,比如我们最常用的ActivityManagerService(AMS)也是一个系统服务进程,此外APP使用WIFI 、定位、媒体服务等都是系统进程,APP想要使用这些系统服务的功能一定要通过Binder进行通信。

Binder到底是什么

我们一直在说利用Binder机制进行进程间通信,那么Binder具体是什么?是一个Java类,还是一个底层驱动?通常我们说Binder机制是Android系统不同层Binder相关代码组成的一套跨进程通信功能。Binder机制相关代码从最底层的驱动层到最顶层的应用层都有,所以要读懂Binder机制,就需要我们耐心的逐层进行分析。


Binder机制代码结构

如何理解AIDL

我们从上图没有看到任何AIDL相关的信息,也就是说Binder机制是与AIDL无关的,那么我们日常中如果要跨进程都要写一个AIDL类然后由AS生成一些Java类,我们使用这些类实现进程间通信,这么做的目的其实是由AS帮我们生成一些模板代码,减少我们的工作和出错概率,其实不用AIDL我们也可以实现Binder通信,并且可以更好的理解Binder机制。下面我写一个Demo进程,这个Demo中有AIDL文件并生成相关代码,但我们不用,只是用来作为对比,我们用最少的代码实现Binder通信,并通过对比我们写的代码和AIDL生成的代码来更好的理解AIDL生成的代码的作用。代码Github

不使用ADIL,手动实现进程间通信

项目结构

代码中client为客户端,server为服务端



客户端进程发送一个字符串给服务端,服务端进程接收到将字符显示到界面上。项目中没有用到AIDL为我们生成Binder通信类,而是用最简单的方式实现Binder通信,因而我们可以看清Binder通信最关键的地方。首先我们要知道,实现了IBinder接口的类的对象是可以跨进程传递的。

服务端

1.服务端RemoteService继承Service
2.创建一个继承Binder的类ServerBinder,并覆写onTransact方法,用于处理Client的调用,Binder实现了IBinder接口
3.服务端覆写Service的onBind方法,返回一个ServerBinder对象(这个ServerBinder对象是最终传递给Client端)

public class RemoteService extends Service {
    public static final int TRANSAVTION_showMessage = IBinder.FIRST_CALL_TRANSACTION;

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

    static class ServerBinder extends Binder   {
        public ServerBinder() {
        }

        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {

            switch (code) {
                case TRANSAVTION_showMessage:
                    String message = data.readString();
                    Log.d("ServerBinder", "showMessage " + message);
                    if (ServerMainActivity.tvShowMessage != null) {//显示收到数据逻辑
                        new Handler(Looper.getMainLooper()).post(new Runnable() {
                            @Override
                            public void run() {
                                ServerMainActivity.tvShowMessage.setText(message);
                            }
                        });
                    }
                    if (reply != null) {
                        reply.writeNoException();
                    }
                    return true;
            }
            return super.onTransact(code, data, reply, flags);
        }


    }
}
客户端

1.客户端创建一个ServiceConnection对象,用于与服务端建立连接,并获取到服务端的IBinder对象
2.客户端通过bindService与服务端的RemoteService建立连接

public class ClientMainActivity extends AppCompatActivity {
    private Button mSendString;
    private EditText mStingEditText;
    public static final int TRANSAVTION_showMessage = IBinder.FIRST_CALL_TRANSACTION;
    private IBinder mServer;//服务端的Binder对象
    private boolean isConnection = false;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            
            isConnection = true;
            mServer = service;//建立连接成功,保存服务端进程的IBinder对象
            Log.d("Client"," onServiceConnected success");
        }

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

    //与服务端进程中RemoteService建立连接
    private void attemptToBindService() {
        Intent intent = new Intent();
        intent.setClassName("com.binder.server", "com.binder.server.RemoteService");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSendString = findViewById(R.id.btn_send_string);
        mStingEditText = findViewById(R.id.et_string);
        mSendString.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isConnection) {
                    attemptToBindService();
                    return;
                }
                //发送数据给服务端进程
                Parcel data = Parcel.obtain();
                Parcel replay = Parcel.obtain();
                if (mServer != null) {
                    try {
                        data.writeString(mStingEditText.getText().toString());
                        Log.d("Client"," mServer.transact call");
                      //发送数据到服务端进程
                        mServer.transact(TRANSAVTION_showMessage, data, replay, 0);
                        replay.readException();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    } finally {
                        replay.recycle();
                        data.recycle();
                    }
                }


            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!isConnection) {
            attemptToBindService();
        }
    }

从上面的代码来看Binder的跨进程通信核心就是客户端获取到服务端的IBinder对象,然后调用这个对象的transact方法发送数据,实现进程间通信。

使用ADIL生成相关类,进行进程间通信

加入AIDL文件
interface IShowMessageAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
   void showMessage(String msg);
}
修改Client端代码
public class ClientMainActivityUseAidl extends AppCompatActivity {
    private Button mSendString;
    private EditText mStingEditText;
    private IShowMessageAidlInterface mServer;//服务端的Binder对象代理
    private boolean isConnection = false;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isConnection = true;
            //调用IShowMessageAidlInterface.Stub.asInterface静态方法,将service转换为一接口
            mServer = IShowMessageAidlInterface.Stub.asInterface(service);
            Log.d("Client"," onServiceConnected success");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isConnection = false;
        }
    };
    private void attemptToBindService() {
        Intent intent = new Intent();
        intent.setClassName("com.binder.server", "com.binder.server.RemoteServiceUseAidl");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSendString = findViewById(R.id.btn_send_string);
        mStingEditText = findViewById(R.id.et_string);
        mSendString.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isConnection) {
                    attemptToBindService();
                    return;
                }
                try {
                  //直接调用接口的showMessage方法
                    mServer.showMessage(mStingEditText.getText().toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!isConnection) {
            attemptToBindService();
        }
    }

1.客户端利用 IShowMessageAidlInterface生成类中的Stub内部类将接受到的IBinder对象转换为一个接口
2.在发送数据时,直接调用IShowMessageAidlInterface接口的showMessage方法

asInterface方法
   public static com.binder.server.IShowMessageAidlInterface asInterface(android.os.IBinder obj)
   {
     if ((obj==null)) {
       return null;
     }
   //查询obj对象是否是本地接口,也就是是不是在同一个进程
     android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
     if (((iin!=null)&&(iin instanceof com.binder.server.IShowMessageAidlInterface))) {
       如果是同一个进程直接返回
       return ((com.binder.server.IShowMessageAidlInterface)iin);
     }
 //如果是不同进程,则将IBinder对象利用Proxy封装一层
     return new com.binder.server.IShowMessageAidlInterface.Stub.Proxy(obj);
   }
Proxy类
 private static class Proxy implements com.binder.server.IShowMessageAidlInterface
    {
      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;
      }
      /**
           * Demonstrates some basic types that you can use as parameters
           * and return values in AIDL.
           */
      //代理对象做的工作是把AIDL接口中定义的方法中的数据进行封装,方便进行跨进程传输
      @Override public void showMessage(java.lang.String msg) 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.writeString(msg);
          boolean _status = mRemote.transact(Stub.TRANSACTION_showMessage, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().showMessage(msg);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      public static com.binder.server.IShowMessageAidlInterface sDefaultImpl;
    }

所以我们可以知道,客户端用到了AIDL文件生成Stub类中的asInterface方法,把接收到的远程IBinder转换为IShowMessageAidlInterface接口,而这个接口的具体实现其实是Proxy类,代理类对方法传入数据进行封装,然后发送给mRemote 服务端。

修改服务端代码

public class RemoteServiceUseAidl extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IShowMessageAidlInterface.Stub() {
            @Override
            public void showMessage(String msg) throws RemoteException {
                if (ServerMainActivity.tvShowMessage != null) {
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            ServerMainActivity.tvShowMessage.setText(msg);
                        }
                    });
                }
            }
        };
    }
}

服务端的 onBind方法返回AIDL生成的Stub类的对象,Stub是个抽象类,其中待实现的方法为AIDL中定义的showMessage方法。

 public static abstract class Stub extends android.os.Binder implements com.binder.server.IShowMessageAidlInterface
  {
    private static final java.lang.String DESCRIPTOR = "com.binder.server.IShowMessageAidlInterface";
    static final int TRANSACTION_showMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    @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
    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_showMessage:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.showMessage(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
   
  }

可以看到Sub抽象类中继承自Binder,也就是客端最终拿到的是这个Stub IBinder对象,客户端调用tansact方法最终会调用到Stub类的onTransact进行处理,Stub的onTransact方法根据code确定客端户调用了哪个方法,然后对接收到的data数据进行读取解析,将处理好的数据交给IShowMessageAidlInterface中对应的方法。

总结:
1.AIDL生成的类中Stub的静态方法asInterface和Proxy类是给客户端用于发送数据的
2.Stub抽象类是由服务端实现,接收处理客户端数据的

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

推荐阅读更多精彩内容