AIDL:Android Interface Definition Language,即 Android 接口定义语言。java
Android 系统中的进程之间不能共享内存,所以,须要提供一些机制在不一样进程之间进行数据通讯。 linux
为了使其余的应用程序也能够访问本应用程序提供的服务,Android 系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与不少其余的基于 RPC 的解决方案同样,Android 使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。咱们知道 Android 四大组件中的 3 种(Activity、BroadcastReceiver和ContentProvider)均可以进行跨进程访问,另一种 Android 组件 Service 一样能够。所以,能够将这种能够跨进程访问的服务称为 AIDL(Android Interface Definition Language)服务。android
在介绍 AIDL 的使用以及其它特性前,咱们先来了解下 AIDL 的核心——Binder。服务器
看过一些关于 Binder 的文章,总得来讲 Binder 机制的底层实现很复杂,至关复杂,要彻底搞清楚,得花大量的时间。从某种角度来讲,我的以为,对于 Binder,咱们只须要了解其上层原理以及使用方法便可。app
直观来看,从代码的角度来讲,Binder 是 Android 系统源码中的一个类,它实现了 IBinder 接口;从 IPC 角度来讲,Binder 是 Android 中的一种跨进程通讯方式;从 Android Framework 角度来说,Binder 是 ServiceManager 链接各类 Manager(ActivityManager、WindowManager 等等)和相应 ManagerService 的桥梁;从 Android 应用层来讲,Binder 是客户端和服务端进行通讯的媒介,当 bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象,经过这个 Binder 对象,客户端就能够和服务端进行通讯,这里的服务包括普通服务和基于 AIDL 的服务。异步
接下来,咱们经过一个 AIDL 示例,来分析 Binder 的工做机制。在工程目录中新建一个名为 aidl 的 package,而后新建 Book.Java、Book.aidl(建立此文件时,as 会提示已存在,须要先用其它命令,建立成功后再重命名为 Book.aidl )和 IBookManager.aidl,代码以下:ide
// Book.java package com.cy.ipcsample.aidl; import android.os.Parcel; import android.os.Parcelable; /** * 数据类 * @author cspecialy * @version v1.0.0 * @date 2018/5/14 21:38 */ public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } protected Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(bookId); parcel.writeString(bookName); } @Override public String toString() { return "Book{" + "bookId=" + bookId + ", bookName='" + bookName + '\'' + '}'; } }
// Book.aidl package com.cy.ipcsample.aidl; parcelable Book;
// IBookManager.aidl package com.cy.ipcsample.aidl; import com.cy.ipcsample.aidl.Book; import com.cy.ipcsample.aidl.IOnNewBookArrivedListener; interface IBookManager { List<Book> getBookList(); void addBook(in Book book); void registerListener(IOnNewBookArrivedListener listener); void unRegisterListener(IOnNewBookArrivedListener listener); }
建立好三个文件以后,编译一下,as 会在 app/build/generated/source/aidl/debug
目录下的 com.cy.ipcsample 包中生成一个名为 IBookManager.Java 的类,以下图所示:函数
这是系统生成的 Binder 类,接下来咱们要利用这个类来分析 Binder 的工做原理。其代码以下:(生成的代码格式很乱,能够格式化代码以后看)ui
/* * This file is auto-generated. DO NOT MODIFY. * Original file: G:\\Android\\Github\\Bugly-Android-Demo\\sample\\ipcsample\\src\\main\\aidl\\com\\cy\\ipcsample * \\aidl\\IBookManager.aidl */ package com.cy.ipcsample.aidl; public interface IBookManager extends android.os.IInterface { public java.util.List<com.cy.ipcsample.aidl.Book> getBookList() throws android.os.RemoteException; public void addBook(com.cy.ipcsample.aidl.Book book) throws android.os.RemoteException; /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.cy.ipcsample.aidl.IBookManager { static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); private static final java.lang.String DESCRIPTOR = "com.cy.ipcsample.aidl.IBookManager"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.cy.ipcsample.aidl.IBookManager interface, * generating a proxy if needed. */ public static com.cy.ipcsample.aidl.IBookManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.cy.ipcsample.aidl.IBookManager))) { return ((com.cy.ipcsample.aidl.IBookManager) iin); } return new com.cy.ipcsample.aidl.IBookManager.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.cy.ipcsample.aidl.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.cy.ipcsample.aidl.Book _arg0; if ((0 != data.readInt())) { _arg0 = com.cy.ipcsample.aidl.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.cy.ipcsample.aidl.IBookManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public android.os.IBinder asBinder() { return mRemote; } @Override public java.util.List<com.cy.ipcsample.aidl.Book> getBookList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.cy.ipcsample.aidl.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.cy.ipcsample.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(com.cy.ipcsample.aidl.Book book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); 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(); } finally { _reply.recycle(); _data.recycle(); } } } } }
可见,系统为咱们生成了一个 IBookManager 接口,它继承了 IInterface 这个接口,因此这里要注意下,全部能够在 Binder 中传输的接口,都须要继承 IInterface 接口。this
接下来分析下,该类的工做机制。仔细看,能够发现,该类主要分红三个部分:
第一部分咱们不用管它,主要看 Stub 类和 Proxy 类。在 Stub 中,首先声明了两个用于标识 IBookManager 方法的整型变量,这两个变量用于标识在 transact 过程当中客户端所请求的是哪一个方法。接着,是 asInterface 方法,该方法用于将服务端的 Binder 对象转换成客户端所需的 AIDL 接口类型的对象,该方法经过调用 Binder 的 queryLocalInterface 方法,判断客户端和服务端是否处于同一进程,若是客户端、服务端处于同一进程,那么此方法直接返回服务端的 Stub,不然,返回 Stub 的代理对象 Proxy。queryLocalInterface 实现以下:
/** * Use information supplied to attachInterface() to return the * associated IInterface if it matches the requested * descriptor. */ public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) { if (mDescriptor.equals(descriptor)) { return mOwner; } return null; }
mOwner 是在 Stub 构造函数中传进去的 this 参数。
接下来是 Stub 的代理类 Stub.Proxy,由上面的分析可知,Stub.Proxy 是运行在客户端的(由 asInterface 方法返回给客户端的对象),Stub.Proxy对象建立后,持有服务端的 Binder 对象,用于客户端请求时调用服务端方法进行远程调用。客户端在向服务端发起请求时,调用 Stub.Proxy 的相应方法,Stub.Proxy 方法的流程以下:
以上,就是系统生成的 IBookManager 的工做过程,须要注意下,服务端的 onTransact 方法是运行在 Binder 线程池中的。因为 IBookManager.Stub 类继承 Binder,因此上述分析即 Binder 的工做机制,简单总结下:
或者参考下图理解下:
因而可知,Binder 在 AIDL 中承载着重要的职能,是 AIDL 的核心,理解了 Binder 的工做机制,其实在不少方面都颇有用。
一套 AIDL 服务搭建的步骤以下:
接下来,咱们使用上面的 IBookManager 来实现 AIDL。
直接使用上面建立好的 Book.aidl、IBookManager.aidl 文件便可
建立一个 Service,命名为 BookManagerService,代码以下:
package com.cy.ipcsample.aidl import android.app.Service import android.content.Intent import android.os.IBinder import android.os.RemoteCallbackList import android.util.Log import java.util.concurrent.CopyOnWriteArrayList class BookManagerService : Service() { private val TAG = "BookManagerService" private val mBookList = CopyOnWriteArrayList<Book>() private val mListenerList = RemoteCallbackList<IOnNewBookArrivedListener>() /** * 实现 AIDL 接口的 Binder 对象,客户端绑定服务端时,直接返回此 Binder 对象 */ private val mBinder = object : IBookManager.Stub() { override fun getBookList(): MutableList<Book> { return mBookList } override fun addBook(book: Book?) { mBookList.add(book) } } override fun onBind(intent: Intent): IBinder { return mBinder } override fun onCreate() { super.onCreate() // 建立两本图书 mBookList.add(Book(1, "Android")) mBookList.add(Book(2, "iOS")) } } 而后在 AndroidManifest 中注册 Service,注意启动多进程: <service android:name=".aidl.BookManagerService" android:process="com.cy.ipcsample.bookManagerService"> </service>
客户端的建立,直接使用 Activity 便可,绑定远程服务的代码以下:
private val mConnection = object : ServiceConnection { override fun onServiceDisconnected(name: ComponentName?) { } /** * 链接远程服务成功的回调 */ override fun onServiceConnected(name: ComponentName?, service: IBinder?) { } } // 绑定远程服务 bindService(Intent(this, BookManagerService::class.java), mConnection, Context.BIND_AUTO_CREATE)
与服务器绑定成功后,首先在 ServiceConnection 的回调中,将服务端返回的 Binder 对象转换成 AIDL 接口所属的对象,就能够调用相应方法和服务端通讯了,代码以下:
// 将服务端返回的 Binder 对象转换成 IBookManager 对象 val bookManager = IBookManager.Stub.asInterface(service) // 与服务端通讯 try { // 获取图书列表 val list = bookManager.bookList Log.i(TAG, "query book list, list type: ${list.javaClass.canonicalName}") Log.i(TAG, "query book list: $list") // 添加一本图书 val book = Book(3, "Android开发艺术探索") bookManager.addBook(book) Log.i(TAG, "add book: $book") // 获取图书列表 val newList = bookManager.bookList Log.i(TAG, "query book list: $newList") } catch (e: RemoteException) { e.printStackTrace() }
代码中,咱们先查询了服务端的图书列表,接着向服务端添加一本书Android艺术开发探索
,而后再次查询看是否添加成功。运行下看 log,以下图所示:
可见,运行结果和预期结果一致。完整的客户端代码以下:
package com.cy.ipcsample.aidl import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.os.Bundle import android.os.IBinder import android.os.RemoteException import android.support.v7.app.AppCompatActivity import android.util.Log import com.cy.ipcsample.R class BookManagerActivity : AppCompatActivity() { private val TAG = "BookManagerActivity" private val mConnection = object : ServiceConnection { override fun onServiceDisconnected(name: ComponentName?) { Log.d(TAG, "binder died.") } /** * 链接远程服务成功的回调 */ override fun onServiceConnected(name: ComponentName?, service: IBinder?) { // 将服务端返回的 Binder 对象转换成 IBookManager 对象 val bookManager = IBookManager.Stub.asInterface(service) // 与服务端通讯 try { // 获取图书列表 val list = bookManager.bookList Log.i(TAG, "query book list, list type: ${list.javaClass.canonicalName}") Log.i(TAG, "query book list: $list") // 添加一本图书 val book = Book(3, "Android开发艺术探索") bookManager.addBook(book) Log.i(TAG, "add book: $book") // 获取图书列表 val newList = bookManager.bookList Log.i(TAG, "query book list: $newList") } catch (e: RemoteException) { e.printStackTrace() } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_book_manager) // 绑定远程服务 bindService(Intent(this, BookManagerService::class.java), mConnection, Context.BIND_AUTO_CREATE) } override fun onDestroy() { unbindService(mConnection) super.onDestroy() } }
到此,一个简单的 AIDL 示例就完成了,固然,AIDL 的使用远没有那么简单,还有不少情景须要考虑的,好比:客户端须要随时服务端在状态变化时同时客户端,相似观察这模式,那么订阅与反订阅怎么实现;Binder 意外死亡,怎么重连等等,更多内容,能够参考《Android艺术开发探索》,电子书下载
AIDL 的调用过程是同步仍是异步的?
这个问题其实看这一节的标题你们都知道了,AIDL 的调用过程是同步的。同时,上面分析 Binder 的机制时,也提到了,客户端进行远程 RPC 请求时,线程会挂起,等待结果,由此也可知,AIDL 的调用过程是同步的,下面来验证下。
首先,在服务端的 BookManagerService 中实现的 Binder 对象的 getBookList 方法添加延时执行,以下图所示:
而后,在客户端的 BookManagerActivity 中添加一个按钮,点击按钮时调用服务端 Binder 的 getBookList 作 RPC 请求。代码以下:
运行后,连续点击按钮,结果以下图所示:
由图所知,连续点击按钮事后一段时间,出现了无响应错误( ANR ),由此可知,客户端向服务端作 RPC 请求时,是同步的,也就是说:AIDL 的调用过程是同步的。
AIDL 的调用过程是同步的,当咱们须要服务端作耗时操做时,确定是不能使用同步调用的,不然轻者影响用户体验,重者直接 ANR 或者应用崩溃。那么如何使 AIDL 的调用过程是异步的呢?
其实也很简单,只须要把调用放到非 UI 线程便可,若是要对调用的返回作 UI 更新的话,再经过 Handler 处理便可。以下图所示: