博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android IPC系列(二):AIDL源码分析
阅读量:4363 次
发布时间:2019-06-07

本文共 23496 字,大约阅读时间需要 78 分钟。

Android 中如何开启多进程

这里说一下一个应用开启多个进程的情况,不讨论多个应用多进程,Android开启多进程只有一种方法,在AndroidManifest文件中给四大组件指定 android:process属性,其实还有一种非常规的方法通过JNI在native层fork一个进程,这种属于特殊情况

复制代码

我们分别给SecondActivity和ThreeActivity指定了process属性,并且他们的属性值不同,所以现在又三个进程,MainActivity默认进程,默认进程进程名是包名,还有俩个我们自己指定的进程 我们通过shell命令,看一下是否有这些进程

$ adb shell ps | grep om.baidu.bpit.aibaidu.ipcu0_a59    2343  204   1606528 98700          0 0000000000 S com.baidu.bpit.aibaidu.ipcu0_a59    2360  204   1609616 97952          0 0000000000 S com.baidu.bpit.aibaidu.ipc:remoteu0_a59    2388  204   1766960 116352          0 0000000000 S om.baidu.bpit.aibaidu.ipc.remote.aa复制代码

我们发现SecondActivity和ThreeActivity指定进程名的方式不同一个是 android:process=":remote" ,一个是android:process="om.baidu.bpit.aibaidu.ipc.remote.aa这俩种有什么区别吗?

区别有两方面

  • “:”的意思是在当前进程名上加上本包名,另一种是一个完整的命名方式
  • “:”开头的进程,属于当前应用的私有进程,其他应用组件不可以和他跑在同一进程,完整包名的属于全局进程,应用可以通过ShareUid的方式可以和他跑在同一进程

Android 系统会为每一个进程分配一个唯一的Uid,具有相同Uid的应用才可以共享数据,比如data目录,组件信息等,不管他们是否跑在同一进程,如果他们跑在同一进程,那么除了共享data目录,组件信息,还可以共享内存

多进程会遇到的问题

每一个进程都会有一个独立的虚拟机,不同的虚拟机在内存分配不同的空间,所以只要需要通过共享内存,共享数据的操作都会失败

  • 静态成员和单例完全失效
  • 线程同步机制完全失效
  • SharePreference可靠性下降,因为sp底层是通过读写xml来实现的,那么并发读写会发生问题
  • Application多次创建

什么是Binder

  • 直观来说Binder是Android中的一个类,实现了IBinder
  • IPC角度来说,Binder是Android中一种跨进程通信的方式
  • Binder还可以理解为一种虚拟的物理设备,他的驱动是dev/binder,这种通信方式linux中没有
  • 从Android Framework角度来说,Binder是ServerManager连接各种Manager(如ActivityManager)和ManagerService的桥梁
  • 从Android应用层来说,他是服务端和客户端通信的媒介

Android 中的IPC方式

  • 使用Bundle,四大组件都是支持Bundle的,由于Bundle实现了Parcelable接口,可以在进程中传递数据,我们在一个进程中启动另一个进程的Bundle,可以进程间通信
  • 使用文件共享
  • 使用Messager
  • 使用Aidl
  • 使用ContentProvider
  • 使用socket

分析一下AIDL源码

上篇博客我们分析了如何使用AIDL,这篇我们分析一下之前定义的BookName.aidl文件到底生成了什么,编译项目后我们可以在gen目录下找到BookName.java文件,这个就是aidl生成文件,我们看一下文件内容

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: /Users/v_renxiaohui01/Downloads/aidl/app/src/main/aidl/com/baidu/bpit/aibaidu/aidl/BookName.aidl */package com.baidu.bpit.aibaidu.aidl;public interface BookName extends android.os.IInterface {    /**     * Local-side IPC implementation stub class.     */    public static abstract class Stub extends android.os.Binder implements com.baidu.bpit.aibaidu.aidl.BookName {        private static final java.lang.String DESCRIPTOR = "com.baidu.bpit.aibaidu.aidl.BookName";        /**         * Construct the stub at attach it to the interface.         */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an com.baidu.bpit.aibaidu.aidl.BookName interface,         * generating a proxy if needed.         */        public static com.baidu.bpit.aibaidu.aidl.BookName asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.baidu.bpit.aibaidu.aidl.BookName))) {                return ((com.baidu.bpit.aibaidu.aidl.BookName) iin);            }            return new com.baidu.bpit.aibaidu.aidl.BookName.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_getName: {                    data.enforceInterface(DESCRIPTOR);                    java.lang.String _result = this.getName();                    reply.writeNoException();                    reply.writeString(_result);                    return true;                }                case TRANSACTION_getList: {                    data.enforceInterface(DESCRIPTOR);                    java.util.List
_result = this.getList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addInout: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; if ((0 != data.readInt())) { _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addInout(_arg0); reply.writeNoException(); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } case TRANSACTION_addIn: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; if ((0 != data.readInt())) { _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addIn(_arg0); reply.writeNoException(); return true; } case TRANSACTION_addout: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; _arg0 = new com.baidu.bpit.aibaidu.aidl.User(); this.addout(_arg0); reply.writeNoException(); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.baidu.bpit.aibaidu.aidl.BookName { 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 getName() 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); mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public java.util.List
getList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List
_result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.baidu.bpit.aibaidu.aidl.User.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addInout, _data, _reply, 0); _reply.readException(); if ((0 != _reply.readInt())) { user.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } @Override public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addIn, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_addout, _data, _reply, 0); _reply.readException(); if ((0 != _reply.readInt())) { user.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_addInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); static final int TRANSACTION_addIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); static final int TRANSACTION_addout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4); } public java.lang.String getName() throws android.os.RemoteException; public java.util.List
getList() throws android.os.RemoteException; public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException; public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException; public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;}复制代码

我们分析一下这个类,首先BookName.java继承自android.os.IInterface接口,同时他自己也是一个接口,所有在Binder传输的都需要继承IInterface接口,这个类首先声明了5个方法getName,getList,addInout,addin,addout,这些是我们在aidl文件定义的,同时他还为每个方法声明一个整形,为了在transct区分方法,当应用处于不同进程时需要走transct方法,这个逻辑由sub内部类proxy代理

IBinder

IBinder是一个接口,它代表了一种跨进程传输的能力;只要实现了这个接口,就能将这个对象进行跨进程传递;这是驱动底层支持的;在跨进程数据流经驱动的时候,驱动会识别IBinder类型的数据,从而自动完成不同进程Binder本地对象以及Binder代理对象的转换。

IInterface

这里的IInterface代表的就是远程server对象具有什么能力。具体来说,就是aidl里面的接口。

DESCRIPTOR

Binder的唯一标识,一般用Binder的类名表示,

private static final java.lang.String DESCRIPTOR = "com.baidu.bpit.aibaidu.aidl.BookName";复制代码

asInterface

用于将服务端的Binder转换为客户端所需的AIDL接口类型对象,这种转换是区分进程的,如果客户端和服务端位于同一进程,那么返回的就是服务端本身Stub的对象,如果是不同进程,就返回Stub.Proxy对象

asBinder

此方法用于返回当前的Binder对象

onTransact

该方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,请求会通过底层封装后,交由此方法处理,服务端通过code来判断客户端请求的方法是什么,接着从data取出目标方法需要的参数(如果目标方法有参数的话),然后执行目标方法,执行完之后就向reply中写入返回值(如果目标方法有返回值的话),这就是这个方法的执行过程,如果此方法返回false,那么客户端请求失败,可以以此做验证

Proxy/addList

此方法运行在客户端,当客户端调用从方法时,他的内部实现是这样的,首先准备输入型Parcel对象_data,输出型Parcel对象_reply,和返回值对象List,然后把该方法的参数写入_data(如果有参数的话),接着调用transact方法发起RPC(远程过程调用),同时当前线程挂起,然后服务端的onTransact方法会被调用,知道RPC过程返回后,当前线程继续执行,并从_reply取出返回结果

其他几个Proxy中的方法和此过程一样

这就是Binder工作机制,我们需要注意俩点

  • 当客户端发起远程请求是时,当前线程会被挂起,知道服务端返回,如果远程方法是一个耗时方法,那不能在UI线程做
  • 服务端的Binder方法运行在Binder线程池中,所以Binder方法不管是否耗时都可以同步调用

自定义一个Binder

从上方流程看出,其实我们可以不通过aidl就可以实现Binder,之所以有了Aidl是为了方便生成代码,那我们手动生成一个Bidner

首先生成一个aidl性质的接口,继承自IInterface即可,如下:

public interface CustomBookName extends IInterface {    static final java.lang.String DESCRIPTOR = "com.baidu.bpit.aibaidu.aidl.BookName";    static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    static final int TRANSACTION_getList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);    static final int TRANSACTION_addInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);    static final int TRANSACTION_addIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);    static final int TRANSACTION_addout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);    public java.lang.String getName() throws android.os.RemoteException;    public java.util.List
getList() throws android.os.RemoteException; public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException; public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException; public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;}复制代码

之后我们需要实现一个Binder继承自Binder,和上方的接口

public abstract class CustomBinder extends Binder implements CustomBookName {    /**     * Construct the stub at attach it to the interface.     */    public CustomBinder() {        this.attachInterface(this, DESCRIPTOR);    }    /**     * Cast an IBinder object into an com.baidu.bpit.aibaidu.aidl.BookName interface,     * generating a proxy if needed.     */    public static CustomBookName asInterface(android.os.IBinder obj) {        if ((obj == null)) {            return null;        }        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);        if (((iin != null) && (iin instanceof CustomBookName))) {            return (CustomBookName) iin;        }        return new CustomBinder.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_getName: {                data.enforceInterface(DESCRIPTOR);                java.lang.String _result = this.getName();                reply.writeNoException();                reply.writeString(_result);                return true;            }            case TRANSACTION_getList: {                data.enforceInterface(DESCRIPTOR);                java.util.List
_result = this.getList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addInout: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; if ((0 != data.readInt())) { _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addInout(_arg0); reply.writeNoException(); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } case TRANSACTION_addIn: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; if ((0 != data.readInt())) { _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addIn(_arg0); reply.writeNoException(); return true; } case TRANSACTION_addout: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; _arg0 = new com.baidu.bpit.aibaidu.aidl.User(); this.addout(_arg0); reply.writeNoException(); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements CustomBookName { 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 getName() 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); mRemote.transact(TRANSACTION_getName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public java.util.List
getList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List
_result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(TRANSACTION_getList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.baidu.bpit.aibaidu.aidl.User.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(TRANSACTION_addInout, _data, _reply, 0); _reply.readException(); if ((0 != _reply.readInt())) { user.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } @Override public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(TRANSACTION_addIn, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(TRANSACTION_addout, _data, _reply, 0); _reply.readException(); if ((0 != _reply.readInt())) { user.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } }}复制代码

这里面的主要方法,极其作用我们上方都讲过

下面我们使用一下自定义的Bidner类

  • 首先实现自己的binder
class MyCustomBinder extends CustomBinder {        @Override        public String getName() throws RemoteException {            return "我是自定义的binder";        }        @Override        public List
getList() throws RemoteException { return null; } @Override public void addInout(User user) throws RemoteException { } @Override public void addIn(User user) throws RemoteException { } @Override public void addout(User user) throws RemoteException { } }复制代码
  • 然后在service中返回自定义的binder
@Override    public IBinder onBind(Intent intent) {        return new MyCustomBinder();    }复制代码
  • 把自定义的binder类拷进客户端
  • 客户端绑定service获取binder
private void bindServer() {        Intent mIntent = new Intent();        //你定义的service的action        mIntent.setAction("com.aaa.aaa");        //这里你需要设置你应用的包名        mIntent.setPackage("com.baidu.bpit.aibaidu.aidl");        bindService(mIntent, new ServiceConnection() {            @Override            public void onServiceConnected(ComponentName name, IBinder service) {                customBookName = CustomBinder.asInterface(service);            }            @Override            public void onServiceDisconnected(ComponentName name) {            }        }, BIND_AUTO_CREATE);    }复制代码
  • 点击按钮调用自定义binder的getName方法
private void initView() {        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                try {                    String name = customBookName.getName();                    Log.d("mmm",name);                } catch (RemoteException e) {                    e.printStackTrace();                }            }        });    }复制代码
  • 看下log
01-18 19:14:45.688 3554-3554/com.baidu.bpit.aibaidu.client D/mmm: 我是自定义的binder复制代码

成功了

转载于:https://juejin.im/post/5d4d136b5188255d846009d3

你可能感兴趣的文章
Ionic3 demo TallyBook 实例3
查看>>
laravel服务容器
查看>>
Entity Framework的查询
查看>>
ZH奶酪:Python按行读取文件
查看>>
07-使用循环进行遍历数组(运算符)
查看>>
控件布局通用解决方案
查看>>
scala流程控制语句以及方法和函数
查看>>
MySQL的sql_mode模式
查看>>
windows命令——explorer
查看>>
<转载>Bootstrap 入门教程 http://www.cnblogs.com/ventlam/archive/2012/05/28/2520703.html 系列...
查看>>
jquery和js cookie的使用解析
查看>>
类的内置方法
查看>>
世界是数字的 读后感
查看>>
算法项目步骤流程
查看>>
POJ 2942 Knights of the Round Table ★(点双连通分量+二分图判定)
查看>>
10.scheam.xml的配置
查看>>
通过命令给Linux(CentOS)分区
查看>>
python接口自动化3-自动发帖(session)
查看>>
复杂问题的简单抽象:魔兽世界中的兔子们
查看>>
那些美到极致的语言!
查看>>