进程间消息机制:AIDL的使用与分析

207 阅读2分钟

遇到的问题

最近在看《Android开发艺术探索》,为了更深入了解一下Android通信机制,浅试了一下Android studio中实现AIDL,结果调试了一上午,看了好几遍文档,也没看出来有啥问题,但是都无法正常通信。

原来在API级别高于30的时候加入了限制,所以有两种办法:

方法一:把项目的 targetSdkVersion 版本修改到30以下

方法二:在manifest.xml下加入:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"   
    package="com.example.myapplication">
 
    <queries>
        <package android:name="com.example.myaidlserver" />
    </queries>
    // 如果想要与其他的应用进行AIDL通信的话,需要在这里注册包名的信息
    // 官方文档:https://developer.android.google.cn/guide/topics/manifest/queries-element?hl=en
    
    <application
        ......
        <activity

其他代码就不贴了,可以参考远程服务Service(含AIDL & IPC讲解)

预备知识

Binder是Android的一个类,继承于IBinder接口;
它是ServiceManager ----- ActivityManager、WindowManager、XXManagerService中的桥梁

生成的aidl.java文件解读

interface MyAidl implements IInterface{

    // 默认实现
    static class Default implements MyAidl{
        int add(){return 0;}
    }

    IBinder asBinder(){return null;}

    static abstract class Stub extends Binder implements MyAidl{

        static MyAidl asInterface(IBinder obj){
            return MyAdidl.Stub.Proxy(obj)
        }

        IBinder asBinder(){ return this; }

        boolean onTranact(){ switch语句,代表着输入 }

        setDefaultImpl(){ Stub.proxy.Impl = impl; }

        static Proxy implements MyAidl{
            Proxy(IBinder remote){ mRemote = remote; }
            int add(){
                remote.onTransact()
            } 
        }
    }
}
classDiagram
IMyAidl <|-- absStub
Stub <|-- Proxy

class IMyAidl{
Default
}

class absStub{
asInterface(IBinder obj)
asBinder()
onTranact()
}

class Proxy{
customOperation(
}

在生成的aidl.java中,有Aidl接口,以及Stub和Proxy两个实现了MyAidl接口的类。其中Stub定义在MyAidl接口中,Proxy定义在Stub类中。 为什么不把他们分开呢,为了防止有多个ADIL类时,Stub和Proxy重名,把他们套娃在aidl接口里,使用时就可以直接Aidl.Stub也直接方便。

Stub类继承了 Binder, 说明它是一个 Binder 本地对象,它实现了 IInterface 接口 Stub类中重要的是的asInterface方法和onTransact方法。 在Client中,我们会这么写:

MyAidl.Stub.asInterface(某IBinder对象).add(1, 2);        

asInterface方法的作用是判断参数——也就是IBinder对象,和自己是否在同一个进程:

是的话则直接转换、直接使用

// 如果不是则把这个IBinder参数包装成一个Proxy对象
return new MyAidl.Stub.Proxy(obj)
// 再调用Stub的sum方法->间接调用Proxy的sum方法
// Proxy在自己的sum方法中,会使用Parcelable来准备数据
// 最后使用IBinder的transact方法传递参数,把数据就传给Binder的Server端了。
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0)
// _data中包含函数名称、函数参数,_reply接收函数返回值

在Server中,通过onTransact方法接收数据,找到对应方法,返回结果

总结

顺便总结一下AIDL:

  • 其实这个过程中就是根据AIDL文件生成了一个java文件来实现进程间通信,我们完全可以自己写一个这样的ADIL.java文件
  • ADIL其实就是是binder的延伸,binder在kennel层,ADIL在Framework层
  • Messager也是AIDL封装的,但是它只能进行进程间通信
  • 和ContentProvider相比呢,底层都是Binder
  • AIDL的优势在于支持一对多并发实时通信
  • ContentProvider可以理解为受约束的AIDL,它的优势在于使用方便,有强大的数据共享能力,比较适合在通讯录、日历使用

四大组件的启动和后续流程,都是在和AMS来来回回的通信,四大组件给AMS发消息,四大组件就是Client,而AMS就是Server;AMS发消息通知四大组件,角色就互换,就这样发送返回发送返回。为什么称为Android四大组件,我理解它们都是能够直接和AMS通信的,那么四大组件共同的源头是哪里呢?又是怎么样统一管理的?小伙伴们可能就会好奇了

请移步:Android:Context家族史