写点什么

【中级—(1),Android 资深架构师分享学习经验及总结

用户头像
Android架构
关注
发布于: 2021 年 11 月 05 日

this.bookName = in.readString();}


public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {@Overridepublic Book createFromParcel(Parcel source) {return new Book(source);}


@Overridepublic Book[] newArray(int size) {return new Book[size];}};}


由于 AIDL 只支持数据类型:基本类型(int,long,char,boolean 等),String,CharSequence,List,Map,其他类型必须使用 import 导入,即使它们可能在同一个包里,比如上面的 Book。


最终 IBookManager.aidl 的实现


// Declare any non-default types here with import statementsimport com.lvr.aidldemo.Book;


interface IBookManager {/**


  • Demonstrates some basic types that you can use as parameters

  • and return values in AIDL.*/void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);


void addBook(in Book book);


List<Book> getBookList();


}


注意:如果自定义的 Parcelable 对象,必须创建一个和它同名的 AIDL 文件,并在其中声明它为 parcelable 类型。


Book.aidl


// Book.aidlpackage com.lvr.aidldemo;


parcelable Book;


以上就是 AIDL 部分的实现,一共三个文件。


然后 Make Project ,SDK 为自动为我们生成对应的 Binder 类。


在如下路径下:



其中该接口中有个重要的内部类 Stub ,继承了 Binder 类,同时实现了 IBookManager 接口。这个内部类是接下来的关键内容。


public static abstract class Stub extends android.os.Binder implements com.lvr.aidldemo.IBookManager{}


服务端服务端首先要创建一个 Service 用来监听客户端的连接请求。然后在 Service 中实现 Stub 类,并定义接口中方法的具体实现。


//实现了 AIDL 的抽象函数 private IBookManager.Stub mbinder = new IBookManager.Stub() {@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {//什么也不做}


@Overridepublic void addBook(Book book) throws RemoteException {//添加书本 if (!mBookList.contains(book)) {mBookList.add(book);}}


@Overridepublic List<Book> getBookList() throws RemoteException {return mBookList;}};


当客户端连接服务端,服务端就会调用如下方法:


public IBinder onBind(Intent intent) {return mbinder;}


就会把 Stub 实现对象返回给客户端,该对象是个 Binder 对象,可以实现进程间通信。本例就不真实模拟两个应用之间的通信,而是让 Service 另外开启一个进程来模拟进程间通信。


<serviceandroid:name=".MyService"android:process=":remote"><intent-filter><category android:name="android.intent.category.DEFAULT" /><action android:name="com.lvr.aidldemo.MyService" /></intent-filter></service>


android:process=":remote"设置为另一个进程。<action android:name="com.lvr.aidldemo.MyService"/>是为了能让其他 apk 隐式 bindService。通过隐式调用的方式来连接 service,需要把 category 设为 default,这是因为,隐式调用的时候,intent 中的 category 默认会被设置为 default。


客户端


首先将服务端工程中的 aidl 文件夹下的内容整个拷贝到客户端工程的对应位置下,由于本例的使用在一个应用中,就不需要拷贝了,其他情况一定不要忘记这一步。


客户端需要做的事情比较简单,首先需要绑定服务端的 Service。


Intent intentService = new Intent();intentService.setAction("com.lvr.aidldemo.MyService");intentService.setPackage(getPackageName());intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);MyClient.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);Toast.makeText(getApplicationContext(), "绑定了服务", Toast.LENGTH_SHORT).show();


将服务端返回的 Binder 对象转换成 AIDL 接口所属的类型,接着就可以调用 AIDL 中的方法了。


if (mIBookManager != null) {try {mIBookManager.addBook(new Book(18, "新添加的书"));Toast.makeText(getApplicationContext(), mIBookManager.getBookList().size() + "", Toast.LENGTH_SHORT).show();} catch (RemoteException e) {e.printStackTrace();}}

3.AIDL 的工作原理

Binder 机制的运行主要包括三个部分:注册服务、获取服务和使用服务。其中注册服务和获取服务的流程涉及 C 的内容,由于个人能力有限,就不予介绍了。


本篇文章主要介绍使用服务时,AIDL 的工作原理。

①.Binder 对象的获取

Binder 是实现跨进程通信的基础,那么 Binder 对象在服务端和客户端是共享的,是同一个 Binder 对象。在客户端通过 Binder 对象获取实现了 IInterface 接口的对象来调用远程服务,然后通过 Binder 来实现参数传递。


那么如何维护实现了 IInterface 接口的对象和获取 Binder 对象


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


呢?


服务端获取 Binder 对象并保存 IInterface 接口对象 Binder 中两个关键方法:


public class Binder implement IBinder {void attachInterface(IInterface plus, String descriptor)


IInterface queryLocalInterface(Stringdescriptor) //从 IBinder 中继承而来..........................}


Binder 具有被跨进程传输的能力是因为它实现了 IBinder 接口。系统会为每个实现了该接口的对象提供跨进程传输,这是系统给我们的一个很大的福利。


Binder 具有的完成特定任务的能力是通过它的 IInterface 的对象获得的,我们可以简单理解 attachInterface 方法会将(descriptor,plus)作为(key,value)对存入 Binder 对象中的一个 Map<String,IInterface>对象中,Binder 对象可通过 attachInterface 方法持有一个 IInterface 对象(即 plus)的引用,并依靠它获得完成特定任务的能力。queryLocalInterface 方法可以认为是根据 key 值(即参数 descriptor)查找相应的 IInterface 对象。


在服务端进程,通过实现private IBookManager.Stub mbinder = new IBookManager.Stub() {}抽象类,获得 Binder 对象。并保存了 IInterface 对象。


public Stub() {this.attachInterface(this, DESCRIPTOR);}


客户端获取 Binder 对象并获取 IInterface 接口对象


通过 bindService 获得 Binder 对象


MyClient.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);


然后通过 Binder 对象获得 IInterface 对象。


private ServiceConnection mServiceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {//通过服务端 onBind 方法返回的 binder 对象得到 IBookManager 的实例,得到实例就可以调用它的方法了 mIBookManager = IBookManager.Stub.asInterface(binder);}


@Overridepublic void onServiceDisconnected(ComponentName name) {mIBookManager = null;}};


其中asInterface(binder)方法如下:


public static com.lvr.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.lvr.aidldemo.IBookManager))) {return ((com.lvr.aidldemo.IBookManager) iin);}return new com.lvr.aidldemo.IBookManager.Stub.Proxy(obj);}


先通过queryLocalInterface(DESCRIPTOR);查找到对应的 IInterface 对象,然后判断对象的类型,如果是同一个进程调用则返回 IBookManager 对象,由于是跨进程调用则返回 Proxy 对象,即 Binder 类的代理对象。

②.调用服务端方法

获得了 Binder 类的代理对象,并且通过代理对象获得了 IInterface 对象,那么就可以调用接口的具体实现方法了,来实现调用服务端方法的目的。


以 addBook 方法为例,调用该方法后,客户端线程挂起,等待唤醒:


@Override public void addBook(com.lvr.aidldemo.Book book) throws android.os.RemoteException{..........//第一个参数:识别调用哪一个方法的 ID//第二个参数:Book 的序列化传入数据//第三个参数:调用方法后返回的数据//最后一个不用管 mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);_reply.readException();}..........}


省略部分主要完成对添加的 Book 对象进行序列化工作,然后调用transact方法。


Proxy 对象中的 transact 调用发生后,会引起系统的注意,系统意识到 Proxy 对象想找它的真身 Binder 对象(系统其实一直存着 Binder 和 Proxy 的对应关系)。于是系统将这个请求中的数据转发给 Binder 对象,Binder 对象将会在 onTransact 中收到 Proxy 对象传来的数据,于是它从 data 中取出客户端进程传来的数据,又根据第一个参数确定想让它执行添加书本操作,于是它就执行了响应操作,并把结果写回 reply。代码概略如下:


case TRANSACTION_addBook: {data.enforceInterface(DESCRIPTOR);com.lvr.aidldemo.Book _arg0;if ((0 != data.readInt())) {_arg0 = com.lvr.aidldemo.Book.CREATOR.createFromParcel(data);} else {_arg0 = null;}//这里调用服务端实现的 addBook 方法 this.addBook(_arg0);reply.writeNoException();return true;

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
【中级—(1),Android资深架构师分享学习经验及总结