Android AIDL的用法
一、什麼是AIDL服務
二、AIDL基本語法
AIDL使用簡單的語法來聲明接口,描述其方法以及方法的參數和返回值。這些參數和返回值可以是任何類型,甚至是其他AIDL生成的接口。
其中對於Java編程語言的基本數據類型 (int, long, char, boolean,String,CharSequence)集合接口類型List和Map,不需要import 語句。
而如果需要在AIDL中使用其他AIDL接口類型,需要import,即使是在相同包結構下。AIDL允許傳遞實現Parcelable接口的類,需要import
需要特別注意的是,對於非基本數據類型,也不是String和CharSequence類型的,需要有方向指示,包括in、out和inout,in表示由客戶端設置,out表示由服務端設置,inout是兩者均可設置。
AIDL只支持接口方法,不能公開static變量。
三、android應用層使用AIDL
3.1、簡要步驟
1、在Eclipse Android工程的Java包目錄中建立一個擴展名爲A.aidl的文件,並寫下需要的接口。如果aidl文件的內容是正確的,ADT會在gen目錄下自動生成一個A.Java接口文件。
2、建立一個服務類(Service的子類)。並在創建的服務類中創建一個內部類,實現由aidl文件生成的Java接口。
3、在服務類的onBind方法返回時,將實現aidl接口的內部類對象返回出去。
4、在AndroidManifest.xml文件中配置AIDL服務,尤其要注意的是,<action>標籤中android:name的屬性值就是客戶端要引用該服務的ID,也就是Intent類的參數值。
3.2、具體操作
3.2.1、創建文件IMyService.aidl:
文件內容:
- package du.pack;
- interface IMyService{
- //只有一個接口
- String getValue();
- }
3.2.2、創建服務類及實現內部類
- public class MyService extends Service {
- @Override
- public IBinder onBind(Intent arg0) {
- // 把內部類的對象返回給客戶端使用
- return new MyServiceImpl();
- }
- // 創建一個繼承自IMyService.Stub的內部類
- public class MyServiceImpl extends IMyService.Stub {
- // 必須實現AIDL文件中的接口
- public String getValue() throws RemoteException {
- return null;
- }
- }
- }
注意,我們寫的service中,onBind方法必須返回MyServiceImpl類的對象實例,否則客戶端無法獲得服務對象。
3.2.3、在AndroidManifest.xml文件中配置MyService類
- <service android:name=".MyService" >
- <intent-filter>
- <action android:name="du.pack.IMyService" />
- </intent-filter>
- </service>
上面的"du.pack.IMyService"是客戶端用於訪問AIDL服務的ID。
4、本地客戶端的使用方法
4.1、新建一個Eclipse Android工程,並將剛纔遠程服務端自動生成的gen目錄下面的IMyService.java文件連同包目錄一起復制到客戶端工程的src目錄中。
4.2、調用AIDL服務首先要綁定服務,然後才能獲得服務對象。
- public class AidlClientTestActivity extends Activity {
- // 遠程服務端的對象
- IMyService mIMyService;
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- // 綁定成功,得到遠程服務端的對象,目標完成!!!
- mIMyService = IMyService.Stub.asInterface(service);
- }
- public void onServiceDisconnected(ComponentName name) {
- // 解除綁定
- mIMyService = null;
- }
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // 綁定遠程服務端服務
- Intent serviceIntent = new Intent("du.pack.IMyService");
- bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
- }
- }
5、用法小結
回顧一下整個調用的過程:
服務端方面:將需要開放的接口抽象到aidl文件中,然後在自己的內部類中對接口進行實現,並在自己被綁定的時候把內部類對象返回給客戶端。客戶端方面:當我們需要遠程某個Service時,就像在綁定本地的Service一樣去bindService,然後在綁定成功的回調函數中(也就是onServiceConnected方法)得到一個Ibinder對象(比如Service),這時調用IMyService.Stub.asInterface(service)這樣的語句,就可以得到服務端開放的interface接口對象,此時客戶端可以直接調用這個對象的方法,猶如直接調用遠程的Service對象一般。
四、Framework中使用AIDL
Framework中使用AIDL我們通過ITelephonyRegistry這個SystemService進行分析。該服務的主要作用就是對通話相關的事件進行監聽,我們重心放在AIDL的實現結構上,不去關注ITelephonyRegistry具體的實現。
1、AIDL文件相關
先來看一下這個服務的AIDL文件:
- @ITelephonyRegistry.aidl
- interface ITelephonyRegistry {
- void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);
- void notifyCallState(int state, String incomingNumber);
- void notifyServiceState(in ServiceState state);
- void notifySignalStrength(in SignalStrength signalStrength);
- void notifyMessageWaitingChanged(boolean mwi);
- }
再來看這個服務的真正實現:
- @TelephonyRegistry.java
- class TelephonyRegistry extends ITelephonyRegistry.Stub {
- TelephonyRegistry(Context context) {
- ......
- }
- void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow){
- ......
- }
- void notifyCallState(int state, String incomingNumber){
- ......
- }
- void notifyServiceState(in ServiceState state){
- ......
- }
- void notifySignalStrength(in SignalStrength signalStrength){
- ......
- }
- void notifyMessageWaitingChanged(boolean mwi){
- ......
- }
- }
上面的兩個文件是這個服務的核心部分,aidl文件規定了這個服務的功能,而java文件是對功能的具體實現。但是,此時的TelephonyRegistry並沒有繼承Service的類,也就是說,當前他並不具備作爲一個Service的資格。那麼他是如何變成一個服務的呢?
2、服務的註冊過程
在SystemService中可以找到答案。
- @SystemServer.java
- class ServerThread extends Thread {
- @Override
- public void run() {
- try {
- telephonyRegistry = new TelephonyRegistry(context);
- ServiceManager.addService("telephony.registry", telephonyRegistry);
- }
- }
- }
我們看到,在這一步中,把telephonyRegistry對象(也就是ITelephonyRegistry.Stub的子類對象)作爲一個Service註冊給了ServiceManager。並且註冊的名字是“telephony.registry”
有了這一步,TelephonyRegistry就可以作爲服務提供者向客戶端開放了。也就是說,有了這一步,TelephonyRegistry纔算上是一個真正的Service,可以接受客戶端的連接申請。
那麼接下來,我們怎麼得到這個Service呢?
3、如何得到註冊的服務
既然通過ServiceManager註冊了服務,我們就需要再次通過ServiceManager得到它的服務對象。
- private ITelephonyRegistry sRegistry;
- sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
通過這樣的方法,我們就得到了ITelephonyRegistry.aidl的對象sRegistry。
4、流程總結
回顧一下這種AIDL服務的框架:通過繼承ITelephonyRegistry.Stub父類去實現AIDL中規定的接口,然後把TelephonyRegistry作爲ServiceManager註冊給SystemService。客戶端可以通過ServiceManager得到TelephonyRegistry的對象,然後就可以去調用AIDL中定義的接口了。