美文网首页程序员
利用AIDL实现Android APP间通讯

利用AIDL实现Android APP间通讯

作者: 某翼 | 来源:发表于2018-02-10 23:13 被阅读0次

    最近工作上有一个需求需要实现应用间的双向通讯,且主应用可能需要接受多个应用的调用,同时有一定的安全要求,经过研究学习后决定使用AIDL进行实现。

    一、为什么用AIDL

    实现广义的进程间通讯有多种方式,包括但不限于:

    ①利用Messager机制进行

    ②利用ContentProvider通过数据交互方式进行

    ③利用指定Action的广播的方式进行单向通讯

    出于可能需要接受多个应用的调用这一方面,Android中的Messager机制是一个单线程处理的机制,不能满足可能出现的多应用同时调用的需求。出于安全需求这一方面,利用ContentProvider这样的方法也不能满足需求。出于需要双向通讯的需求这一方面,广播方式也要抛弃。

    AIDL全称Android Interface Definition Language,用于定义一个可供其他应用访问的接口。通常用来实现跨进程调用,访问其他应用的服务,实现进程间通讯等。

    AIDL利用的是C/S模型进行应用间的通讯,主应用实现一个Service扮演Server角色,其他应用可以通过bindService的方式来实现Client角色。

    二、Server端应用AIDL的实现

    Server端需要实现的代码分为三部分,一个是通讯过程中需要用到的自定义类(非必须),一个是AIDL文件,还有一个是Service代码。

    (1)自定义类

    AIDL本身支持6种基本类型的传输,分别是int,long,boolean,float,double,String。如果在这六种类型之外还需要传输自定义类型的参数,则需要编写自定义类。注意自定义类都必须继承Parcelable接口,并按需实现readFromParcel(Parcel dest)方法。最好每一个自定义类都放到单独一个文件里,便于后面AIDL文件的处理。

    (2)AIDL文件

    AIDL文件分为两类,一种是自定义类对应的AIDL文件,另一种是主要的接口AIDL文件。

    如工程中定义了自定义类,则每个自定义类都必须编写一个同名的AIDL文件。如A.java文件必须对应A.aidl文件。这类aidl文件格式都一样,里面都仅含两行代码如下:

    package **.**.**;

    parcel A;

    其中第一行声明包名,第二行声明可序列化的类的名称。用Android Studio自动添加AIDL文件时,命名不能与已有的java文件同名,这是因为自动生成的AIDL文件里都会添加一个同名的interface,这在Java语言规范里是不允许的。解决办法是先新增一个不同名字的AIDL文件,删除里面的interface并且写好代码后重命名文件。注意此类的AIDL文件不能添加接口!

    接口AIDL文件可以自定义名字,此处以IMyAidlInterface为例,新建AIDL文件后,import需要用到的自定义类,并且在接口里添加需要的方法即可。要注意以下几点:

    ①自动生成的文件里自带的basicTypes方法是官方告诉开发者AIDL中可以使用的基本类型,如果不想后面每次实现接口都重写该方法可以注释或者删除。

    ②存在多个方法时,不能存在方法名一样参数不同的方法,否则编译时会报错。

    ③方法参数是自定义类型时,需要在对应参数签名添加in/out/inout关键字,其中in表示输入型参数,即Server可以获取到Client传递过去的数据,但是不能对Client端的数据进行修改;out表示输出型参数,即Server获取不到Client传递过去的数据,但是能对Client端的数据进行修改;inout表示输入输出型参数,即Server可以获取到Client传递过去的数据,且能对Client端的数据进行修改。

    (3)Service代码

    上述两点做完后,先make一下,让Gandle处理aidl文件并自动生成IMyAidlInterface.java类,否则下面的步骤可能会报错。

    在Service中添加一个IMyAidlInterface.Stub类的变量,其中前面部分与接口AIDL文件名字一样,并且重写里面的IMyAidlInterface接口方法。该类继承于iBinder类,用作其他应用绑定该Service所用,故还应在Service的onBind(Intent intent)方法中返回该变量。

    完成Service的编码后,需要在Manifest中注册该Service,并且添加一个<Intent-filter><android:name>参数,以便于用Intent定向启动该Service。同时还应添加android:export=”true”属性,以允许外部应用调用。android:permission属性应有类似作用,此处没做测试,以后有空补上。

    三、Client端应用AIDL的实现

    与Client端应用类似,Client端应用也分为三部分,分别是通讯过程中需要用到的自定义类(非必须)、AIDL文件,以及调用Service代码。

    (1)自定义类

    将Server端接口需要用到的自定义类完全复制到Client端,注意包名要一致。

    (2)文件

    将Server端AIDL文件完全复制到Client端,注意包名要一致。

    (3)调用Service代码

    与Server端类似,上述两点做完后,先make一下,让Gandle处理aidl文件并自动生成IMyAidlInterface.java类,否则下面的步骤可能会报错。

    由于Server端提供的服务都封装在IMyAidInterface.Stub类中,该类通过Service暴露,Client端需要以IMyAidInterface的方式调用,故Client端需要做的工作分别是初始化一个IMyAidInterface,通过BindService的方式定向唤起Server端Service,实例化IMyAidInterface,然后进行调用。bindService部分示例代码如下:

    Intent intent = new Intent();

    intent.setAction("**.**.AIDLClientService");

    intent.setPackage("**.**");

    conn = new ServiceConnection(){

    @Override

       public void onServiceConnected(ComponentName name, IBinder service)

        {

               iMyAidlInterface =IMyAidlInterface.Stub.asInterface(service);

        }

       @Override

       public void onServiceDisconnected(ComponentName name){ }

    };

    BindService(intent, conn, BIND_AUTO_CREATE);

    之后就可以通过iMyAidInterface.***()的方式调用Server端应用提供的服务了。

    注意以下几点:

    ①调用bindService(Intent service, ServiceConnection conn, int flags)时,Android5.0以后的规范规定Intent不能隐式声明。

    ②定义时需要指定Action以及Package,其中Action是Server端的manifest中注册的参数,Package是接口AIDL文件所在的包名

    ③BindService是一个异步操作,BindService后不是立即执行onServiceConnected(ComponentName name, IBinder service),因此BindService后不能立即执行iMyAidInterface.***()。可以考虑在进入Activity前BindService,或在onCreate()时BindService并将iMyAidInterface.***()放在某个View的OnClick()事件里。

    ④注意在调用结束后或者onDestory()里调用unbindService(conn)

    相关文章

      网友评论

        本文标题:利用AIDL实现Android APP间通讯

        本文链接:https://www.haomeiwen.com/subject/fpghtftx.html