前一篇我們從理論上來了解Android的Binder機(jī)制,本篇從實(shí)戰(zhàn)來深入了解Android的Binder機(jī)制。
Binder的使用
Binder依賴于Service,在組件(Activity)中通過bindService(),就可以獲取Service中的Binder對象,實(shí)現(xiàn)Service與Activity的通信。
服務(wù)分為本地服務(wù)和遠(yuǎn)程服務(wù),都可以使用Binder。
一:本地服務(wù)
在本地服務(wù)中使用Binder,只需要兩步:
- 聲明一個(gè)Service,重寫onBind(),返回一個(gè)繼承Binder的自定義對象;
- 聲明一個(gè)ServiceConnection,重寫onServiceConnected(),獲取IBinder對象,然后調(diào)用Activity的bindService();
聲明Service:
class ServiceWithBinder : Service() {
private val TAG = "ServiceWithBinder"
class InnerBinder : Binder() {
@Throws(RemoteException::class)
fun multiply(x: Int, y: Int): Int {
return x * y
}
@Throws(RemoteException::class)
fun divide(x: Int, y: Int): Int {
return x / y
}
}
override fun onBind(t: Intent): IBinder? {
Log.e(TAG, "onBind")
return InnerBinder()
}
}
在Activity中綁定服務(wù):
fun myBindService() {
val mServiceConnBinder = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) {
Log.e(TAG, "onServiceDisconnected Binder")
binderInterface = null
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
Log.e(TAG, "onServiceConnected Binder")
binderInterface = service as ServiceWithBinder.InnerBinder
//調(diào)用服務(wù)的方法
binderInterface?.multiply(3,2)
}
}
val intentBinder = Intent(this, ServiceWithBinder::class.java)
//此時(shí)使用bindService開啟服務(wù)
bindService(intentBinder, mServiceConnBinder, Context.BIND_AUTO_CREATE)
//銷毀服務(wù)
unbindService(mServiceConnBinder)
}
二:遠(yuǎn)程服務(wù)
在遠(yuǎn)程服務(wù)中使用Binder,需要三步:創(chuàng)建aidl、聲明Service、調(diào)用bindService。
1.創(chuàng)建aidl
aidl是進(jìn)程間通信接口,需要在客戶端(聲明Service的應(yīng)用)和服務(wù)端(調(diào)用Service的應(yīng)用)同時(shí)聲明,并且要完全一致。
首先,在服務(wù)端端創(chuàng)建aidl:選中項(xiàng)目目錄->右鍵選中new->然后選中AIDL->填寫文件名(比如:ICalcAIDL)->修改aidl的代碼。
// ICalcAIDL.aidl
package com.pmm.demo.advanced;
// Declare any non-default types here with import statements
interface ICalcAIDL {
int add(int x , int y);
int min(int x , int y );
}
然后,在客戶端創(chuàng)建aidl,步驟同上??蛻舳说腶idl要與服務(wù)端的一模一樣,包括包名、類名、方法。
然后,選中Build->Make Project,編譯整個(gè)工程,就會在build/generated/source/aidl/debug目錄下生產(chǎn)一個(gè)名為ICalcAIDL的接口。
2.聲明Service
在服務(wù)端,聲明一個(gè)類(比如:MyStub)繼承ICalcAIDL.Stub(ICalcAIDL的代理類,系統(tǒng)幫我們生成的),然后聲明一個(gè)繼承Service的類,在onBind()中返回MyStub對象。
class ServiceWithAIDL : Service() {
private val TAG = "ServiceWithAIDL"
private val mBinder = object : ICalcAIDL.Stub() {
@Throws(RemoteException::class)
override fun add(x: Int, y: Int): Int {
return x + y
}
@Throws(RemoteException::class)
override fun min(x: Int, y: Int): Int {
return x - y
}
}
override fun onBind(t: Intent): IBinder? {
Log.e(TAG, "onBind")
return mBinder
}
}
3.調(diào)用bindService
在客戶端,聲明一個(gè)匿名內(nèi)部類ServiceConnection,重寫onServiceConnected(),通過ICalcAIDL.Stub.asInterface(iBinder)獲取服務(wù)端的ICalcAIDL對象。
fun myBindService() {
val mServiceConnAIDL = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) {
Log.e(TAG, "onServiceDisconnected AIDL")
mCalcAidl = null
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
Log.e(TAG, "onServiceConnected AIDL")
mCalcAidl = ICalcAIDL.Stub.asInterface(service)
//調(diào)用服務(wù)的方法
mCalcAidl?.add(3, 2)
}
}
val intentBinder = Intent(this, ServiceWithAIDL::class.java)
//此時(shí)使用bindService開啟服務(wù)
bindService(intentBinder, mServiceConnAIDL, Context.BIND_AUTO_CREATE)
//銷毀服務(wù)
unbindService(mServiceConnAIDL)
}
AIDL機(jī)制
aidl是進(jìn)程間通信接口,它不是Java的類,更像一種協(xié)議。它的作用是讓AS自動生成Binder類,供客戶端調(diào)用。
aidl文件編譯后,會生成一個(gè)Java接口,分為3層:ICalcAIDL、Stub、Proxy。
package com.pmm.demo.advanced;
public interface ICalcAIDL extends android.os.IInterface {
//具體的Binder類
public static abstract class Stub extends android.os.Binder implements com.pmm.demo.advanced.ICalcAIDL {
·······
//代理類
private static class Proxy implements com.pmm.demo.advanced.ICalcAIDL {
·······
}
}
//加
public int add(int x, int y) throws android.os.RemoteException;
//減
public int min(int x, int y) throws android.os.RemoteException;
}
下面是生成類ICalcAIDL的結(jié)構(gòu)圖

1.ICalcAIDL
ICalcAIDL就是aidl文件中聲明的接口,也就是我們要給客戶端調(diào)用的功能。
2.Sub
Stub是一個(gè)使用ICalcAIDL裝飾的Binder類,是我們實(shí)際傳遞給客戶端的對象。通過Stub,客戶端就可以調(diào)用ICalcAIDL的方法。
首先是DESCRIPTOR,默認(rèn)值是包名+類名,作用是在IBinder中查找ICalcAIDL接口。
private static final java.lang.String DESCRIPTOR = "com.pmm.demo.advanced.ICalcAIDL";
然后是asInterface(),作用是返回Proxy對象,也就是把ICalcAIDL對象返回給客戶端。我們注意到一般obj都是對應(yīng)服務(wù)的指引,然后傳遞給Proxy對象。
public static com.pmm.demo.advanced.ICalcAIDL asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.pmm.demo.advanced.ICalcAIDL))) {
return ((com.pmm.demo.advanced.ICalcAIDL) iin);
}
//返回Proxy對象
return new com.pmm.demo.advanced.ICalcAIDL.Stub.Proxy(obj);
}
然后是onTransact(),作用是處理客戶端的請求,并將處理結(jié)果返回給客戶端。
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
//用來指定DESCRIPTOR,從而確定需要通訊的接口
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
//用來處理ICalcAIDL的add()
case TRANSACTION_add: {
//說明data正準(zhǔn)備給指定DESCRIPTOR的接口使用
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
//說明當(dāng)前操作沒有出現(xiàn)異常
reply.writeNoException();
reply.writeInt(_result);
return true;
}
//用來處理ICalcAIDL的min()
case TRANSACTION_min: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.min(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
3.Proxy
Proxy,顧名思義,就是ICalcAIDL的代理類,用來實(shí)現(xiàn)ICalcAIDL的方法。Proxy的作用是將需要傳遞的參數(shù)轉(zhuǎn)化為Parcel,從而跨進(jìn)程傳輸。
我們來看下核心代碼,只顯示add(),不顯示min方法。
private static class Proxy implements com.pmm.demo.advanced.ICalcAIDL {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public int add(int x, int y) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
//用來標(biāo)識_data是給含有DESCRIPTOR標(biāo)志的Binder接口的參數(shù)
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(x);
_data.writeInt(y);
//調(diào)用Stub的transact,處理add請求
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
//獲取Stub的處理結(jié)果
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
我們可以看到,前面asInterface(obj)傳遞進(jìn)來的對象,是我們代理里真正的執(zhí)行對象。比如:mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
AIDL其實(shí)通過我們寫的aidl文件,幫助我們生成了一個(gè)接口,一個(gè)Stub類用于服務(wù)端,一個(gè)Proxy類用于客戶端調(diào)用。
客戶端使用AIDL接口的asInterface()獲取對應(yīng)的代理,代理調(diào)用對應(yīng)的方法,方法里都有mRemote.transact()進(jìn)行具體的調(diào)用發(fā)消息給服務(wù)器,最后服務(wù)端對應(yīng)Binder對象中的onTransact()來處理客戶端的請求,并將處理結(jié)果返回給客戶端。
PS:本文
整理自以下博客
安卓移動架構(gòu)07-Binder核心機(jī)
Android aidl Binder框架淺析
若有發(fā)現(xiàn)問題請致郵 caoyanglee92@gmail.com