Android進程內通信


什麼是IPC?
IPC(Inter-Process Communication,進程間通信)
IPC ( Instruction per Clock 及CPU每一時鐘週期內所執行的指令多少) IPC代表了一款處理器的設計架構,一旦該處理器設計完成之後,IPC值就不會再改變了。

1、Android進程內通信
Android爲了屏蔽進程的概念,利用不同的組件[Activity、Service 或者其它組件]來表示進程之間的通信。
組件間通信的核心機制是Intent ,通過Intent可以開啓一個Activity或Service,

Intent包含兩部分:action和category(區分性數據)、data(內容性數據)
Intent類型:
1)顯示Intent---直接指定消息的目的地,只適合同一進程內的不同組件之間通信new Intent(this,Target.class)
2)隱式Intent---AndroidMainifest.xml中註冊,一般用於跨進程通信new Intent(String action)

3)實現Intent簡單進程間通信
重點在於隱式Intent:
a、一個<activity>包括:
零個或多個<intent-filter>
它主要是作爲匹配的標準,能否匹配成功由<action>、<category>、<data>三個tag共同決定的。
b、一個<intent-filter>包括:
一個或多個 <action>
零個或多個 <category>
指定<activity>的分類特徵
例如:
<category android :name="android.intent.category.LAUNCHER" />
--說明該<activity>是該project運行 的第一個界面
<category android:name="android.intent.category.HOME" />
--說明該<activity>可以作爲Launcher的,即系統 操作界面
c、零個或一個 <data>
指定攜帶的數據的類型,使用MIME類型描述方式來描述
例如:<data android:mimeType="video/mpeg" />
video/mpeg表示編碼格式爲mpeg的視頻 ,
也可以使用通配符video/*表示任意格式的視頻文件類型;

4)一個Intent對應多種匹配結果的處理說明
一個intent有多個可匹配的處理組件,系統如何處理?
分響應消息的組件類型:
1)如果是service那麼這些service都可以啓動並處理消息。
2)如果是Activity則會彈出一個對話框讓用戶進行選擇。
5)安全問題----權限permission


2、IPC機制
Android 對進程間通信實現了一套輕量級的IPC機制 --- Binder機制,基於此基礎之上提供了整體的封裝,從而實現對象代理機制。
A、什麼是Binder工作模式?
1)客戶端通過某種方式得到服務器端的代理對象。
2)客戶端通過調用服務器代理對象的方法向服務器端發送請求。
3)代理對象把用戶請求通過Android內核(Linux內核)的Binder驅動發送到服務器進程。
4)服務器進程處理用戶請求,並通過Android內核(Linux內核)的Binder驅動返回處理結果給客戶端的服務器代理對象。
5)客戶端收到服務器端的返回結果。
B、binder機制的組成
1)Binder驅動---/dev/binder 是Android內核的一個字符驅動設備,它是IPC的核心部分。
客戶端發送請求最終就是通過它來傳遞到服務端,而服務端的返回結果也是通過它來傳給客戶端。內核源碼:binder.c
2)Service Manager---服務端有服務的話就得向它註冊,而客戶端需要向它查詢、獲得服務。
3)提供服務的Server (Service)
提供服務的Server, 對普通的應用開發來講,咱們用到的就是Service, 具體的工作Android都幫忙做了封裝,所以開發變得很容易。
4)調用對象代理的Client (Activity)
普通的應用開發來講就是 Activity 通過代理對象去請求調用服務。
注意:這個過程是同步的,所以如果估計這個服務調用很耗時,那麼需要考慮啓新線程來調用,而不能用UI主線程。
5)代理對象 (底層:BpBinder)
客戶端進程通過服務代理BpBinder對象,調用transact函數,該函數作用就是把客戶端的請求寫入binder設備另一端的Service進程。
從JAVA層面來講,Android已經爲我們封裝定義了IBinder接口.

3、AIDL--遠程服務
1)在Android中, 每個應用程序都有自己的進程,當需要在不同的進程之間傳遞對象時,該如何實現呢? 顯然, Java中是不支持跨進程內存共享的。
因此要傳遞對象, 需要把對象解析成操作系統能夠理解的數據格式, 以達到跨界對象訪問的目的。在JavaEE中,採用RMI通過序列化傳遞對象。
在Android中, 則採用AIDL(Android InterfaceDefinition Language:接口定義語言)方式實現。

2)什麼是AIDL?
AIDL是一種接口定義語言,用於約束兩個進程間的通訊規則,供編譯器生成代碼,實現Android設備上的兩個進程間通信(IPC)。
原理:進程之間的通信信息,首先會被轉換成AIDL協議消息,然後發送給對方,對方收到AIDL協議消息後再轉換成相應的對象。
由於進程之間的通信信息需要雙向轉換,所以android採用代理類在背後實現了信息的雙向轉換,代理類由android編譯器生成,對開發人員來說是透明的。

3)使用AIDL實現進程間通信的步驟:
a、遠程服務應用中:
遠程服務端,也就是RemoteService應用中,在src下某個包中創建一個處理業務的接口類
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
9
package.cn.itcast.bh.service;
 public interface IStudentService{
       public String query(int no);
           }
 然後,根據後面注意事項中的規則,將接口的代碼做一些處理,變成如下:
 package.cn.itcast.bh.service;
 interface IStudentService{
        String query(int no);
           }


然後,將相應的接口文件的後綴名改爲“ .aidl”.
然後,刷新,則系統會在gen目錄下生成一個Java文件,會用到這個Java文件中的類及其內部類。
(注意:如果項目中還有有錯誤的地方,系統則無法根據AIDL生成Java代碼。

遠程服務的類文件如下:
其中,(1)部分,是來自於系統根據AIDL生成的Java類的一個內部類,遠程服務的onBind方法返回的IBinder對象,必須是該類(IstudentService.Stub)的子類的對象。
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
pcakage cn.itcast.bh.remoteService;
  
import cn.itcast.bh.service.IStudentService;
  
public class StudentRemoteService extends Service{
      private String[] students = new String[]{"張""郭","李""lihao"};
          private MyBinder myBinder = new MyBinder();
          @override
          public IBinder onBind(Intent intent) {
                         return myBinder;
                 }
          
        private final class MyBinder extends IStudentService.Stub(1){
                 public String query(int no) throws RemoteException{
                  
                        return students[no];
                         }
                 }
         }


b、訪問者的應用中:
將遠程服務端的aidl文件拷貝到訪問者的應用中(src目錄下),然後,訪問者端代碼如下:
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import android.view.View;
 import android.widget.EditText;
 import android.widget.TextView;
 import cn.itcast.bh.service.IStudentService;
  
public class MainActivity extends Activity{
  
        private EditText noText;
         private IStudentService studentService;
         private TextView showView;
         public void onCreate(Bundle savedInstanceState){
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         noText = (EditText)findViewById(R.id.no);
         showView = (TextView)findViewById(R.id.showView);
         Intent intent = new Intent("cn.itcast.remoteService");
         this.bindService(intent,new MyConnection(),Context.BIND_AUTO_CREATE);
         }
          
        private final class MyConnection implenments ServiceConnection{
                 @override
                 public void onServiceConnected(ComponentName name, IBinder service){
                         srudentService=IStudentService.Stub.asInterface(service);
                 }
                  
                @override
                 public void onSericeDisconnected(ComponentName name) {
                         studentService = null;
                 }
         }
          
        public void query(View v){
                 try{
                         int no = Integer.parseInt(noText.getText().toString());
                         String name = studentService.query(no);
                         showView.setText(name);
                 }catch(Exception e){
                                 e.printStackTrace();
                                 }
                 }
         }
 }


訪問者代碼中,訪問者端拿到的IBinder對象不能(像訪問者與本地服務通信那樣)強制類型轉換,需要通過如下方法進行類型轉換。
binder = StudentService.Stub.asInterface(service);
進程間通信的時候,訪問者獲得的對象,也並不是遠程服務onBind方法中返回的原始的對象,
而是那個對象的代理對象,操作系統在代理中做了協議轉換的事情,對開發者是透明的。
進程間通過AIDL通信的注意事項:
AIDL文件的書寫規則:
1)接口名和aidl文件名相同。
2)接口和方法前不用加訪問權限修飾符public,private,protected等,也不能用final,static。
3)aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),
使用這些類型時不需要import聲明。對於List和Map中的元素類型必須是Aidl支持的類型。
如果使用自定義類型作爲參數或返回值,自定義類型必須實現Parcelable接口。
4)自定義類型和AIDL生成的其它接口類型在aidl描述文件中,應該顯式import,即便在該類和定義的包在同一個包中。
5)在aidl文件中所有非Java基本類型參數必須加上in、out、inout標記,以指明參數是輸入參數、輸出參數還是輸入輸出參數。
6)Java原始類型默認的標記爲in,不能爲其它標記。       
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章