從一個(gè)例子開始分析AIDL原理

上一個(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é)一下步驟:

  1. 在src/main/aidl/下創(chuàng)建了兩個(gè) aidl文件:DownloadTask.aidl梯轻、IDownloadCenter.aidl;
  2. 創(chuàng)建一個(gè)IDownloadCenter.Stub實(shí)現(xiàn)類食磕,并在onBind中返回一個(gè)實(shí)例: DownloadCenter extends IDownloadCenter.Stub;
  3. 在客戶端實(shí)現(xiàn)一個(gè)ServiceConnection;
  4. Context.bindService(), 在回調(diào)onServiceConnected()中接收IBinder實(shí)例, 并且調(diào)用IDownloadCenter.Stub.asInterface(service)將返回值轉(zhuǎn)換為IDownloadCenter類型喳挑。
  5. 調(diào)用IDownloadCenter接口中定義的方法來完成跨進(jìn)程調(diào)用芬为;
  6. 調(diào)用Context.unbindService()斷開連接。
兩個(gè)項(xiàng)目結(jié)構(gòu)

粗略地總結(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è)過程:

Binder

三.最后

  • 在這個(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的含義以及用法沃粗。

關(guān)注微信公眾號(hào)粥惧,第一時(shí)間接收推送!

客戶端源碼
服務(wù)端源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市最盅,隨后出現(xiàn)的幾起案子突雪,更是在濱河造成了極大的恐慌,老刑警劉巖涡贱,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咏删,死亡現(xiàn)場離奇詭異,居然都是意外死亡问词,警方通過查閱死者的電腦和手機(jī)督函,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來激挪,“玉大人辰狡,你說我怎么就攤上這事÷⒎郑” “怎么了宛篇?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長薄湿。 經(jīng)常有香客問我叫倍,道長,這世上最難降的妖魔是什么豺瘤? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任段标,我火速辦了婚禮,結(jié)果婚禮上炉奴,老公的妹妹穿的比我還像新娘逼庞。我一直安慰自己,他們只是感情好瞻赶,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布赛糟。 她就那樣靜靜地躺著,像睡著了一般砸逊。 火紅的嫁衣襯著肌膚如雪璧南。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天师逸,我揣著相機(jī)與錄音司倚,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛动知,可吹牛的內(nèi)容都是我干的皿伺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼盒粮,長吁一口氣:“原來是場噩夢啊……” “哼鸵鸥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丹皱,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤妒穴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后摊崭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讼油,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年呢簸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矮台。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阔墩,死狀恐怖嘿架,靈堂內(nèi)的尸體忽然破棺而出瓶珊,到底是詐尸還是另有隱情啸箫,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布伞芹,位于F島的核電站忘苛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏唱较。R本人自食惡果不足惜扎唾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望南缓。 院中可真熱鬧胸遇,春花似錦、人聲如沸汉形。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽概疆。三九已至逗威,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間岔冀,已是汗流浹背凯旭。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罐呼。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓鞠柄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親弄贿。 傳聞我的和親對象是個(gè)殘疾皇子春锋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容