利用AIDL实现Android APP间通讯

最近工作上有一个需求需要实现应用间的双向通讯,且主应用可能需要接受多个应用的调用,同时有一定的安全要求,经过研究学习后决定使用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)

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 175,764评论 25 709
  • 【Android Service】 Service 简介(★★★) 很多情况下,一些与用户很少需要产生交互的应用程...
    Rtia阅读 8,320评论 1 21
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,644评论 19 139
  • Jianwei's blog 首页 分类 关于 归档 标签 巧用Android多进程,微信,微博等主流App都在用...
    justCode_阅读 11,190评论 1 23
  • 亲爱的网友们,请大家帮帮这个可怜的孩子吧!我是孩子的舅舅,去年农历12月13,本来是高高兴兴的一天,因为我外甥女要...
    萧玄辞阅读 2,206评论 0 0