什麼是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下某個包中創建一個處理業務的接口類
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)的子類的對象。
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目錄下),然後,訪問者端代碼如下:
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,不能爲其它標記。