Framework 源碼解析知識梳理(3) 應用進程之間的通訊實現

1、前言

Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現Framework 源碼解析知識梳理(2) - 應用進程與 WMS 的通訊實現 這兩篇文章中,咱們介紹了應用進程與AMS以及WMS之間的通訊實現,可是邏輯仍是比較繞的,爲了方便你們更好地理解,咱們介紹一下你們見得比較多的應用進程間通訊的實現。java

2、例子

提及應用進程之間的通訊,相信你們都不陌生,應用進程之間通訊最經常使用的方式就是AIDL,下面,咱們先演示一個AIDL的簡單例子,接下來,咱們再分析它的內部實現。android

2.1 服務端

2.1.1 編寫 AIDL 文件

第一件事,就是服務端須要聲明本身能夠爲客戶端提供什麼功能,而這一聲明則須要經過一個.aidl文件來實現,咱們先簡要介紹介紹一下AIDL文件:bash

  • AIDL文件的後綴爲*.aidl
  • 對於AIDL默認支持的數據類型,是不須要導入包的,這些默認的數據類型包括:
  • 基本數據類型:byte/short/int/long/float/double/boolean/char
  • String/CharSequence
  • List<T>:其中T必須是AIDL支持的類型,或者是其它AIDL生成的接口,或者是實現了Parcelable接口的對象,List支持泛型
  • Map:它的要求和List相似,可是不支持泛型
  • Tag標籤:對於接口方法中的形參,咱們須要用in/out/inout三種關鍵詞去修飾:
  • in:表示服務端會收到客戶端的完整對象,可是在服務端對這個對象的修改不會同步給客戶端
  • out:表示服務端會收到客戶端的空對象,它對這個對象的修改會同步到客戶端
  • inout:表示服務端會收到客戶端的完整對象,它對這個對象的修改會同步到客戶端

說了這麼多,咱們最多見的需求無非就是兩個:讓複雜對象實現Parcelable接口實現傳輸以及定義接口方法。app

(1) Parcelable 的實現ide

咱們能夠很方便地經過AS插件來讓某個對象實現Parcelable接口,並自動補全要實現的方法: 函數

安裝完以後重啓,咱們本來的對象爲:

public class InObject {

    private int inData;

    public int getInData() {
        return inData;
    }

    public void setInData(int inData) {
        this.inData = inData;
    }
    
}
複製代碼

在文件的空白處,點擊右鍵Generate -> Parcelableui

它就會幫咱們實現 Parcelable中的接口:

public class InObject implements Parcelable {

    private int inData;

    public int getInData() {
        return inData;
    }

    public void setInData(int inData) {
        this.inData = inData;
    }

    public InObject() {}

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.inData);
    }

    protected InObject(Parcel in) {
        this.inData = in.readInt();
    }

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

        @Override
        public InObject[] newArray(int size) {
            return new InObject[size];
        }
    };
}
複製代碼

(2) 編寫 AIDL 文件this

點擊File -> New -> AIDL -> AIDL File以後,會多出一個名叫aidl的文件夾,以後咱們全部須要用到的aidl文件都被存放在這裏: spa

初始時候, AS爲咱們生成的 AIDL文件爲:

// AIDLInterface.aidl
package com.demo.lizejun.binderdemoclient;

// Declare any non-default types here with import statements

interface AIDLInterface {
    /**
     * 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);
}
複製代碼

當咱們編譯以後,就會在下面這個路徑中生成一個Java文件:插件

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /home/lizejun/Repository/RepoGithub/BinderDemo/app/src/main/aidl/com/demo/lizejun/binderdemoclient/AIDLInterface.aidl
 */
package com.demo.lizejun.binderdemoclient;
// Declare any non-default types here with import statements

public interface AIDLInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.demo.lizejun.binderdemoclient.AIDLInterface {
        private static final java.lang.String DESCRIPTOR = "com.demo.lizejun.binderdemoclient.AIDLInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.demo.lizejun.binderdemoclient.AIDLInterface interface,
         * generating a proxy if needed.
         */
        public static com.demo.lizejun.binderdemoclient.AIDLInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.demo.lizejun.binderdemoclient.AIDLInterface))) {
                return ((com.demo.lizejun.binderdemoclient.AIDLInterface) iin);
            }
            return new com.demo.lizejun.binderdemoclient.AIDLInterface.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_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.demo.lizejun.binderdemoclient.AIDLInterface {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}
複製代碼

整個結構圖爲:

有沒有感受在 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現Framework 源碼解析知識梳理(2) - 應用進程與 WMS 的通訊實現 中也見到過相似的東西 asInterface/asBinder/transact/onTransact/Stub/Proxy...,這個咱們以後再來解釋,下面咱們介紹服務端的第二步操做。

2.1.2 編寫 Service

既然服務端已經定義好了接口,那麼接下來就服務端就須要實現這些接口:

public class AIDLService extends Service {

    private final AIDLInterface.Stub mBinder = new AIDLInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.d("basicTypes", "basicTypes");
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
複製代碼

在這個Service中,咱們只須要實現AIDLInterface.Stub接口中的basicTypes接口就能夠了,它就是咱們在AIDL文件中定義的接口,再把這個對象經過onBinde方法返回。

2.1.3 聲明 Service

最後一步,在AndroidManifest.xml文件中聲明這個Service

<service
            android:name=".server.AIDLService"
            android:enabled="true"
            android:exported="true">
        </service>
複製代碼

2.2 客戶端

2.2.1 編寫 AIDL 文件

和服務端相似,客戶端也須要和服務端同樣,生成一個相同的AIDL文件,這裏就很少說了:

// AIDLInterface.aidl
package com.demo.lizejun.binderdemoclient;

// Declare any non-default types here with import statements

interface AIDLInterface {
    /**
     * 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);
}
複製代碼

和服務端相似,咱們也會獲得一個由aidl生成的Java接口文件。

2.2.2 綁定服務,並調用

(1) 綁定服務

private void bind() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.demo.lizejun.binderdemo", "com.demo.lizejun.binderdemo.server.AIDLService"));
        boolean result = bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
        Log.d("bind", "result=" + result);
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinder = AIDLInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBinder = null;
        }
    };
複製代碼

(2) 調用接口

public void sayHello(View view) {
        try {
            if (mBinder != null) {
                mBinder.basicTypes(0, 0, false, 0, 0, "aaa");
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
複製代碼

以後咱們打印出響應的log

3、實現原理

下面,咱們就一塊兒來分析一下經過AIDL實現的進程間通訊的原理。

**(1) 客戶端得到服務端的遠程代理對象 IBinder **

咱們從客戶端提及,當咱們在客戶端調用了bindService方法以後,就會啓動服務端實現的AIDLService,而在該AIDLServiceonBind()方法中返回了AIDLInterface.Stub的實現類,綁定成功以後,客戶端就經過onServiceConnected的回調,獲得了它在客戶端進程的遠程代理對象IBinder

(2) AIDLInterface.Stub.asInterface(IBinder)

在拿到這個IBinder對象以後,咱們經過AIDLInterface.Stub.asInterface(IBinder)方法,對這個IBinder進行了一層包裝,轉換成爲AIDLInterface接口類,那麼這個AIDLInterface是怎麼來的呢,它就是經過咱們在客戶端定義的aidl文件在編譯時生成的,能夠看到,最終asInterface方法會返回給咱們一個AIDLInterface的實現類ProxyIBinder則被保存爲它內部的一個成員變量mRemote

也就是說,咱們在客戶端中保存的 mBinder其實是一個由 AIDL文件所生成的 Proxy對象。

(3) 調用 AIDLInterface 的接口方法

Proxy實現了AIDLInterface中定義的接口方法,當咱們調用它的接口方法時:

其實是經過它內部的 mRemote對象的 transact方法發送消息:

(4) 服務端接收消息

那麼客戶端發送的這一消息去哪裏了呢,回想一下在服務端中經過onBind()放回的IBinder,它實際上是由AIDL文件生成的AIDLInterface.java中的內部類Stub的實現,而在Stub中有一個回調函數onTransact,當咱們經過客戶端發送消息以後,那麼服務端的Stub對象就會經過onTransact收到這一發送的消息:

(5) 調用子類的處理邏輯

onTransact方法中,又會去調用AIDLInterface定義的接口:

而咱們在服務端AIDLService中實現了該接口,所以,最終就打印出了咱們在上面所看到的文字:

4、進一步討論

如今,咱們經過這一進程間的通訊過程來複習一下前面兩篇文章中討論的應用進程與AMSWMS之間的通訊實現。

Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現 中,AMS所在進程在收到消息以後,進行了下面的處理邏輯:

這裏面的處理邏輯就至關於咱們在客戶端中 onServiceConnected中作的工做同樣, IApplicationThread其實是一個 ApplicaionThreadProxy對象,和咱們上面 AIDLInterface其實是一個 AIDLInterface.Stub.Proxy的原理是同樣的。當咱們調用了它的接口方法時,就是經過內部的 mRemote發送了消息。

AMS通訊中,接收消息的是應用進程中的ApplicationThread,而咱們上面例子中接收消息的是服務端進程的AIDLInterface.Stub的實現類mBinder,一樣是在onTransact()方法中接收消息,再由子類去實現處理的邏輯。

Framework 源碼解析知識梳理(2) - 應用進程與 WMS 的通訊實現 中的原理就更好解釋了,由於它就是經過AIDL來實現的,咱們從客戶端向WMS所在進程創建會話時,是經過IWindowSession來實現的,會話過程當中IWindowSession就對應於AIDLInterface,而WMS中的IWindowSession.Stub的實現類Session,就對應於上面咱們在AIDLService中定義的AIDLInterface.Stub的實現類mBinder

5、小結

其實AIDL並無什麼神祕的東西,它的本質就是Binder通訊,咱們定義aidl文件的目的,主要有兩個:

  • 爲了讓應用進程間通訊的實現者不用再去編寫transact/onTransact裏面的代碼,由於這些東西和業務邏輯是無關的,只不過是簡單的發送消息、接收消息。
  • 讓服務端、客戶端只關心接口的定義。

若是咱們明白了AIDL的原理,那麼咱們徹底能夠不用定義AIDL文件,本身去參考由AIDL文件所生成的Java文件的邏輯,進行消息的發送和處理。


更多文章,歡迎訪問個人 Android 知識梳理系列:

相關文章
相關標籤/搜索