AIDL機制詳解

  • 一、背景

  ·1、AIDL出現的原因

   在android系統中,每一個程序都是運行在自己的進程中,進程之間無法進行通訊,爲了在Android平臺,一個進程通常不能訪問另一個進程的內存空間,所以要想對話,需要將對象分解成操作系統可以理解的基本單元,並且有序的通過進程邊界。通過代碼來實現這個數據傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工作,實現IPC(進行間的通信)與J2e中的RMI類似。

  ·2、綁定service

  我看了很多人都博客都沒有說到這裏,其實我個人感覺AIDL就是一個遠程的客戶端而已,把之前BindService的中service的繼承類單獨拿出來放到遠程客戶端,在本地綁定service中我們一般是在onBind方法中將內部定義好的一個繼承Binder類的對象返回給activity,在active裏面獲得該對象然後去調用方法對於BindService的介紹見下一篇博客。但是這裏不一樣我們是要去另一個進程說白點也就是另一個應用裏面去獲得數據,因此我們需要一些接口上的規則,來規範進程間的通信,讓服務端應用進行發送,客戶端應用來接受。下面說一下規則是神馬?

  • 二、AIDL服務端的步驟

  ·1. 實現.aidl文件

  以eclipse開發爲例,我們需要在一般是在src文件夾下,新建一個包---文件夾,然後再該文件夾目錄下新建一個文件該文件的後綴名以.aidl結尾,這是必須如此的。

  ·2. 在文件內部首先要給出包名,

  ·3. 在接口中用到的數據類型,除了基本類型,String,List,Map,CharSequence,之外其他的都需要導包,即使在同一個包內也是需要導包,因此裏面的類,以及方法我們都是需要導包的。

  ·4. 安卓ADT一般會幫我們自動將寫好的aidl文件編譯生成一個java語法的接口類,這個藉口在gen文件下,可以打開裏面默認生成了一個內部抽象類stub該類繼承Binder實現了起外部接口,

  ·5. 定義方法名稱,所有的.aidl文件已經需要傳遞的對象接口需要在Service 與Client中各一份,最好是直接複製過去即可。

//----------------------------------下面是提供數據訪問的服務端aidl文件定義:

1 package com.example.service;
2 
3 interface DataService{
4     double getData(String arg);
5           
6 }

  ·6. 下面就是在service的自定義類中來實現上面的aidl接口,上面已經解釋了我們通過綁定service的來傳遞的其實是一個IBinder的對象,這裏實際實現的接口是在 gen中自動生成的 DataService.java的抽象內部類 Stub,因爲該類繼承了Binder,而Binder則實現了IBinder接口,所以我們實際上需要的DataService.Stub的對象。

//---------------下面是服務端service中的代碼,其實和綁定service的規則是一致的,

//===========目標是一致就是把一個IBinder的對象代理髮到客戶端,客戶端接收到該對象,有了這個對象就可以調用該對象的一些方法。

複製代碼

 1 import com.example.service.DataService;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.Binder;
 6 import android.os.IBinder;
 7 import android.os.RemoteException;
 8 
 9 public class MyService extends Service {
10 
11     @Override
12     public IBinder onBind(Intent intent) {
13         // 這個binder就是要發送給遠程客戶端的對象,在建立連接後客戶端得到該對象,調用該對象的方法來獲取數據。
14         return binder;
15     }
16     // 還有一個需要注意的就是我們的service需要在配置文件裏面進行註冊,同時添加一個action的過濾器方便我們調用,綁定。
17     // 因爲需要一個IBinder的對象返回而這個對象是一個接口,所以需要具體實現類的對象,Binder實現了該接口,因此我們需要創建一個Binder的對象,
18     // 又我們定義的aidl文件中的接口中被編譯後會自動生成一個內部類stub,該內部類就繼承了Binder
19     // 定義提供給客戶端client調用的方法
20     Binder binder = new DataService.Stub() {
21         @Override
22         public double getData(String arg) throws RemoteException {
23             // TODO Auto-generated method stub
24             
25             if (arg.equals("a")) {
26                 return 1;
27             } else if (arg.equals("b")) {
28                 return 2;
29             }
30             
31             return 0;
32         }
33     };
34 
35 }

複製代碼

//=====================上面直接運行跑起來就可以空的activity.

// 下面說下客戶端的運行

// 客戶端首先是要有aidl文件的上面已經說到了,下面就是直接上代碼了

//---------------首先是佈局文件,簡單地兩個按鈕

View Code

下面是activity中的代碼,我們要和服務端建立連接,就要在bindService方法中綁定到我們在服務端定義好的service類

Ok

複製代碼

 1 import com.example.service.DataService;
 2 
 3 import android.os.Bundle;
 4 import android.os.IBinder;
 5 import android.app.Activity;
 6 import android.content.ComponentName;
 7 import android.content.Context;
 8 import android.content.Intent;
 9 import android.content.ServiceConnection;
10 import android.view.View;
11 import android.widget.Button;
12 
13 public class MainActivity extends Activity {
14 
15     private Button button1, button2;
16     private DataService dataService;
17 
18     @Override
19     protected void onCreate(Bundle savedInstanceState) {
20         super.onCreate(savedInstanceState);
21         setContentView(R.layout.activity_main);
22         button1 = (Button) findViewById(R.id.button1);
23         button2 = (Button) findViewById(R.id.button2);
24 
25         // 綁定
26         button1.setOnClickListener(new View.OnClickListener() {
27 
28             @Override
29             public void onClick(View v) {
30                 // TODO Auto-generated method stub
31                 Intent intent = new Intent(DataService.class.getName());
32                 // 綁定service
33                 /* bindService(service, conn, flags)該方法的參數解釋一下
34                  * 1.service: 表示通過該參數要啓動的service
35                  * 2.conn: 該參數是一個ServiceConnection,該對象是用於劍聽雨service之間的連接情況,當訪問者與service之間
36                  * 成功連接時會調用onServiceConnected(ComponentName name, IBinder service)方法,通過該方法來獲取
37                  * service中的IBinder對象,通過對象來獲取數據。連接失敗onServiceDisconnected。
38                  */
39                 
40                 bindService(intent, connection, Context.BIND_AUTO_CREATE);
41             }
42         });
43 
44         button2.setOnClickListener(new View.OnClickListener() {
45 
46             @Override
47             public void onClick(View v) {
48                 // TODO Auto-generated method stub
49                 try {
50                     double result = dataService.getData("a");
51                     System.out.println("--->>" + result);
52 
53                 } catch (Exception e) {
54                     // TODO: handle exception
55                     e.printStackTrace();
56                 }
57             }
58         });
59     }
60 
61     // 客戶端與服務進行連接
62     private ServiceConnection connection = new ServiceConnection() {
63         @Override
64         public void onServiceDisconnected(ComponentName name) {
65             // TODO Auto-generated method stub
66 
67         }
68 
69         @Override
70         public void onServiceConnected(ComponentName name, IBinder service) {
71             // TODO Auto-generated method stub
72             // 這些都是在API可以參考的,由SDK自動生成的DataService.Stub.asInterface的幾個類
73             // 將客戶端傳遞過來的IBinder對象,換爲DataService對象,
74             // 在這裏需要使用到aidl文件被編譯後自動生成的內部類Stub中的一個方法asInterface,該方法的作用就是類型轉換
75             dataService = DataService.Stub.asInterface(service);
76         }
77     };
78 }

複製代碼

總結其實這個aidl和綁定service並沒有太大的區別,只不過我們把service的繼承類放在了客戶端,增加了一些協議規範罷了。

最後,總結下 AIDL 的步驟:

服務端進程:

1:創建AIDL文件如xxx.aidl

2:在接口中自定義方法,如果有bean則實例化改bean實體類

3:構建Service遠程服務

4:最後在清單文件裏 註冊服務,其中屬性要加上export:true 讓外部程序可以訪問。

客戶端進程:

1:將服務端的AIDL文件拷貝到main文件夾下,(如果有bean也要對應拷貝)。(包名要求與服務端的一致)

2:重建一下項目檢查一下java文件是否構建成功

3:連接綁定服務,獲取aidl接口實例。
4:按需調用遠程接口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章