Binder原理、AIDL的使用、多进程的定义和特性

Binder原理、AIDL的使用、多进程的定义和特性

多进程的定义和特性

进程指的是系统中能独立运行并作为资源分配的基本单位,多进程一般指一个应用中包含多个进程

Android需要开启多进程模式需要在Manifest文件给四大组件加上android:process属性

Android系统中每个应用程序都会有一个唯一的UID,每个UID对应的程序都是独立的,不可共享内存和数据,多进程模式可以通过在Manifest文件给四大组件加上android:process属性开启,这些进程都有一个自己PID,他们属于同一个应用,所以他们的UID是一样,这些进程拥有自己的内存空间,他们可共享数据但不共享内存,所以内存空间上他们是互相不影响的,这也说明进程的结构性,独立性,并发性和异步性

开启多进程导致的问题:

  1. 静态成员,单例模式和线程同步机制失效,不同进程操作各自内存下的对象
  2. SharedPreferences的可靠性下降,多进程并发操作导致sp写入或读取不可靠
  3. Application多次初始化,每个进程启动都会创建新的application

AIDL的使用

基本使用
在这里插入图片描述

1.服务端
1.1创建一个Service监听客户端的连接请求
  • 1.1.1 创建一个实现与Service的类,并在Manifest文件声明
1.2创建一个AIDL文件,在AIDL文件中声明对客户端提供的接口
  • 1.2.1 AIDL文件用到自定义的Parcelable对象必须新建一个同名的aidl文件,并声明为Pracelable类型
  • 1.2.2 aidl自定义的Parcelable对象和AIDL对象必须显示import进来
  • 1.2.3 aidl中除了,基本数据类型,其他类型的参数必须标上方向in,out或inout
    void addBook(in Book book);
1.3在Service中实现接口,并返回
  • 1.3.1 创建一个binder对象,对象实现与aidl接口.Stub,并实现它的接口
  • 1.3.2 aidl的方法时在服务端的Binder线程池中执行的,注意处理线程同步(CopyOnWriteArrayList和ConcurrentHashMap)
2.客户端
2.1绑定Service,将service返回来的Binder对象转成AIDL接口所属的类型
  • 2.1.1 aidl接口.Stub.asInterface(binder)将Binder对象转换
2.2调用AIDL提供的接口
  • 2.2.1 调用接口注意有可能是耗时操作
2.3在onDestroy方法解绑Service

客户端监听服务端数据变化

1.服务端
1.1 创建一个Listener.aidl文件,声明监听回调接口方法
1.2 在aidl接口中增加addListener(listener)和removeListener(listener)的接口方法
1.3 在Service的Binder中实现接口方法,并添加一个容器保存listener
  • 1.3.1 使用RemoteCallbackList容器来保存listener
  • 1.3.2 使用register和unregister方法添加或移除listener
  • 1.3.3 使用getBroadcastItem(index)获取Listener
  • 1.3.4 beginBroadcast和finishBroadcast必须要配对使用
  • 1.3.5 listener的回调方法运行在客户端的Binder线程池中,所以建议在非UI线程运行
2.客户端
2.1 创建一个Listener.Stub,实现接口回调方法获取数据
2.2 在绑定Service后调用addListener添加监听
2.3 在回调方法接受服务端回调信息
  • 2.3.1 需要考虑是否主线程
2.4 在onDestroy方法调用removeListener(listener)方法移除接口

监听服务端的断连状态
1.通过onServiceDisconnedtion方法监听
2.通过设置DeathRecipient在biderDied监听
这区别在于1在客户端的UI线程中回调,2在Binder的线程池中被回调

服务端添加权限认证

1.在onBind方法中进行验证
1.1 在Manifest文件中声明所需的权限
	<permission android:name="${applicationId}.permission.ACCESS_BOOK_SERVICE"
        android:protectionLevel="normal"/>
1.2 在服务端的onBind方法中校验权限
		int check = checkCallingOrSelfPermission("Manifest中声明的权限");
        if (check == PackageManager.PERMISSION_DENIED){
            return null;
        }
		return binder;
1.3 在使用该服务的客户端就必须在Manifest中添加服务权限声明
<uses-permission android:name="${applicationId}.permission.ACCESS_BOOK_SERVICE"/>
2.在服务端的onTransact方法中进行权限验证
1.1 重写Binder的onTransact,在发放里采用和onBind方法一样的permission方法检验
2.1 重写Binder的onTransact,通过getCallingUid和getCallingPid获取Uid和Pid,校验调用服务者的包名
	@Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

//检验权限声明
        int check = checkCallingOrSelfPermission("${applicationId}.permission.ACCESS_BOOK_SERVICE");
        if (check == PackageManager.PERMISSION_DENIED){
            return false;
        }

//检验包名
        String packageName = null;
        String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
        if (packages != null && packages.length > 0){
            packageName = packages[0];
        }
        if (!packageName.startsWith("${applicationId}")){
            return false;
        }

        return super.onTransact(code, data, reply, flags);
    }

Binder连接池

1.创建IBinderPool.aidl文件,声明IBinder queryBinder(int binderCode)方法
2.创建BinderPoolImpl.java实现IBinderPool.Stub
  • 2.1 定义每个服务的binderCoder常量
  • 2.2 根据传入的binderCode构造对应的Binder实现返回
3.创建BinderPoolService.java,在onbind返回BinderPoolImpl的实例
4.创建BinderPool.java
  • 4.1 构造BinderPool时连接绑定BinderPoolService
  • 4.2 在绑定服务的过程中使用CountDownLatch锁住线程,服务连接后释放线程
  • 4.3 设置IBinder.DeathRecipient监听,断连时重连服务
5.使用BinderPool
  • 5.1 在子线程中获取BinderPool单例
  • 5.2 调用queryBidner,传入binderCode获取Binder实例

Binder的原理

远程服务的调用过程

1.Activity创建Intent绑定服务
2.在连接成功的回调中asInterface(IBinder pBinder)拿到远程服务代理
  • 2.1 拿到的服务是IBookManager.Stub.Proxy(IBinder pBinder)
	public static com.maotou.aidldemo.IBookManager asInterface(android.os.IBinder obj) {
         if ((obj == null)) {
             return null;
         }
//查询本地服务
         android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
         if (((iin != null) && (iin instanceof com.maotou.aidldemo.IBookManager))) {
             return ((com.maotou.aidldemo.IBookManager) iin);
         }
//远程服务走了这里
         return new com.maotou.aidldemo.IBookManager.Stub.Proxy(obj);
     }
3.调用服务的addBook方法List<Book> addBook(in Book book)
  • 3.1 这里实际调用了IBookManager.Stub.Proxy的addBook方法
	@Override
    public java.util.List<com.maotou.aidldemo.Book> addBook(com.maotou.aidldemo.Book book) throws android.os.RemoteException {
       android.os.Parcel _data = android.os.Parcel.obtain();
       android.os.Parcel _reply = android.os.Parcel.obtain();
       java.util.List<com.maotou.aidldemo.Book> _result;
       try {
           _data.writeInterfaceToken(DESCRIPTOR);
           if ((book != null)) {
               _data.writeInt(1);
               book.writeToParcel(_data, 0);
           } else {
               _data.writeInt(0);
           }
           mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
           _reply.readException();
           _result = _reply.createTypedArrayList(com.maotou.aidldemo.Book.CREATOR);
       } finally {
           _reply.recycle();
           _data.recycle();
       }
       return _result;
   }
  • 3.2 addBook方法中数据通过实现Parelable是重写的writeToParel写入_data
4.调用远程服务代理mRemote.transact()
  • 4.1 transact()方法的实现在Binder中
    public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
    
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }
    
    • 4.2 transact中调到onTransact,期实现在IBookManager.Stub.onTransact()
5.调用远程服务的业务方法IBookManager.Stub.onTransact()
	case TRANSACTION_addBook: {
        data.enforceInterface(descriptor);
        com.maotou.aidldemo.Book _arg0;
        if ((0 != data.readInt())) {
            _arg0 = com.maotou.aidldemo.Book.CREATOR.createFromParcel(data);
        } else {
            _arg0 = null;
        }
        java.util.List<com.maotou.aidldemo.Book> _result = this.addBook(_arg0);
        reply.writeNoException();
        reply.writeTypedList(_result);
        return true;
    }
  • 5.1 通过实现Parelable是重写的createFromParcel方法将data转为实体Book
  • 5.2 调用IBookManager.Stub.addBook(),这个addBook方法的实现就是在BookService里写的业务逻辑
  • 5.3 返回书本列表_result,通过writeTypedList方法将_result写入reply
6.onTransact方法走完,一层一层返回到IBookManager.Stub.Proxy的addBook方法
  • 6.1 通过reply的createTypedArrayList方法获取业务返回的图书列表
  • 6.2 最终将结果返回掉业务调用端

参考资料

Android深入浅出之Binder机制
MediaService初始化过程

int main(int argc, char** argv)
{

//获得一个ProcessState实例
	sp<ProcessState> proc(ProcessState::self());

//得到一个ServiceManager对象
    sp<IServiceManager> sm = defaultServiceManager();

    MediaPlayerService::instantiate();//初始化MediaPlayerService服务

    ProcessState::self()->startThreadPool();//看名字,启动Process的线程池?

    IPCThreadState::self()->joinThreadPool();//将自己加入到刚才的线程池?
}

第一步
打开/dev/binder,又来和内核Binder机制的交互通道,映射fd内存(估计这块内存适合binder设备共享的)

第二步
初始化ServiceManager,中间创建ProcessState,IPCThreadState(主线程),BpBinder(handler值为0),defaultServiceManager()返回了ServiceManager的代理BpServiceManager,他的remote是BpBinder

第三步
初始化MediaService并添加进ServiceManager,过程中是将BnMediaPlayService通过BpServiceManager与ServiceManager通讯,将其添加进ServiceManager
调用addService方法时,是调用了remote(BpBinder)的transact方法,再到IPCTreadState的transact方法,通过writeTransactionData()方法将MediaPlayService的名称和data(命令包)写入mOut缓存区,最后从wirteForResponse()中调用talkWriteDriver()通过ioctl()将mIn和mOut赋值给bwr(binder_write_read)

第四步
BnServiceManager(servicemanager.c)处理BpServiceManager发送到binderDriver的请求命令,BnServiceManager初始化打开/dev/binder/,将自己设置为manager(BINDER_SET_CONTEXT_MGR,0写入binderDriver),调用binder_loop()开启循环从binderDriver中读取请求命令,binder_parse()解析命令,最后在一个类似于handleMessage的地方(svcmgr_handler)处理解析出来的命令,svcmgr_handler()中通过code(SVC_MGR_ADD_SERVICE)去调用对应方法,通过code调用do_add_service()将服务添加到svclist(ServiceManager的服务列表)

第五步
创建线程池,在工作线程中循环调用executeCommand(cmd)处理消息,最后调用BBinder的transact再调用自己的onTransact通过code确认调用那个业务方法

在这里插入图片描述

罗升阳的Android进程间通信(IPC)机制Binder简要介绍和学习计划
MediaService初始化过程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值