本例和下個例子Remote Service Controller 涉及到的文件有RemoteService.java ,IRemoteService.aidl, IRemoteServiceCallback.aidl 及ISecondary.aidl。html
Android Interface Definition Language(AIDL)和其它一些支持遠程方法調用RMI的系統的IDL相似,它定義了Service和Client 之間的使用接口約定,這種遠程調用通常須要經過進程間通訊機制(IPC)來實現。在Android系統,一個進程(Process)一般不能直接訪問其它 進程的內存空間,Android系統支持使用AIDL來實現使用不一樣進程間調用服務接口。java
在設計AIDL接口以前,要意識到使用AIDL接口是經過直接函數調用的方法來進行的,但這種遠程調用所發生的線程Thread隨調用者是否和Service提供者屬於同一進程Process的不一樣而不一樣:android
定義AIDL 接口的步驟以下:api
AIDL接口定義使用Java Interface語法定義在 .aidl文件中,而後必須同時放在Service和Client 的 src目錄下。 當使用Eclipse 編譯時,Android SDK工具會根據 .aidl的接口定義自動生成對應的IBinder接口定義 (定義在gen目錄下) Service必須實現由這個IBinder接口定義。 Client而後能夠經過綁定Service來訪問這些方法。app
1. 建立. aidl 文件異步
AIDL接口定義使用和Java Interface定義一樣的語法,每一個.aidl文件只能定義一個調用接口,並且只能定義接口方法,不能含有靜態變量定義。AIDL缺省支持 int ,long, char, boolean, String, CharSequence, List ,Map 變量類型,也能夠引用其它 .aidl中定義的類型。ide
下面是IRemoteService.aidl 的定義:wordpress
package com.example.android.apis.app; import com.example.android.apis.app.IRemoteServiceCallback; /** * Example of defining an interface for calling on to a remote service * (running in another process). */ interface IRemoteService { /** * Often you want to allow a service to call back to its clients. * This shows how to do so, by registering a callback interface with * the service. */ void registerCallback(IRemoteServiceCallback cb); /** * Remove a previously registered callback interface. */ void unregisterCallback(IRemoteServiceCallback cb);<br /> }
編譯時,Android SDK 工具自動在gen目錄下生成對應的 IRemoteService.java。函數
2. 實現這個AIDL接口工具
編譯時,Android SDK 工具自動在gen目錄下生成對應的 IRemoteService.java,這個文件中會定義一個子類Stub (IRemoteService.Stub),Stub定義了由.aidl 定義的抽象方法實現。除此以外,Stub還定義了幾個Help 方法,好比asInterface() 參數爲IBinder對象(一般由Client中的onServiceConnected()傳入),返回一個Stub實例供Client使用。
下面是IRemoteService.Stub 在 RemoteService中的實現:
/** * The IRemoteInterface is defined through IDL */ private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public void registerCallback(IRemoteServiceCallback cb) { if (cb != null) mCallbacks.register(cb); } public void unregisterCallback(IRemoteServiceCallback cb) { if (cb != null) mCallbacks.unregister(cb); } };
3. Expose the interface to Clients
在定義.aidl 和實現AIDL接口定義後,就須要將這個接口提供給Client使用。方法是派生Service並提供onBind方法返回上面實現的Stub的一個實例。
public class RemoteService extends Service { ... @Override public IBinder onBind(Intent intent) { // Select the interface to return. If your service only implements // a single interface, you can just return it here without checking // the Intent. if (IRemoteService.class.getName().equals(intent.getAction())) { return mBinder; } if (ISecondary.class.getName().equals(intent.getAction())) { return mSecondaryBinder; } return null; }
RemoteService 中定義了兩個.aidl 接口可供Client使用IRemoteService.aidl和ISecondary.aidl。
有了AIDL定義並在Service中定義了可供Client使用的AIDL實現。下面再來看看Client的實現步驟:
Remote Service Binding 例子中 Service 端實現了兩個Service:
/** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mService = IRemoteService.Stub.asInterface(service); mKillButton.setEnabled(true); mCallbackText.setText("Attached."); // We want to monitor the service for as long as we are // connected to it. try { mService.registerCallback(mCallback); } catch (RemoteException e) { // In this case the service has crashed before we could even // do anything with it; we can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. } // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mKillButton.setEnabled(false); mCallbackText.setText("Disconnected."); // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_disconnected, Toast.LENGTH_SHORT).show(); } };
在RemoteService 的主線程中定義了一個mHandler,它每隔1秒鐘將value值加1,並經過IRemoteServiceCallback 將這個新值發給註冊過的Client。
// Up it goes. int value = ++mValue; // Broadcast to all clients the new value. final int N = mCallbacks.beginBroadcast(); for (int i=0; i<N; i++) { try { mCallbacks.getBroadcastItem(i).valueChanged(value); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing // the dead object for us. } } mCallbacks.finishBroadcast(); // Repeat every 1 second. sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
當運行這個示例時,若是沒有事先運行Remote Service Controller 的start Service ,這個值會從1開始,每隔1秒鐘加1.
再來看看Client如何綁定Service的。
bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE); bindService(new Intent(ISecondary.class.getName()), mSecondaryConnection, Context.BIND_AUTO_CREATE);
注意這裏的Context.BIND_AUTO_CREATE,這意味這若是在綁定的過程當中,若是Service因爲某種緣由被Destroy 了,Android還會自動從新啓動被綁定的Service。你能夠點擊Kill Process 殺死Service看看結果 :-)。
private OnClickListener mKillListener = new OnClickListener() { public void onClick(View v) { // To kill the process hosting our service, we need to know its // PID. Conveniently our service has a call that will return // to us that information. if (mSecondaryService != null) { try { int pid = mSecondaryService.getPid(); // Note that, though this API allows us to request to // kill any process based on its PID, the kernel will // still impose standard restrictions on which PIDs you // are actually able to kill. Typically this means only // the process running your application and any additional // processes created by that app as shown here; packages // sharing a common UID will also be able to kill each // other's processes. Process.killProcess(pid); mCallbackText.setText("Killed service process."); } catch (RemoteException ex) { // Recover gracefully from the process hosting the // server dying. // Just for purposes of the sample, put up a notification. Toast.makeText(Binding.this, R.string.remote_call_failed, Toast.LENGTH_SHORT).show(); } } } };
在RemoteService.Binding 中也定義了一個mHandler,是用來顯示從Service發回的當前的Value值,Servcie發回Client的接口 IRemoteServiceCallback 一樣也須要對應的AIDL定義。使用mHandler是由於valueChanged 須要更新UI。Android 系統Handler用法簡介。
本例按unBind Service 會解除與Service之間的綁定,一樣若是沒有事先運行Remote Service Controller 的start Service,這個操做會致使Destory Service,由於」Bound」 Service 在沒有與之綁定的Service是自動退出,下次綁定時,Value又會從1開始計數。
此外本例中用到了Android.os.RemoteCallbackList 這個類是爲了方便管理Remote調用時向Client發送Callback而提供的,具體不詳述了,能夠參見http://developer.android.com/reference/android/os/RemoteCallbackList.html