【Android】由淺到深理解AIDL

來源:互聯網
上載者:User

【Android】由淺到深理解AIDL
一、 Binder概述1.1 為什麼要用binder出於安全性、穩定性和記憶體管理的考慮,Android的應用和系統服務運行在分離的進程中,但是它們之間需要通訊和共用資料避免傳統IPC開銷和服務拒絕的問題android的庫不支援System V 的IPCBinder加入了對象引用的引用計數器,消亡提醒機制。當一個Binder服務沒有任何終端引用時,它的所有者可以自動提醒它去處理自己Binder通過UID/PID來分辨寄件者和接受者(對於安全很重要)

但是:

Binder不支援RPC,只有本地用戶端和服務端基於訊息通訊,不適用於流不符合POSIX標準
1.2 binder通訊流程

用戶端使用服務

進程之間無法進行直接通訊,所以通過Binder驅動

用戶端和服務端不需要瞭解binder協議,所以使用代理和存根

用戶端不想要知道正在使用IPC,也不關心binder和代理,所以,需要管理對象進行抽象

但是用戶端怎樣擷取它想要通訊的服務的handle,只需要問問sevicemanager(Context Manager),服務是否已經註冊

最後,我們看下總體的架構

二 、 AIDL樣本

 

使用aidl實現跨進程的加減法

2.1 服務端

建立android工程,建立包com.realize.calc.aidl,建立檔案ICalcAIDL.aidl,內容如下

 

package com.realize.calc.aidl;interface ICalcAIDL{int add(int x , int y);int min(int x , int y );}

建立包com.realize.lizijun.binder_server,建立服務CalcService.java,內容如下

 

 

package com.realize.lizijun.binder_server; import com.realize.calc.aidl.ICalcAIDL; import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.util.Log; public class CalcService extends Service{private static final String TAG = "server"; public void onCreate(){Log.e(TAG, "onCreate");} public IBinder onBind(Intent t){Log.e(TAG, "onBind");return mBinder;} public void onDestroy(){Log.e(TAG, "onDestroy");super.onDestroy();} public boolean onUnbind(Intent intent){Log.e(TAG, "onUnbind");return super.onUnbind(intent);} public void onRebind(Intent intent){Log.e(TAG, "onRebind");super.onRebind(intent);} private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub(){ @Overridepublic int add(int x, int y) throws RemoteException{return x + y;} @Overridepublic int min(int x, int y) throws RemoteException{return x - y;} }; }

AndroidManifest.xml中註冊服務
        <service android:name="com.realize.lizijun.binder_server.CalcService">            <intent-filter>                <action android:name="com.realize.calc.aidl">                 <category android:name="android.intent.category.DEFAULT">            </category></action></intent-filter>        </service>
2.2 用戶端

建立android工程,建立包com.realize.calc.aidl,建立檔案ICalcAIDL.aidl(與服務端是一樣的),內容如下

package com.realize.calc.aidl;interface ICalcAIDL{int add(int x , int y);int min(int x , int y );}

主activity內容如下
package com.realize.lizijun.binder_client; import android.app.Activity;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.util.Log;import android.view.View;import android.widget.Toast; import com.realize.calc.aidl.ICalcAIDL; public class MainActivity extends Activity{private static final String TAG = "client";private ICalcAIDL mCalcAidl; private ServiceConnection mServiceConn = new ServiceConnection(){@Overridepublic void onServiceDisconnected(ComponentName name){Log.e(TAG, "onServiceDisconnected");mCalcAidl = null;} @Overridepublic void onServiceConnected(ComponentName name, IBinder service){Log.e(TAG, "onServiceConnected");mCalcAidl = ICalcAIDL.Stub.asInterface(service);}}; @Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); } /** * 點擊BindService按鈕時調用 * @param view */public void bindService(View view){Intent intent = new Intent();intent.setAction("com.realize.calc.aidl");bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);}/** * 點擊unBindService按鈕時調用 * @param view */public void unbindService(View view){unbindService(mServiceConn);}/** * 點擊12+12按鈕時調用 * @param view */public void addInvoked(View view) throws Exception{ if (mCalcAidl != null){int addRes = mCalcAidl.add(12, 12);Toast.makeText(this, addRes + "", Toast.LENGTH_SHORT).show();} else{Toast.makeText(this, "伺服器被異常殺死,請重新綁定服務端", Toast.LENGTH_SHORT).show(); } }/** * 點擊50-12按鈕時調用 * @param view */public void minInvoked(View view) throws Exception{ if (mCalcAidl != null){int addRes = mCalcAidl.min(50, 12);Toast.makeText(this, addRes + "", Toast.LENGTH_SHORT).show();} else{Toast.makeText(this, "伺服器未綁定或被異常殺死,請重新綁定服務端", Toast.LENGTH_SHORT).show(); } } }

介面如下:

 

2.3 結果說明

 

點擊BindService之後,服務端執行了onCreate和onBind的方法, 用戶端執行onServiceConnected方法然後點擊12+12,50-12可以成功的調用服務端的代碼並返回正確的結果再點擊unBindService, Service調用了onUnbind和onDestory然後點擊12+12,50-12,仍然能夠看到正確的結果,說明用戶端與服務端的串連仍然存在我們通過後台,把服務強制停止掉,可以看到調用了onServiceDisconnected方法,此時,再點擊12+12,50-12,就擷取不到結果了三、 分析AIDL產生的介面代碼

上面建立ICalcAIDL.aidl之後,在gen目錄下回組建檔案ICalcAIDL.java, 該檔案實現了用戶端和服務端的代理(proxy)和存根(stub)

3.1 服務端

服務端代碼中調用ICalcAIDL.Stub

 

private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub(){ @Overridepublic int add(int x, int y) throws RemoteException{return x + y;} @Overridepublic int min(int x, int y) throws RemoteException{return x - y;} };

而在CalcService.java中,很明顯Stub就是Binder的子類

 

 

public static abstract class Stub extends android.os.Binder implements com.realize.calc.aidl.ICalcAIDL

接著看看Stub下面的方法onTransact,該方法內部實現了加減法的操作
@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_add:{data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.add(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}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;}}return super.onTransact(code, data, reply, flags);}服務端會根據用戶端發送來的訊息執行onTransact方法,該方法有四個參數code 是一個整形的唯一標識,用於區分執行哪個方法data 用戶端傳遞過來的參數reply 伺服器返回的值flags 標明是否有傳回值,0為有(雙向),1為沒有(單向)

 

3.2 用戶端

用戶端代碼中,調用了ICalcAIDL.Stub.asInterface

 

private ServiceConnection mServiceConn = new ServiceConnection(){@Overridepublic void onServiceDisconnected(ComponentName name){Log.e(TAG, "onServiceDisconnected");mCalcAidl = null;} @Overridepublic void onServiceConnected(ComponentName name, IBinder service){Log.e(TAG, "onServiceConnected");mCalcAidl = ICalcAIDL.Stub.asInterface(service);}};

而ICalcAIDL.java檔案中,asInterface,最終是調用到Proxy

 

 

public static com.realize.calc.aidl.ICalcAIDL asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.realize.calc.aidl.ICalcAIDL))) {return ((com.realize.calc.aidl.ICalcAIDL)iin);}return new com.realize.calc.aidl.ICalcAIDL.Stub.Proxy(obj);}

所以,我們看下Proxy的add方法
@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 {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(x);_data.writeInt(y);mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;}聲明了兩個android.os.Parcel對象,_data用於傳遞資料,_replay用於接收返回的資料最後調用transact方法,與服務端進行通訊


 

四、不依賴AIDL的樣本

 

怎樣不通過AIDL檔案,實現跨進程通訊呢,從上面的分析中,我們可以知道,不通過AIDL檔案,實現跨進程通訊,那麼實際上,就是要實現自動產生的AIDL檔案中的介面功能, 下面我們實現跨進程的乘除調用

4.1 服務端

建立工程,實現CalcService.java,代碼如下

package com.realize.lizijun.noaidl_binder_server; import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.Parcel;import android.os.RemoteException;import android.util.Log; public class CalcService extends Service{private static final String DESCRIPTOR = "CalcService";private static final String TAG = "server"; public void onCreate(){Log.e(TAG, "onCreate");} @Overridepublic int onStartCommand(Intent intent, int flags, int startId){Log.e(TAG, "onStartCommand");return super.onStartCommand(intent, flags, startId);} public IBinder onBind(Intent t){Log.e(TAG, "onBind");return mBinder;} public void onDestroy(){Log.e(TAG, "onDestroy");super.onDestroy();} public boolean onUnbind(Intent intent){Log.e(TAG, "onUnbind");return super.onUnbind(intent);} public void onRebind(Intent intent){Log.e(TAG, "onRebind");super.onRebind(intent);} private MyBinder mBinder = new MyBinder(); private class MyBinder extends Binder{@Overrideprotected boolean onTransact(int code, Parcel data, Parcel reply,int flags) throws RemoteException{switch (code){case 0x110:{Log.e(TAG, "0x110");data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = _arg0 * _arg1;reply.writeNoException();reply.writeInt(_result);return true;}case 0x111:{Log.e(TAG, "0x111");data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = _arg0 / _arg1;reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);} }; }自訂了一個Binder子類,然後複寫了其onTransact方法

AndroidManifest.xml中註冊服務
        <service android:name="com.realize.lizijun.noaidl_binder_server.CalcService">            <intent-filter>                <action android:name="com.realize.noaidl.calc">                <category android:name="android.intent.category.DEFAULT">            </category></action></intent-filter>        </service>
4.2 用戶端

建立工程,其主activity內容如下所示:

 

package com.realize.lizijun.noaidl_binder_client; import android.app.Activity;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;import android.view.View;import android.widget.Toast; public class MainActivity extends Activity{private static final String TAG = "client";private IBinder mPlusBinder = null;private ServiceConnection mServiceConnPlus = new ServiceConnection(){// 只有當servie異常退出時,系統才會調用onServiceDisconnected()@Overridepublic void onServiceDisconnected(ComponentName name){Log.e(TAG, "mServiceConnPlus onServiceDisconnected");mPlusBinder = null;} @Overridepublic void onServiceConnected(ComponentName name, IBinder service){ Log.e(TAG, " mServiceConnPlus onServiceConnected");mPlusBinder = service;}}; @Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); } public void bindService(View view){Intent intentPlus = new Intent();intentPlus.setAction("com.realize.noaidl.calc");boolean result = bindService(intentPlus, mServiceConnPlus,Context.BIND_AUTO_CREATE);Log.e(TAG, result + "");} public void unbindService(View view){if (mServiceConnPlus != null){Log.e(TAG, "unbindService");unbindService(mServiceConnPlus);//mServiceConnPlus = null;}} public void mulInvoked(View view){ if (mPlusBinder == null){Toast.makeText(this, "未串連服務端或服務端被異常殺死", Toast.LENGTH_SHORT).show();} else{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result = 0;try{_data.writeInterfaceToken("CalcService");_data.writeInt(50);_data.writeInt(12);mPlusBinder.transact(0x110, _data, _reply, 0);_reply.readException();_result = _reply.readInt();Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show(); } catch (RemoteException e){e.printStackTrace();} finally{_reply.recycle();_data.recycle();}} } public void divInvoked(View view){ if (mPlusBinder == null){Toast.makeText(this, "未串連服務端或服務端被異常殺死", Toast.LENGTH_SHORT).show();} else{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result = 0;try{_data.writeInterfaceToken("CalcService");_data.writeInt(36);_data.writeInt(12);mPlusBinder.transact(0x111, _data, _reply, 0);_reply.readException();_result = _reply.readInt();Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show(); } catch (RemoteException e){e.printStackTrace();} finally{_reply.recycle();_data.recycle();}} }}

介面如下:

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.