Android IPC 之 AIDL(一)

來源:互聯網
上載者:User

標籤:

IPC是Inter-Process Communication的縮寫,即跨進程通訊。Android中跨進程通訊有多種方式,如檔案分享權限設定、使用ContentProviderBroadcast、和Socket等。比較複雜的情況下,常用的兩種方式為MessengerAIDL,而Messenger的底層實現又是AIDL。

首先不看別的,先來看一下AIDL是如何使用的。

假設我們現在有一個兩數相加的任務,用戶端沒辦法完成(別問我它為什麼完不成==,咱舉栗子簡單點哈~),需要將任務交給另一個進程中的服務端完成,再從服務端擷取到該任務的結果。

我們首先如方式建立一個AIDL介面:

Android Studio會自動為它產生一個路徑,如:

在該檔案中聲明一個介面以及一個我們想讓服務端實現的介面方法。如下:

package com.vera.aidltest;interface IMyAdd {   int myAdd(int num_a,int num_b);}

注意,並不是所有資料類型都能在AIDL檔案中使用,AIDL檔案只支援以下幾種資料類型:

  • Java 中的基礎資料型別 (Elementary Data Type)
  • String 和CharSequence
  • List 和 Map ,且List和Map 對象的元素必須是AIDL支援的資料類型
  • AIDL 自動產生的介面 ,需要匯入(import,即使同處於一個包中)
  • 實現android.os.Parcelable 介面的類的對象. 需要匯入(import,即使同處於一個包中),且必須建立一個與其同名的AIDL檔案,並在檔案中聲明該類為Parcelable

在介面定義好後,系統將為我們產生一個Java檔案,AS下是在app\build\generated\source\aidl\debug目錄下,產生的程式碼如下:

package com.vera.aidltest;public interface IMyAdd extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.vera.aidltest.IMyAdd{private static final java.lang.String DESCRIPTOR = "com.vera.aidltest.IMyAdd";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.vera.aidltest.IMyAdd interface, * generating a proxy if needed. */public static com.vera.aidltest.IMyAdd asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.vera.aidltest.IMyAdd))) {return ((com.vera.aidltest.IMyAdd)iin);}return new com.vera.aidltest.IMyAdd.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_myAdd:{data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.myAdd(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.vera.aidltest.IMyAdd{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;}@Override public int myAdd(int num_a, int num_b) 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(num_a);_data.writeInt(num_b);mRemote.transact(Stub.TRANSACTION_myAdd, _data, _reply, 0);_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_myAdd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public int myAdd(int num_a, int num_b) throws android.os.RemoteException;}

代碼很長很淩亂的樣子……嗯,本節我們先不看它,只需要知道它是根據AIDL檔案產生的一個介面IMyAdd,包含一個繼承自Binder的靜態內部抽象類別Stub(咦,這麼連起來說總有哪裡怪怪的……),並且聲明了myAdd()方法。為什麼是抽象類別呢,因為它實現了IMyAdd介面卻並沒有真正實現,那放到哪裡實現呢?當然是我們的服務端咯。

接下來,我們可以就可以來寫用戶端和服務端的代碼了,那麼,我們這裡的用戶端和服務端指的是什麼呢?就本例來說,它們分別是一個Activity和一個Service,這裡我們將它們放在了同一個應用中,只不過通過某種方法使其運行在不同的進程。更多情況下它們並不運行在同一個應用中,這時候我們需要將整個aidl檔案夾的內容複寫一份,這是因為用戶端和服務端的AIDL包結構需要保持一致,否則將會出現還原序列化不成功的結果,那麼跨進程通訊將無法進行。

那麼我們現在開始寫服務端的代碼,建立一個Service名為ServerService如下:

package com.vera.aidltest;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;public class ServerService extends Service {   private Binder mBinder=new IMyAdd.Stub(){       @Override       public int myAdd(int num_a, int num_b) throws RemoteException {           int result=num_a+num_b;           Log.d("ServerService","the result of "+num_a+" and "+num_b+" is "+result);           return result;       }   };    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }}

ServerService很簡單,它只是建立了一個Binder對象並在onBinder()方法中將其返回。該Binder對象就是我們實現了介面方法的Stub對象。好啦,現在我們可以在myAdd()方法中愉快地進行我們的操作啦。在這裡我們只是得到num_a和num_b的和並將其列印,最後再返回結果。

另外,我們需要將該Service設定在一個獨立的進程中,不然還怎麼玩跨進程通訊~
更改AndroidManifest.xml如下:

<service            android:name=".ServerService"            android:process=":remote"></service>

好了,服務端的建立已經完成啦,我們現在來看用戶端。用戶端要做些什麼呢?,我們來看代碼:

package com.vera.aidltest;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.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.Button;public class ClientActivity extends AppCompatActivity {    Button mButton;    private ServiceConnection myConnection=new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {            IMyAdd mMyAdd=IMyAdd.Stub.asInterface(iBinder);            try {                int result=mMyAdd.myAdd(1,2);                Log.d("ClientActivity","get the result is "+result);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName componentName) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_client);        mButton=(Button)findViewById(R.id.activity_client_mbutton);        mButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Intent intent=new Intent(ClientActivity.this,ServerService.class);                bindService(intent,myConnection, Context.BIND_AUTO_CREATE);            }        });    }    @Override    protected void onDestroy(){        unbindService(myConnection);        super.onDestroy();    }}

首先,我們建立了一個ServiceConnection的匿名類並執行個體化一個對象,然後,我們又設定了在Button點擊之後通過bindService()方法綁定服務。bindService()方法接收三個參數,第一個是在這之前我們建立的Intent對象,第二個是ServiceConnection的執行個體對象,第三個參數是一個標誌位,BIND_AUTO_CREATE表示在Activity和Service進行綁定後,服務將自動建立。然後我們重寫了onDestroy()方法,將Activity和Service解除綁定。

重點是在建立的ServiceConnection匿名類裡!在這個類裡我們重寫了onServiceConnected()方法和onServiceDisconnected()方法,這兩個方法分別會在Activity與Service成功綁定和解除綁定的時候調用。在onServiceConnected()方法裡,我們調用了Stub()的asInterface()方法,該方法返回一個Binder代理對象,並向上轉型成為用戶端接受的AIDL介面類型的對象!

好啦,拿到了這個對象,現在只差一步調用方法的事啦,我們來試一試1+2等於多少叭~

運行程式,在點擊Button之後,查看日誌列印資訊,如:

在com.vera.aidltest進程中,用戶端列印出得到的結果為3,在com.vera.aidltest:remote進程中,服務端列印出1+2等於3(我英語不好……),通訊成功啦!

嗯好,那我們來總結一下,具體的步驟吧:

  • 首先建立一個AIDL檔案,在其中定義一個介面,介面中應包含我們希望服務端實現的方法的聲明。
  • 其次,在服務端中將實現好方法的Stub對象通過onBind()方法返回
  • 最後,在用戶端中將服務綁定,並重寫ServiceConnection類中的方法,在其中獲得Binder對象,調用服務端的介面方法。

這其中我們屏蔽了很多細節,只談了使用方法,下一節婷子會把更具體的部分再貼出來,第一篇技術部落格,有什麼不對的地方還請留言哦,麼麼噠~

Android IPC 之 AIDL(一)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.