上一個(gè)項(xiàng)目(下載中心)使用到了AIDL相關(guān)的技術(shù)沈矿,趁現(xiàn)在項(xiàng)目不是特別忙谷异,總結(jié)一下庸疾。
首先第一個(gè)問題轿腺,AIDL是個(gè)什么两嘴?它的全稱叫 Android Interface Definition Language,中文叫做安卓接口定義語言族壳,這里面有兩個(gè)關(guān)鍵字憔辫,“Interface”和“Language",從這兩個(gè)關(guān)鍵字來看它是一門用于定義接口的語言仿荆,既然是語言那自然就有它的語法與規(guī)則贰您,但是本著先實(shí)現(xiàn)一個(gè)例子再回過頭來學(xué)習(xí)語法的原則,下一篇文章再詳細(xì)說明AIDL的語法拢操。坦率講锦亦,即使你不了解AIDL語法,基本上也能看懂令境,因?yàn)樗cJava非常相似杠园。下面通過一個(gè)例子來展示如何通過AIDL來實(shí)現(xiàn)跨進(jìn)程通信(IPC)。
假設(shè)這樣一個(gè)場景:有一個(gè)DownloadCenter展父,它可以向外提供下載服務(wù)返劲,其他App有下載需求的話就可以通過它提供的服務(wù)來完成下載,這種方式非常類似于C/S模型栖茉。很明顯DownloadCenter和其他App不是在同一個(gè)進(jìn)程中篮绿,不能直接調(diào)用,就需要通過IPC機(jī)制來完成吕漂,而Android下的IPC方式有很多亲配,比如:通過Inten傳遞數(shù)據(jù)、文件共享惶凝、Messenger吼虎、ContentProvider、aidl苍鲜、Socket等思灰,根據(jù)業(yè)務(wù)需求,選用aidl混滔。
一.先寫一個(gè)例子
我們通常把提供服務(wù)的一方叫做服務(wù)端洒疚,請求服務(wù)的一方叫做客戶端歹颓,這里就把服務(wù)端命名為 DownloadCenter,客戶端就叫 Client油湖,接下來就開始實(shí)現(xiàn)這個(gè)例子巍扛。
(1) 創(chuàng)建一個(gè)普通的Android工程DownloadCenter,接著創(chuàng)建一個(gè)包 com.jdqm.downloadcenter.aidl乏德,在這個(gè)包下創(chuàng)建一個(gè)DownloadTask類撤奸。
public class DownloadTask implements Parcelable{
private int id;
private String url;
public DownloadTask() {
}
public DownloadTask(int id, String url) {
this.id = id;
this.url = url;
}
protected DownloadTask(Parcel in) {
id = in.readInt();
url = in.readString();
}
public static final Creator<DownloadTask> CREATOR = new Creator<DownloadTask>() {
@Override
public DownloadTask createFromParcel(Parcel in) {
return new DownloadTask(in);
}
@Override
public DownloadTask[] newArray(int size) {
return new DownloadTask[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(url);
}
public void readFromParcel(Parcel in) {
id = in.readInt();
url = in.readString();
}
//重寫toString方法方便打印
@Override
public String toString() {
return "DownloadTask{" +
"id=" + id +
", url='" + url + '\'' +
'}';
}
}
這個(gè)類代碼看起來很多,其實(shí)就定義了兩個(gè)成員變量id和url喊括,并且實(shí)現(xiàn)Parcelable接口胧瓜,其他的代碼都是AS生成的。為什么要實(shí)現(xiàn)Parcelable接口郑什?這是因?yàn)樵诳邕M(jìn)程通信過程中贷痪,涉及到對象數(shù)據(jù)的序列化和反序列化。關(guān)于Android中實(shí)現(xiàn)對象的序列化通潮奈螅可以實(shí)現(xiàn)Parcelable或者Serializable接口劫拢,為了更好的理解建議還是先熟悉對象的序列化和反序列化相關(guān)的知識(shí),這里就不再深入了强胰。
插播一個(gè)場景:當(dāng)初在學(xué)對象的序列化和反序列化的時(shí)候萌生了這樣一個(gè)幻想舱沧,如今春節(jié)車票真的是一票難求,想回家的你費(fèi)盡了心思也沒買到一張二等座偶洋。假設(shè)有這么一個(gè)設(shè)備能把一個(gè)人(對象)序列化熟吏,然后通過網(wǎng)絡(luò)傳輸?shù)郊依铮俜葱蛄谢鰜硇眩@該是多么美好的一件事牵寺!
(2) 在(1)中的包名右鍵,New->AIDL->AIDL File 創(chuàng)建一個(gè)aild文件 DownloadTask.aidl
// DownloadTask.aidl
package com.jdqm.downloadcenter.aidl;
parcelable DownloadTask;
當(dāng)你在(1)中的包名右鍵新建一個(gè)AIDL文件時(shí)恩脂,項(xiàng)目的目錄結(jié)構(gòu)中main下面多了一個(gè)aidl的文件夾帽氓,并且會(huì)幫你創(chuàng)建一個(gè)與你右鍵的地方相同的包名。緊接著在相同的包名的創(chuàng)建IDownloadCenter.aidl
// IDownloadCenter.aidl
package com.jdqm.downloadcenter.aidl;
import com.jdqm.downloadcenter.aidl.DownloadTask;
interface IDownloadCenter {
//添加下載任務(wù)
void addDownloadTask(in DowloadTask task);
//查詢所有的添加的下載任務(wù)
List<DownloadTask> getDownloadTask();
}
到這里俩块,你需要Build一下項(xiàng)目黎休,一來是檢查有沒有錯(cuò)誤,更重要的是讓Android SDK Tool根據(jù)aidl文件生成對應(yīng)的Java文件玉凯,如果Build成功势腮,那么會(huì)在app/build/generated/source/aidl/debug下生成IDownloadCenter.java這個(gè)文件(切換到Project視圖,或者直接double shift搜索更快)漫仆。
(3) 創(chuàng)建服務(wù) DownloadCenterService
public class DownloadCenterService extends Service {
private List<DownloadTask> tasks;
private DownloadCenter downloadCenter;
@Override
public void onCreate() {
super.onCreate();
//由于是在Binder線程池中訪問這個(gè)集合捎拯,所以有必要好線程同步。除非你能確保并發(fā)情況下不會(huì)出現(xiàn)問題
tasks = Collections.synchronizedList(new ArrayList<DownloadTask>());
downloadCenter = new DownloadCenter();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return downloadCenter;
}
/**
* 完成aidl文件編寫后盲厌,下次Build時(shí)Android SDK tools會(huì)根據(jù)IDownloadCenter.aidl文件生成
* IDownloadCenter.java文件署照,Stub是它的內(nèi)部類
*/
private class DownloadCenter extends IDownloadCenter.Stub {
@Override
public void addDownloadTask(DownloadTask task) throws RemoteException {
tasks.add(task);
}
@Override
public List<DownloadTask> getDownloadTask() throws RemoteException {
return tasks;
}
}
}
這個(gè)Service其實(shí)也很簡單座菠,首先創(chuàng)建一個(gè)IDownloadCenter.Stub的實(shí)現(xiàn)類DownloadCenter,并且實(shí)現(xiàn)其內(nèi)部的兩個(gè)抽象方法(可以看到這兩個(gè)方法就是在aidl文件中聲明的方法)藤树,然后在onBind()方法將其返回它的一個(gè)實(shí)例。
(4) 最后別忘了在AndroidManifest.xml文件中注冊這個(gè)Service拓萌,另外為了讓其他應(yīng)用通過隱式Intent啟動(dòng)岁钓,需要給這個(gè)Service添加一個(gè)intent-filter
<service android:name=".DownloadCenterService">
<intent-filter>
<action android:name="jdqm.intent.action.LAUNCH"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
接下來是客戶端Client的實(shí)現(xiàn):
(1) 首先創(chuàng)建一個(gè)名為Client的Android工程,然后將服務(wù)端的所有aidl文件以及aidl文件中使用到的Java類拷貝到Cilent中微王,注意包名結(jié)構(gòu)也要和服務(wù)端一致屡限,如果不一致在反序列化的時(shí)候就會(huì)出錯(cuò)。
(2) 通過bindService與服務(wù)端建立連接炕倘,完成服務(wù)方法調(diào)用
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private Button btnAddTask;
private Button btnGetTasks;
private DownloadServiceConn serviceConn;
private IDownloadCenter downloadCenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
serviceConn = new DownloadServiceConn();
Intent intent = new Intent("jdqm.intent.action.LAUNCH");
intent.setPackage("com.jdqm.downloadcenter");
bindService(intent, serviceConn, BIND_AUTO_CREATE);
}
private void initViews() {
btnAddTask = findViewById(R.id.btnAddTask);
btnGetTasks = findViewById(R.id.btnGetTasks);
btnAddTask.setOnClickListener(this);
btnGetTasks.setOnClickListener(this);
btnAddTask.setEnabled(false);
btnGetTasks.setEnabled(false);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnAddTask:
try {
Random random = new Random();
int id = random.nextInt(10);
DownloadTask task = new DownloadTask(id, "http://test.jdqm.com/test.aidl");
//在客戶端直接調(diào)用IBinder接口的方法钧大,最終服務(wù)端對應(yīng)的方法會(huì)調(diào)用,這似乎是在同一個(gè)進(jìn)程中一般
//這是因?yàn)锽inder機(jī)制已經(jīng)把底層的實(shí)現(xiàn)隱藏掉了
downloadCenter.addDownloadTask(task);
Log.d(TAG, "添加任務(wù)成功: " + task);
} catch (RemoteException e) {
e.printStackTrace();
Log.e(TAG, "添加任務(wù)失敗");
}
break;
case R.id.btnGetTasks:
try {
List<DownloadTask> tasks = downloadCenter.getDownloadTask();
Log.d(TAG, "從服務(wù)中獲取到已經(jīng)添加的任務(wù)列表: " + tasks);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
private class DownloadServiceConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "服務(wù)已連接");
//將綁定服務(wù)的返回的IBinder對象轉(zhuǎn)換為目標(biāo)接口類型
downloadCenter = IDownloadCenter.Stub.asInterface(service);
btnAddTask.setEnabled(true);
btnGetTasks.setEnabled(true);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "服務(wù)已斷開");
btnAddTask.setEnabled(false);
btnGetTasks.setEnabled(false);
}
}
@Override
protected void onDestroy() {
unbindService(serviceConn);
super.onDestroy();
}
}
以上是完整的客戶端實(shí)現(xiàn)罩旋,首先bindService啊央,通過ServiceConnection完成綁定時(shí)的回調(diào)方法onServiceConnected拿到服務(wù)端返回的IBinder對象,通過IDownloadCenter.Stub.asInterface()方法將IBinder轉(zhuǎn)換為目標(biāo)IBinder類型IDownloadCenter涨醋,通過它來完成IDownloadCenter.Stub實(shí)現(xiàn)類方法的調(diào)用瓜饥。
現(xiàn)在將DownloadCenter和Client安裝到同一個(gè)Android設(shè)備中,打開客戶端浴骂,點(diǎn)擊界面里的按鈕乓土,下面是輸出的log信息:
/com.jdqm.client D/MainActivity: 服務(wù)已連接
/com.jdqm.client D/MainActivity: 添加任務(wù)成功: DownloadTask{id=6, url='http://test.jdqm.com/test.aidl'}
/com.jdqm.client D/MainActivity: 從服務(wù)中獲取到已經(jīng)添加的任務(wù)列表: [DownloadTask{id=6, url='http://test.jdqm.com/test.aidl'}]
/com.jdqm.client D/MainActivity: 添加任務(wù)成功: DownloadTask{id=5, url='http://test.jdqm.com/test.aidl'}
/com.jdqm.client D/MainActivity: 從服務(wù)中獲取到已經(jīng)添加的任務(wù)列表: [DownloadTask{id=6, url='http://test.jdqm.com/test.aidl'}, DownloadTask{id=5, url='http://test.jdqm.com/test.aidl'}]
到這里,我們完成了客戶端與服務(wù)端的跨進(jìn)程通信溯警。雖然沒有像唐僧西天取經(jīng)那樣歷經(jīng)九九81難(何苦呢)趣苏,但也可以回頭看看我們走過路了。
二.回頭看看
先來總結(jié)一下步驟:
- 在src/main/aidl/下創(chuàng)建了兩個(gè) aidl文件:DownloadTask.aidl梯轻、IDownloadCenter.aidl;
- 創(chuàng)建一個(gè)IDownloadCenter.Stub實(shí)現(xiàn)類食磕,并在onBind中返回一個(gè)實(shí)例: DownloadCenter extends IDownloadCenter.Stub;
- 在客戶端實(shí)現(xiàn)一個(gè)ServiceConnection;
- Context.bindService(), 在回調(diào)onServiceConnected()中接收IBinder實(shí)例, 并且調(diào)用IDownloadCenter.Stub.asInterface(service)將返回值轉(zhuǎn)換為IDownloadCenter類型喳挑。
- 調(diào)用IDownloadCenter接口中定義的方法來完成跨進(jìn)程調(diào)用芬为;
- 調(diào)用Context.unbindService()斷開連接。
粗略地總結(jié)好像并沒什么意思蟀悦,畢竟大多數(shù)真諦是隱藏在細(xì)節(jié)中的媚朦。就好比讓你回憶初戀的過程,可能你更想回憶起接吻時(shí)的感覺日戈! 首先從IDownloadCenter.aidl生成的IDownloadCenter.java開始询张。
public interface IDownloadCenter extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.jdqm.downloadcenter.aidl.IDownloadCenter {
//省略器內(nèi)部實(shí)現(xiàn)
}
//添加下載任務(wù)
public void addDownloadTask(com.jdqm.downloadcenter.aidl.DownloadTask task) throws android.os.RemoteException;
//查詢所有的添加的下載任務(wù)
public java.util.List<com.jdqm.downloadcenter.aidl.DownloadTask> getDownloadTask() throws android.os.RemoteException;
}
打開這個(gè)文件代碼看起來是真的有點(diǎn)多(130多行),而且格式不太好閱讀浙炼,所以最好是先格式化一下份氧。所幸的是它的結(jié)構(gòu)很清晰唯袄,首先它是一個(gè)接口,內(nèi)部定義了兩個(gè)接口方法和一個(gè)內(nèi)部類Stub蜗帜,顯然這兩個(gè)方法就是在aidl文件中定義的方法恋拷,連注釋都給你搬過來了,可見這個(gè)生成工具是如此地用心厅缺。重點(diǎn)就是這個(gè)內(nèi)部類Stub蔬顾,它是一個(gè)抽象類,并且實(shí)現(xiàn)了IDownloadCenter接口湘捎,但它并沒有實(shí)現(xiàn)接口中的兩個(gè)方法诀豁。還記得在Service中IDownloadCenter.Stub的實(shí)現(xiàn)類嗎?
private class DownloadCenter extends IDownloadCenter.Stub {
@Override
public void addDownloadTask(DownloadTask task) throws RemoteException {
tasks.add(task);
}
@Override
public List<DownloadTask> getDownloadTask() throws RemoteException {
return tasks;
}
}
接著我們在Service的onBind方法返回了它的一個(gè)實(shí)例窥妇,創(chuàng)建這個(gè)Binder的過程做了什么舷胜?那就得窺探窺探Stub的真容了。
public static abstract class Stub extends android.os.Binder implements com.jdqm.downloadcenter.aidl.IDownloadCenter {
private static final java.lang.String DESCRIPTOR = "com.jdqm.downloadcenter.aidl.IDownloadCenter";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.jdqm.downloadcenter.aidl.IDownloadCenter asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.jdqm.downloadcenter.aidl.IDownloadCenter))) {
return ((com.jdqm.downloadcenter.aidl.IDownloadCenter) iin);
}
return new com.jdqm.downloadcenter.aidl.IDownloadCenter.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 {
//省略內(nèi)部實(shí)現(xiàn)
}
private static class Proxy implements com.jdqm.downloadcenter.aidl.IDownloadCenter {
//省略內(nèi)部實(shí)現(xiàn)
}
static final int TRANSACTION_addDownloadTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getDownloadTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
可以看到活翩,在Stub的構(gòu)造方法中調(diào)了this.attachInterface(this, DESCRIPTOR)方法烹骨,這個(gè)DESCRIPTOR就是IDownloadCenter的完整名稱。
Binder#attachInterface
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
可以看到把當(dāng)前對象保存到Binder的mOwner材泄,將接口的完整名稱保存到Binder的mDescriptor展氓。至此onBind返回了這個(gè)對象,交由底層的Binder機(jī)制來處理脸爱,底層原理之復(fù)雜度猶如乾坤大挪移一般深噢遇汞,將邏輯轉(zhuǎn)到了客戶端。
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "服務(wù)已連接" );
Log.d(TAG, "ComponentName: " + name.getClassName()); //com.jdqm.downloadcenter.DownloadCenterService
Log.d(TAG, "service: " + service.getClass().getName()); //android.os.BinderProxy
//將綁定服務(wù)的返回的IBinder對象轉(zhuǎn)換為目標(biāo)接口類型
downloadCenter = IDownloadCenter.Stub.asInterface(service);
btnAddTask.setEnabled(true);
btnGetTasks.setEnabled(true);
}
這個(gè)方法是在客戶端與服務(wù)端建立連接完成時(shí)回調(diào)簿废,這個(gè)service到底是什么空入,是Service中onBind返回的DownloadCenter嗎?通過 service.getClass().getName()發(fā)現(xiàn)它是一個(gè)BinderProxy族檬,接著調(diào)用IDownloadCenter.Stub.asInterface(service)歪赢,它的實(shí)現(xiàn)如下:
public static com.jdqm.downloadcenter.aidl.IDownloadCenter asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//這個(gè)obj是BinderProxy
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); //返回null
if (((iin != null) && (iin instanceof com.jdqm.downloadcenter.aidl.IDownloadCenter))) {
return ((com.jdqm.downloadcenter.aidl.IDownloadCenter) iin);
}
return new com.jdqm.downloadcenter.aidl.IDownloadCenter.Stub.Proxy(obj);
}
首先通過調(diào)用了BinderProxy.queryLocalInterface
final class BinderProxy implements IBinder {
public IInterface queryLocalInterface(String descriptor) {
return null;
}
}
它的返回值是null,即 iin 為null单料,所以 IDownloadCenter.Stub.asInterface(service)得到的是IDownloadCenter.Stub.Proxy的實(shí)例埋凯。
通過asInterface方法可以得出一個(gè)結(jié)論:
- 如果服務(wù)端與客戶端在同一個(gè)進(jìn)程,那么在onServiceConnected方法得到的service就是我們在onBind中返回的類型IDownloadCenter.Stub扫尖;
- 如果不是同一個(gè)進(jìn)程白对,那得到的就是IDownloadCenter.Stub.Proxy類型;
拿到了IDownloadCenter.Stub.Proxy换怖,那就可以調(diào)用它的方法了 downloadCenter.addDownloadTask(task)甩恼,下面是IDownloadCenter.Stub.Proxy的實(shí)現(xiàn):
public static abstract class Stub extends android.os.Binder implements com.jdqm.downloadcenter.aidl.IDownloadCenter {
//省略前面代碼
@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_addDownloadTask: {
data.enforceInterface(DESCRIPTOR);
com.jdqm.downloadcenter.aidl.DownloadTask _arg0;
if ((0 != data.readInt())) {
_arg0 = com.jdqm.downloadcenter.aidl.DownloadTask.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addDownloadTask(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getDownloadTask: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.jdqm.downloadcenter.aidl.DownloadTask> _result = this.getDownloadTask();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.jdqm.downloadcenter.aidl.IDownloadCenter {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
//添加下載任務(wù)
@Override
public void addDownloadTask(com.jdqm.downloadcenter.aidl.DownloadTask task) 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 ((task != null)) {
//注意這里寫入的是1
_data.writeInt(1);
//序列化我們傳入的實(shí)參,到這里你知道為什么跨進(jìn)程通信的對象需要實(shí)現(xiàn)Parcelable接口了吧
task.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//這個(gè)mRemote就是是BinderProxy
mRemote.transact(Stub.TRANSACTION_addDownloadTask, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
//查詢所有的添加的下載任務(wù)
@Override
public java.util.List<com.jdqm.downloadcenter.aidl.DownloadTask> getDownloadTask() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.jdqm.downloadcenter.aidl.DownloadTask> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getDownloadTask, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.jdqm.downloadcenter.aidl.DownloadTask.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_addDownloadTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getDownloadTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
從Proxy#addDownloadTask方法可以看到,通過 task.writeToParcel(_data, 0)条摸,將我們傳入的DownloadTask對象進(jìn)行了序列化悦污,終于知道為啥前面寫的DownloadTask要實(shí)現(xiàn)Parcelable接口了。接著調(diào)用 mRemote.transact(Stub.TRANSACTION_addDownloadTask, _data, _reply, 0)方法钉蒲,通過前面創(chuàng)建Proxy對象的過程切端,我們知道這個(gè)mRemote是BinderProxy,這樣邏輯就交給了底層的Binder機(jī)制顷啼,服務(wù)端的onTransact就會(huì)被調(diào)用踏枣。接下來就看看onTransact方法中 TRANSACTION_addDownloadTask 這個(gè)case的實(shí)現(xiàn):
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case TRANSACTION_addDownloadTask: {
data.enforceInterface(DESCRIPTOR);
com.jdqm.downloadcenter.aidl.DownloadTask _arg0;
if ((0 != data.readInt())) {
_arg0 = com.jdqm.downloadcenter.aidl.DownloadTask.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addDownloadTask(_arg0);
reply.writeNoException();
return true;
}
}
//省略其他內(nèi)容
}
首先 if ((0 != data.readInt())) 這個(gè)條件是true,因?yàn)槲覀冊赑roxy#addDownloadTask中寫入的是1线梗。但是這里我有一個(gè)疑問,為啥這個(gè)if語句多了一層圓括號(hào)怠益?你知道嗎仪搔?然后通過DownloadTask.CREATOR.createFromParcel(data)將數(shù)據(jù)反序列化并賦值給 _arg0,接著調(diào)用 this.addDownloadTask(_arg0)蜻牢,這個(gè)this不就是我們在onBind返回的Stub實(shí)例嗎烤咧!就這樣服務(wù)端方法被調(diào)用了。
另外一個(gè)方法getDownloadTask()方法的調(diào)用過程也是類似抢呆,有區(qū)別的是它有返回值煮嫌,所以onTransact()方法時(shí)多了 reply.writeTypedList(_result),回到transact()方法時(shí)將結(jié)果反序列化 _result = _reply.createTypedArrayList(com.jdqm.downloadcenter.aidl.DownloadTask.CREATOR)抱虐。下面給出一張粗略的工作流程圖輔助理解這個(gè)過程:
三.最后
- 在這個(gè)例子中昌阿,我們是在主線程中調(diào)用服務(wù)方法downloadCenter.addDownloadTask(task),在執(zhí)行mRemote.transact(Stub.TRANSACTION_getDownloadTask, _data, _reply, 0)時(shí)調(diào)用線程會(huì)被掛起恳邀,所以如果不能確保遠(yuǎn)程服務(wù)方法的是否耗時(shí)時(shí)懦冰,應(yīng)該避免在主線程中調(diào)用。當(dāng)然了谣沸,aidl還提供了一個(gè)關(guān)鍵字 oneway刷钢,定義方法時(shí)加上這個(gè)關(guān)鍵字就不用等待服務(wù)端方法返回了,如果這滿足你的業(yè)務(wù)不需要等待結(jié)果乳附,那也是一個(gè)不錯(cuò)的選擇内地。
//添加下載任務(wù)
oneway void addDownloadTask(in DownloadTask task);
- 另外一點(diǎn)就是服務(wù)端方法是運(yùn)行在Binder線程池中的,要考慮好線程同步赋除。
最后并不代表結(jié)束了阱缓,可能又是一個(gè)新的開始。細(xì)心的讀者可能會(huì)留意到前面我們寫的aidl文件:
// IDownloadCenter.aidl
package com.jdqm.downloadcenter.aidl;
import com.jdqm.downloadcenter.aidl.DownloadTask;
interface IDownloadCenter {
//添加下載任務(wù)
void addDownloadTask(in DownloadTask task);
//查詢所有的添加的下載任務(wù)
List<DownloadTask> getDownloadTask();
}
其中 addDownloadTask 這個(gè)方法的參數(shù)前面有一個(gè)in举农,這個(gè) in 是什么茬祷?這是一個(gè)定向tag,事實(shí)上定向tag有3個(gè)in、out祭犯、inout秸妥,它們又是什么含義?下一篇文章將從零開始探索這3個(gè)定向tag的含義以及用法沃粗。