Android Aidl 的使用

Android Aidl 的使用

Binder框架 -- android AIDL 的使用

Binder框架 – 用戶空間和驅(qū)動的交互

Binder框架 – Binder 驅(qū)動

Binder 框架 – binder 用戶空間框架

Aidl 是android 跨進程通信的中一種,是一種RPC。底層基于binder 框架。通常用在C/S架構(gòu)中。

Aidl 跨進程通信支持有限的數(shù)據(jù)類型

Aidl 可以進行跨進程通信,但是不是所有的數(shù)據(jù)類型都支持,支持的類型主要是:

  1. Java 的基本類型
  2. String 和CharSequence
  3. List 和 Map, 并且List和Map 對象的元素必須是AIDL支持的數(shù)據(jù)類型;以上三種類型都不需要導入(import)
  4. AIDL 自動生成的接口 需要導入(import)
  5. 實現(xiàn)android.os.Parcelable 接口的類. 需要導入(import)。

創(chuàng)建Aidl 文件

在android studio 中直接new 一個Aidl 文件 IHelloWorldInterface.aidl。注意文件命名規(guī)則,IXXX.adil。 只定義了一個printHelloWorld 接口。

interface IHelloWorldInterface {
    /**
     * 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);

    String printHelloWorld();
}

編譯后生成 IHelloWorldInterface.java 文件。由于文件是aidl 工具生成的,格式比較亂,代碼用工具格式化后IHelloWorldInterface 是一個接口,里面主要是兩個類靜態(tài)虛類public Stub類和private Stub.Proxy 類,Stub為存根的意思,那就是服務(wù)端使用,Stub.Proxy 為代理類,客戶端使用。

public interface IHelloWorldInterface extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements com.example.louiewh.aidlapplication.IHelloWorldInterface {
        private static final java.lang.String DESCRIPTOR = "com.example.louiewh.aidlapplication.IHelloWorldInterface";

        private static class Proxy implements com.example.louiewh.aidlapplication.IHelloWorldInterface {
            private android.os.IBinder mRemote;
            
            @Override
            public java.lang.String printHelloWorld() throws android.os.RemoteException {
              
            }
        }
    }
}

服務(wù)端的實現(xiàn)

服務(wù)端 HelloWorldService 繼承 IHelloWorldInterface.Stub 接口

public class HelloWorldService  extends IHelloWorldInterface.Stub {
    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

    }

    @Override
    public String printHelloWorld() throws android.os.RemoteException {
        if(mListerner != null) {
            final int begin = mListerner.beginBroadcast();
            for (int i = 0; i < begin; i++) {
                mListerner.getBroadcastItem(i).onAidlListerner(
                        new StringBuilder().
                        append("Pid:").append(android.os.Process.myPid()).
                        append(" Threadtime:").append(SystemClock.uptimeMillis()).
                        toString()
                );
            }
            mListerner.finishBroadcast();
        }
        return "Hello AIDL!";
    }
}

創(chuàng)建Service

創(chuàng)建一個AidlService extends Service。AndroidManifext 中注冊Service。 在Service 的
onBind 中返回HelloWorldService。

public class AidlService extends Service {
    public final static String TAG = "AidlService";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new HelloWorldService();
    }
}

代理端調(diào)用

service 啟動

android Service 啟動有兩種方式,一種startService,一種binderService. 由于我們要獲取Service 的代理端,使用binderService。在 MainActivity onCreate 中 binderService。

 Intent intent = new Intent(context, AidlService.class);
 context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

獲取Service Proxy

然后 New 一個ServiceConnection, 在ServiceConnection中的onServiceConnected回調(diào)函數(shù)中會通過IHelloWorldInterface.Stub.asInterface返回Stub.proxy 對象。這樣我們就拿到了服務(wù)的代理端。在MainActivity中設(shè)置text.setOnClickListener,當點擊時顯示printHelloWorld 的結(jié)果,就是進程PID和uptimeMillis時間。

ServiceConnection helloWorldConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            remoteService = IHelloWorldInterface.Stub.asInterface(iBinder);

         }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
    
text.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    text.setText(remoteService.printHelloWorld());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
 });

service 退出

在Activity退出時,需要unbinder

protected void onDestroy() {
     helloWorldProxy.unbindService();
     super.onDestroy();
}

一個Aidl 通信的架構(gòu)就基本完完成了.

需要注意的是ServiceConnection是異步通信

設(shè)置listener

現(xiàn)在客戶端可以調(diào)用服務(wù)端了,如果服務(wù)端需要通知客戶端呢,就需要listener 出場了,listener 同樣基于Aidl。定義一個onAidlListerner 回調(diào)。

1. 定義IAidlListernerInterface

interface IAidlListernerInterface {
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    void onAidlListerner(String str);
}

2. 定義一個抽象類 AidlListerner 繼承IAidlListernerInterface.Stub。

  public  abstract class AidlListerner extends IAidlListernerInterface.Stub {
}

3. IHelloWorldInterface.aidl 中增加兩個函數(shù),注冊listener 和 反注冊listener。

 void registerListerner(IAidlListernerInterface listener);

 void unregisterListerner(IAidlListernerInterface listener);

4. HelloWorldService 中同樣實現(xiàn)這兩個函數(shù)。

同時有一個變量 ArrayList<IAidlListernerInterface> mListerner = new ArrayList<IAidlListernerInterface>() 保存,

在調(diào)用printHelloWorld 時,回調(diào)listener函數(shù)。

 public String printHelloWorld() throws RemoteException {
        if(mListerner != null) {
            for (int i = 0; i < mListerner.size(); i++) {
                mListerner.get(i).onAidlListerner(new StringBuilder().append("current time:").append(SystemClock.currentThreadTimeMillis()).toString());;
            }
        }
        return "Hello AIDL!";
    }

5. MainActivity 中 注冊

這樣在點擊text 的時候 listenertext 會顯示printHelloWorld 的結(jié)果。

remoteService.registerListener(new AidlListener() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }

            @Override
            public void onAidlListerner(String str) throws RemoteException {
                listenerText.setText(str);
            }
        });        

onDestory 中 unregisterListener。

6. 多進程模式下RemoteCallbackList

現(xiàn)在Service 和Activity 運行在一個進程中,Service 和 Client 通常運行在不同的進程中,現(xiàn)在我們配置Service 運行在另外一個進程。 這時候需要注意的是在HelloWorldService 中保存listener 的變量mListerner 類型為ArrayList 修改為: RemoteCallbackList<IAidlListernerInterface> mListerner = new RemoteCallbackList<IAidlListernerInterface>();
原因是因為垮進程的listener 的指針地址改變了。listener 的遍歷方式也發(fā)生了變化。

 <service
     android:name=".AidlService"
     android:process=":AidlService">
 </service>  
if(mListerner != null) {
     final int begin = mListerner.beginBroadcast();
     for (int i = 0; i < begin; i++) {
         mListerner.getBroadcastItem(i).onAidlListerner(new StringBuilder().
                     append("Pid:").append(android.os.Process.myPid()).                      append("Current time:").append(SystemClock.currentThreadTimeMillis()).
                        toString()
        );
    }
    mListerner.finishBroadcast();
}

代理服務(wù)

在一個應用里可能有很多這樣的Aidl 文件,通常的做法是每個Aidl 文件都都建一個Service 用來bind,這樣一個APK 進程中有很多Service 存在。是不是我們用一個Service 就可以了,因為都是在后臺運行,這樣就大大減小了對內(nèi)存的消耗。在講到Aidl 傳輸數(shù)據(jù)類型的時候Aidl 本身也支持Aidl自動生成的Interface 類型的傳輸,而我們定義的業(yè)務(wù)Service,比如HelloWorldService 本身就是繼承Aidl 自動生成的Interface 類型。所以可以有一個專業(yè)的Service 來傳遞業(yè)務(wù)Service。 通常在獲取系統(tǒng)服務(wù)的時候是 getApplicationContext().getSystemService("XXX"), 同樣也定義一個這樣的API,來為應用內(nèi)的調(diào)用提供APK 級別的Service 服務(wù)。

定義 IAidlBinderService.aidl

這個Aidl 文件之定義一個接口getService.

interface IAidlBinderService {
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    IBinder getService(String service);
}

所有的業(yè)務(wù)Service 都通過getService 獲取。

AidlBinderService 的服務(wù)端

onBind 的時候返回AidlBinderService 的Binder。
通過getService 傳遞進來的參數(shù)返回不同的Service 服務(wù)。這里一共兩種業(yè)務(wù)Service,HelloWorldService HelloAidlService。這兩種Service 單獨一個java 文件,和Service 代碼分離,當加入一個新的Service的時候,通常定義Aidl 文件,Service 代碼繼承 Stub 接口。然后定義getService 的字符串就可以加入一個新的Service。

public class AidlService extends Service {

    public final static String HELLOWORLDSERVICE = "HelloWorldService";
    public final static String HELLOAIDLSERVICE = "HelloAidlService";
    public final static String TAG = "AidlService";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new AidlBinderService();
    }

    class AidlBinderService extends IAidlBinderService.Stub{

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public IBinder getService(String service) throws RemoteException {
            switch(service) {
                case HELLOWORLDSERVICE:
                    return new HelloWorldService();
                case HELLOAIDLSERVICE:
                    return new HelloAidlService();
                default:
                    return null;
            }
        }
    }
}

AidlBinderService 的代理。

在處理AidlBinderService 的代理的時候,通常是在 ServiceConnection 回調(diào)中得到 iBinder。 這是一個異步的通信。我們希望代碼能夠耦合度更低,和業(yè)務(wù)能夠分開,把AidlBinderService 單獨的實現(xiàn)子啊一個java 文件中。一個做法是使用異步轉(zhuǎn)同步的方法,直到ServiceConnection 的 onConnected 返回。但是這樣畢竟有系統(tǒng)時間的消耗,而Service 的binder通常在APK 啟動時,影響啟動速度。這里繼續(xù)沿用異步回到,兩種方法,設(shè)置回調(diào)函數(shù),使用Handler。AidlBinderServiceProxy 為單例模式,構(gòu)造函數(shù)中去bindService,在onServiceConnected 使用傳遞進來的Handler 發(fā)送binderService 成功的消息。

獲取AidlBinderService 的代理

public class AidlBinderServiceProxy {
    static final String TAG = "AidlBinderServiceProxy";
    static final int AidlBinderService = 1;
    private Context mContext;
    private ServiceConnection mConnection;
    private IAidlBinderService mRemoteService;
    private Handler mHandler;
    private static AidlBinderServiceProxy instance;

    private AidlBinderServiceProxy(Context context, Handler handler) {
        mContext = context;
        mHandler = handler;
        initServiceConnection();
        Intent intent = new Intent(context, AidlService.class);
        context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    public static AidlBinderServiceProxy instance(Context context, Handler handler){

        synchronized (AidlBinderServiceProxy.class) {
            if(instance == null) {
                instance = new AidlBinderServiceProxy(context, handler);
            }
        }

        return instance;
    }

    public void unbindService() {

        Log.d(TAG, "unbindService");
        mContext.unbindService(mConnection);
        instance = null;
    }

    private void initServiceConnection() {
        mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                mRemoteService = IAidlBinderService.Stub.asInterface(iBinder);
                Message message = Message.obtain(mHandler, AidlBinderService);
                mHandler.sendMessage(message);
                Log.d(TAG, "onServiceConnected");
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {

            }
        };
    }

    public IBinder getService(String service) {
        if(mRemoteService == null) {
        }

        try {
            return mRemoteService.getService(service);
        } catch (RemoteException e) {
            Log.e(TAG, "getService " + "service");
            e.printStackTrace();
        }

        return null;
    }
}

實現(xiàn)業(yè)務(wù)Service 的Proxy

實現(xiàn)兩個業(yè)務(wù)Service 的Proxy。HelloAidlProxy 如下,在構(gòu)造函數(shù)中通過getService 獲取到IBinder 對象,通過I HelloAidlInterface.Stub.asInterface 函數(shù)轉(zhuǎn)為代理對象。

public class HelloAidlProxy {
    static final String TAG = "HelloAidlProxy";
    public AidlBinderServiceProxy  mAidlBinderService;
    private IHelloAidlInterface mRemoteService;


    public HelloAidlProxy(AidlBinderServiceProxy proxy) {

        mAidlBinderService = proxy;
        IBinder binder = mAidlBinderService.getService(AidlService.HELLOAIDLSERVICE);

        mRemoteService = IHelloAidlInterface.Stub.asInterface(binder);
    }

    public PidInfo getPidInfo() {
        if(mRemoteService != null){
            try {
                return mRemoteService.getPidInfo();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        return null;
    }
}

調(diào)用

修改MainActivity 的實現(xiàn), 實現(xiàn)一個內(nèi)部Handler 類:

class AidlHandler extends Handler{
    public void handleMessage(Message msg) {

        switch(msg.what) {
            case AidlBinderServiceProxy.AidlBinderService:
                Log.d("louie", "AidlBinderService");
                helloWorldProxy = new HelloWorldProxy(mAidlBinderService);
            default:
                break;
            }
        });            

使用代理服務(wù)前后App 的結(jié)構(gòu)變化:

Aidl Service

https://www.processon.com/view/link/57a5991be4b02c28bf471316

Parcelable

上面已經(jīng)實現(xiàn)了Aidl 生成的類型的數(shù)據(jù)跨進程傳遞,Aidl 生成的數(shù)據(jù)類型主要用于C/S 這樣的架構(gòu)。對于普通的對象如何處理呢,android 給我們準備了Parcelable 這種數(shù)據(jù)類型。和java 的Serializable 比較類似,Serializable 序列化基于文本,Parcelable 基于binder,效率更高。Parcelable 基于android提供的Parcel類型,將數(shù)據(jù)寫入Parcel打包,需要的時候再從Parcel 讀出完成序列化。

定義類繼承 Parcelable

類 PidInfo implements Parcelable

  • 重寫writeToParcel方法,將對象序列化為一個Parcel對象,即:將類的數(shù)據(jù)寫入外部提供的Parcel中
  • 重寫describeContents方法,內(nèi)容接口描述,默認返回0就可以
  • 實例化靜態(tài)內(nèi)部對象CREATOR實現(xiàn)接口Parcelable.Creator,在 createFromParcel new 了一個參數(shù)為 Parcel 構(gòu)造函數(shù),需要注意的是:
    這個構(gòu)造函數(shù)實現(xiàn)的時候的讀寫順序要和writeToParcel 方法一致
public class PidInfo implements Parcelable {
    private int mPid;
    
    PidInfo(int pid ){
        mPid = pid;
    }

    PidInfo(Parcel in ) {
        mPid = in.readInt();
    }

    public int getPid(){
        return mPid;
    }

    public void setPid(int pid ){
        mPid = pid;
    }

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

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

    public static final Creator<PidInfo> CREATOR = new Creator<PidInfo>(){

        @Override
        public PidInfo createFromParcel(Parcel source) {
            return new PidInfo(source);
        }

        @Override
        public PidInfo[] newArray(int size) {
            return new PidInfo[0];
        }
    };
}

AIDL 聲明

PidInfo 定義完后還不能直接使用,需要在Aidl 中聲明:

  • 新建一個和類同名的Aidl 文件 PidInfo.aidl
  • 在PidInfo.aidl 中 聲明PidInfo 為parcelable 類型
parcelable PidInfo;

code

Aild 的使用大概就是這些,code 地址:github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容