Android學習小Demo(23)Aidl實現處理序間通訊

來源:互聯網
上載者:User

標籤:android   aidl   binder   demo   實現   

我們知道,Android是靠Binder機制來實現進程間的通訊,而上一篇文章中,我們利用AIDL,簡單地從代碼方面的角度講解了在服務端中的Binder的存在形式,是以服務的實現存在的,而在用戶端,則是以代理的形式,實現存在的只是一個關於服務端的Binder實現的引用。

理論上的東西我們要去學習掌握,但是也不能忽略了實際的動手能力,對吧。

今天,我們就一步一步地利用我們所瞭解地關於AIDL的知識來實現一個跨進程通訊的例子。

在Android的上層應用中,每一個App都是一個單獨的進程,所以,要實現跨進程通訊,我們需要至少有2個進程,一個代表格服務端,一個代表用戶端,所以我們會建立2個項目,來分別代表格服務端和用戶端。

服務端是提供服務的,得先提供服務,才能讓用戶端來享受服務,對吧。

服務端

我們建立一個服務端的項目,然後建立我們的aidl檔案,畢竟只是一個Demo,所以我先從簡單入手,方法不要太多,就叫IDemoService.aidl, 如下:

package com.lms.service;interface IDemoService {void invoke();}

我們將其放在com.lms.service的包名目錄下,表明其是提供服務的,當我們編譯一下項目之後,就會發現在gen目錄下面同樣的包名下,產生了一個IDemoService.java的檔案,如下:


而其產生的IDemoService.java檔案內容如下,跟上一篇文章中所展現的其實一樣,就是一個Stub類和一個Proxy類,如下:

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: F:\\workspace_android\\AidlDemoClient\\src\\com\\lms\\service\\IDemoService.aidl */package com.lms.service;public interface IDemoService extends android.os.IInterface {/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implementscom.lms.service.IDemoService {private static final java.lang.String DESCRIPTOR = "com.lms.service.IDemoService";/** Construct the stub at attach it to the interface. */public Stub() {this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.lms.service.IDemoService * interface, generating a proxy if needed. */public static com.lms.service.IDemoService asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.lms.service.IDemoService))) {return ((com.lms.service.IDemoService) iin);}return new com.lms.service.IDemoService.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic 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_invoke: {data.enforceInterface(DESCRIPTOR);this.invoke();reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.lms.service.IDemoService {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic void invoke() throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_invoke, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}}static final int TRANSACTION_invoke = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public void invoke() throws android.os.RemoteException;}

既然已經通過aidl產生了這份介面檔案,那麼根據上一篇文章所講的,我們接下來就要來實現服務端這邊提供的服務了。

我們可以想到,當我們在使用Android中的服務介面的時候,當其需要與其他模組進行相互連信的時候,我們一般是會通過綁定的形式實現的,而通過此種形式的實現,我們都會實現一個Binder,比如我們之前音樂播放器的實現,如下:

class NatureBinder extends Binder{/** * 唱吧,有人想聽 */public void startPlay(int currentMusic, int currentPosition){}/** * 別唱了 */public void stopPlay(){}/** * 後一首 */public void toNext(){}/** * 前一首 */public void toPrevious(){}/** * 有人改變模式了,我得把它記下來  */public void changeMode(){}/** * 告訴別人,你現在到底是順序播放,還是隨機亂彈 * MODE_ONE_LOOP = 1; * MODE_ALL_LOOP = 2; * MODE_RANDOM = 3; * MODE_SEQUENCE = 4;  * @return */public int getCurrentMode(){}/** * 告訴調用者,到底有沒有在做事。。。 * @return */public boolean isPlaying(){}/** * 要告訴調用者,當前播哪首歌了,歌多長啊 */public void notifyActivity(){}/** * 有人拖動Seekbar了,要告訴service去改變播放的位置 * @param progress */public void changeProgress(int progress){}}

在上面這種情況下,Service是與同一個進程中的其他線程或者模組進行通訊的,而在我們這種跨進程的服務中是不是也可以類似自己去實現我們AIDL中的定義的Stub類呢?

因為Stub類也是繼承Binder的,而Binder又是實現了IBinder介面的,所以可以在調用Service的onBinde方法的時候,就返回我們實現的服務呀,對吧。

所以我們自訂一個服務,就叫做DemoService,如下:

package com.lms.service;import android.app.Service;import android.content.Intent;import android.os.IBinder;public class DemoService extends Service{@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn null;}}

Service是一個抽象類別,繼承它必須實現其抽象方法 onBinde,其會返回一個IBinder對象,那麼很顯然,我們就需要在這個方法裡返回我們的Binder了。

下面,我們先簡單實現我們的Binder,如下:

public class DemoService extends Service {private static final String TAG = "DemoService";public final IDemoService.Stub mBinder = new IDemoService.Stub() {@Overridepublic void invoke() throws RemoteException {Log.v(TAG, "Invoke -> Current Process From Server: " + android.os.Process.myPid());}};@Overridepublic IBinder onBind(Intent intent) {Log.v(TAG, "Bind -> Current Process From Server: " + android.os.Process.myPid());return mBinder;}public boolean onUnbind(Intent intent){Log.v(TAG, "Unbind -> Current Process From Server: " + android.os.Process.myPid());return super.onUnbind(intent);}}

最後,在AndroidManifest.xml中註冊Service,如下:

        <service            android:name="com.lms.service.DemoService"            android:exported="true"            android:process=":remote" >            <intent-filter>                <category android:name="android.intent.category.DEFAULT" />                <action android:name="com.lms.service.DemoService" />            </intent-filter>        </service>

在這裡,我們還需要定義一個intent-filter,以便Android系統在第三方調用的時候,可以通過隱式的intent來匹配到這個服務,同時還要注意兩點:

1)android:process 必須設定值,至於什麼值,好像關係並不大,表明可接受遠程調用

2)android:exported設定為true,這表明此組件可供第三方調用,不過對於Service來說,這預設就是true的,所以我們不設也可以。但是對於Activity等來說,其值是預設為false的,這時候,如果我們想讓我們的Activity為第三方調用,就要顯式設定為true了。通常,在接入第三方外掛程式的時候,我們會用到這個屬性。

OK,到這裡為止,我們已經簡單地實現了我們的服務端服務了,接下來我們再繼續實現我們用戶端。

再建立一個項目,AidlDemoClient,然後將我們在服務端的這份aidl檔案,包括包名等,複製到用戶端代碼中,編譯一下,其會產生一份一樣的java檔案,不過此時,我們不再需要去實現Stub類了,我們只是需要去調用這個類就可以了,如下:

接著,我們只需要像平常一樣調用我們的Service就可以了,所不同的是,由於這服務並不在我們進程內,所以我們必須用隱式的Intent去調用,由系統去匹配服務,從而調起,這也是為什麼我們在服務端註冊Service的時候,需要設定IntentFilter的原因了。

public class MainActivity extends ActionBarActivity {private static final String TAG = "DemoService";private static final String ACTION_BIND_SERVICE = "com.lms.service.DemoService";private IDemoService mDemoService;private ServiceConnection mServiceConnection = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {mDemoService = null;}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mDemoService = IDemoService.Stub.asInterface(service);try {mDemoService.invoke();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button btnHelloWorld = (Button) findViewById(R.id.btnBind);btnHelloWorld.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intentService = new Intent(ACTION_BIND_SERVICE);intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);Log.v(TAG, "Current Process From Client: " + android.os.Process.myPid());}}); Button btnUnbind = (Button) findViewById(R.id.btnUnbind);btnUnbind.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if(mServiceConnection != null){unbindService(mServiceConnection);mServiceConnection = null;}}});}

我們定義了兩個按鈕,當點擊的時候,分別通過隱式的intent去啟動服務並觸發方法和解除綁定,我們可以看到在onServiceConnected中,我們利用了IDemoService.Stub.asInterface() 方法來執行個體化這個Binder,至於asterface是如何跨進程找到這個binder的,我們上一篇文章中已經簡單地講了一下。

接下來,我們可以啟動這兩個應用,來試一下啦。

從上面的畫面中,我們可以看到,點擊事件發生的線程ID是685,而真正接受服務和觸發Invoke方法的是在進程676上面,於是我們就實現了跨進程通訊了。

當然在這裡,我們只是做了一個很簡單的調用而已,而實際上我們還能夠傳遞參數和接受返回值,而這些我們都可以在aidl檔案中定義,不過要注意的是:

1)aidl中直接提供的資料類型只支援基本的資料類型和String

2)如果要支援我們自己的自訂的對象,我們的對象必須也通過aidl來定義,而且實現parceable介面,如下:

package com.lms.aidl;import java.util.List;import com.lms.aidl.Bean;interface ITestService {List<Bean> getBean();void addBean(in Bean bean);}

而Bean的aidl檔案如下:

package com.lms.aidl;parcelable Bean;

其具體實現如下:

package com.lms.aidl;import android.os.Parcel;import android.os.Parcelable;public class Bean implements Parcelable {private int i;private String str;public Bean(){}/** * @return the i */public int getI() {return i;}/** * @param i the i to set */public void setI(int i) {this.i = i;}/** * @return the str */public String getStr() {return str;}/** * @param str the str to set */public void setStr(String str) {this.str = str;}public Bean(Parcel in) {readFromParcel(in);}public static final Parcelable.Creator<Bean> CREATOR = new Parcelable.Creator<Bean>() {public Bean createFromParcel(Parcel in) {return new Bean(in);}public Bean[] newArray(int size) {return new Bean[size];}};@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(i);dest.writeString(str);}public void readFromParcel(Parcel in) {i = in.readInt();str = in.readString();}}

而我們可以從其通訊的過程中拿到服務端的資料,如下:


好了,關於aidl的使用,我們就簡單地介紹到這裡吧,我會把兩份Demo的代碼都放上來,大家如果有興趣,可以自己拿去參考一下。

簡單版(只是調用方法):AidlDemo簡單版

進階版(處理序間通訊並且傳遞自訂對象):AidlDemo進階版


Android學習小Demo(23)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.