Android學習筆記二十六.跨進程調用Service(AIDL Service),androidaidl
跨進程調用Service(AIDL Service)一、AIDL Service1.什麼是AIDL Service? AIDL,即Android Interface Definition Language.是Android用於定義遠程介面,AIDL介面定義語言的文法比較簡單,這種介面定義語言並不是真正的程式設計語言,它只是定義兩個進程之間的通訊介面。AIDL的文法與Java介面很相似,但存在如下幾點差異:(1)AIDL定義介面的原始碼必須以.aidl結尾;(2)AIDL介面中用到資料類型,除了基本類型、String、List、Map、CharSequence之外,其他類型全部都需要導包,即使他們在同一個包中也需要導包。 在Android系統中,由於各個應用程式都是運行在自己的進程中(啟動一個應用即啟動一個進程),進程之間一般無法直接進行資料交換。所以,為了實現進程之間的通訊(interprocess communiocation,簡稱IPC),Android提供了AIDL Service。2.遠程Service 與本地Service相對應的是遠程Service,即一個應用程式(進程)可以訪問另一個應用程式的Service組件前者我們稱之為用戶端、後者為服務端,而用戶端訪問服務端就是通過AIDL介面實現彼此間通訊的。通過之前幾篇文章我們知道,用戶端訪問Service時,Android並不是直接返回Service對象給用戶端,而是將Service的代理對象(IBinder對象)通過Service的onBind()方法返回給用戶端,因此,Android的AIDL遠程介面的實作類別就是那個IBinder實作類別。當用戶端擷取了遠程Service的IBinder對象的代理之後,用戶端進程就可通過該IBinder對象去回調遠程Service的屬性或方法,從而實現進程間的通訊。3.Service返回的IBinder對象 當訪問者(用戶端)調用bindService()方法來綁定與啟動指定的Service,對於綁定本地Service還是綁定遠程Service,Service返回的IBinder對象是有所區別的。(1)綁定本地Service:本地Service的onBind()方法會直接把IBinder對象本身傳給用戶端的ServiceConnection對象的onServiceConnected方法的第二個參數;(2)綁定遠程Service:遠程Service的onBind()方法只是將IBinder對象的代理傳給用戶端的ServiceConnection對象的onServiceConnected方法的第二個參數。4.AIDL介面工作原理 當在工程中實現了一個.aidl的AIDL介面源檔案(如/src/com/android_aidlservice/ICat.aidl),我們可以通過兩種方式產生需要的ICat.java介面檔案:一是使用Android SDK安裝目錄下的platform-tools子錄下的aidl.exe工具;二是ADT工具自動為該AIDL介面產生實現(路徑:gen/com.example.android_aidlservice/ICat.java)。ICat.java介面包含一個Stub內部類,該內部類實現了IBinder、ICat兩個介面,這個Stub類將會作為遠程Service的回調類---它實現了IBinder介面,因此可作為Service的onBind()方法的返回值。 二、處理序間通訊開發思路1.服務端-遠程Service開發(1)定義一個AIDL介面,檔案名稱為xxx.aidl,儲存到源碼所在的路徑,如/src/com/android_aidlservice/ICat.aidl;package com.example.android_aidlservice;interface ICat{ String getColor(); double getWeight();}其中,com.example.android_aidlservice為其所在的包名。(2)定義一個Service實作類別,並實現一個內部類繼承於Stub即實現了ICat介面,也就實現了IBinder介面。該Service的onBind()方法所返回的IBinder對象為ICat.Stub的子類的執行個體。private CatBinder catBinder;public class CatBinder extends Stub{ ......}@Overridepublic IBinder onBind(Intent arg0){ return catBinder;}(3)在AndroidMainfest.xml工程檔案中配置該Service <application .... > <!--定義一個Service組件--> <service android:name=".AidlService"> <intent-filter> <action android:name="com.example.sercie.AIDL_SERVICE"></action> </intent-filter> </service> </application>2.用戶端開發思路(1)定義一個AIDL介面,檔案名稱為xxx.aidl,儲存到源碼所在的路徑,如/src/com/android_aidlservice/ICat.aidl;package com.example.android_aidlservice;interface ICat{ String getColor(); double getWeight();}其中,com.example.android_aidlservice為其所在的包名,該.aidl檔案為從Service段的AIDL介面檔案複製到用戶端應用中。(2)建立ServiceConnection對象,並在ServiceConnection對象的onServiceConnected方法中添加如下代碼: catService = ICat.Stub.asInterface(service);其中,catService為Service的onBind()方法返回IBinder對象的代理。(3)以ServiceConnection對象作為參數,調用Context的bindService()方法綁定並啟動遠程Service即可。三、源碼實現1.服務端(1)定義一個Service實作類別-/src/com/example/android_service/AidlService.java
package com.example.android_aidlservice;import java.util.Timer;import java.util.TimerTask;import com.example.android_aidlservice.ICat.Stub;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;public class AidlService extends Service { //1.聲明一個IBinder對象、Timer對象 private CatBinder catBinder; private Timer timer; //2.定義兩個數組與兩個私人變數 private String color; private double weight; String[] colors = new String[]{"黑色","黃色","紅色"}; double[] weigths=new double[]{2.3,3.1,1.58}; //3.繼承stub,也就實現了ICat介面,並實現了IBinder介面 public class CatBinder extends Stub { @Override public String getColor() throws RemoteException { return color; } @Override public double getWeight() throws RemoteException { return weight; } } //4.當Service建立時調用該方法 @Override public void onCreate() { super.onCreate(); //a.執行個體化一個CatBinder對象 catBinder = new CatBinder(); //b.該service完成的任務:隨機地改變Service組件內color、weight屬性的值 timer.schedule(new TimerTask(){ @Override public void run() { int rand=(int)(Math.random()*3); color=colors[rand]; weight = weigths[rand]; System.out.println("-----------"+rand); } }, 0, 800); } /*5.訪問者使用bindService()方法啟動Service時,該方法用於返回catBinder對象 * (1)在綁定本地Service的情況下,該catBinder對象會直接傳給用戶端的ServiceConnection對象; * (2)在綁定遠程Service的情況下,只將catBinder對象的代理傳遞給用戶端的ServiceConnection對象的 * onServiceConnection方法的第二個參數*/ @Override public IBinder onBind(Intent intent) { return catBinder; } //6.回調該方法關閉Service @Override public void onDestroy() { timer.cancel(); }}
(2)在AndroidManifest檔案中配置該Service
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android_aidlservice" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <service android:name=".AidlService" android:exported="true"> <intent-filter> <action android:name="com.example.service.AIDL_SERVICE"/> </intent-filter> </service> </application></manifest>
2.用戶端(1)定義一個Activity實作類別-/src/com/example/android_service/AidlClient.java
package com.example.android_aidl_service;import android.app.Activity;import android.app.Service;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;public class AidlClient extends Activity { private ICat catService;//聲明一個ICat對象 private Button get; private EditText color,weight; //1.建立一個ServiceConnection對象 private ServiceConnection conn=new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service) { //擷取遠程Service的onBind方法的對象的代理 catService = ICat.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { catService=null; } }; //2.綁定遠程Service並擷取其內容 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //a.擷取Activity主介面組件 setContentView(R.layout.main); get=(Button)findViewById(R.id.get); color=(EditText)findViewById(R.id.color); weight=(EditText)findViewById(R.id.weight); //b.建立所需綁定的Service的Intent並設定其Action屬性(即指定啟動哪個遠程Service) Intent intent=new Intent(); intent.setAction("com.example.service.AIDL_SERVICE"); //c.綁定並啟動遠程Service bindService(intent,conn,Service.BIND_AUTO_CREATE); //d.通過IBinder對象的代理擷取遠程Service資料 get.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { try { color.setText(catService.getColor()); weight.setText(catService.getWeight()+""); } catch (RemoteException e) { e.printStackTrace(); } } }); } //3.退出Activity時調用該方法,解除遠程Service的綁定 @Override protected void onDestroy() { super.onDestroy(); this.unbindService(conn); }}
(2)在AndroidManifest檔案中配置該Activity
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android_aidl_service" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".AidlClient" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
效果:運行用戶端程式,單擊程式介面中"擷取遠程Service的資料"按鈕,就會實現用戶端讀取遠程Service功能。