一、IPC進程間通信
IPC是進程間通信方法的統稱,Linux IPC包括以下方法,Android的進程間通信主要采用是哪些方法呢?
1. 管道(Pipe)及有名管道(named pipe):管道可用於具有親緣關系進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關系進程間的通信;
2. 信號(Signal):信號是比較復雜的通信方式,用於通知接受進程有某種事件發生,除了用於進程間通信外,進程還可以發送信號給進程本身;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標准的信號函數sigaction(實際上,該函數是基於BSD的,BSD為了實現可靠信號機制,又能夠統一對外接口,用sigaction函數重新實現了signal函數);
3. 報文(Message)隊列(消息隊列):消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩沖區大小受限等缺點。
4. 共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。
5. 信號量(semaphore):主要作為進程間以及同一進程不同線程之間的同步手段。
6. 套接口(Socket):更為一般的進程間通信機制,可用於不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
二、AIDL(Android Interface Definition Language)
AIDL - Android Interface Definition Language - Android 接口定義語言。因為在Android中,應用程序運行在各自獨立的進程裡。應用程序之間是不能訪問對方的內存空間的。有時為了實現進程間的通信,要用到PCI機制。Android支持PCI機制,但是需要Android能讀懂的序列化數據(marshaling/un marshaling of data). AIDL就是為了描述這樣的數據產生的,它是一種接口定義語言。語法類似JAVA,.aidl文件裡面寫的是公布給客戶端接口聲明。
本文中,為了吸取客戶端,服務器端兩個android 項目的經驗,將兩段整合到一個android項目裡,這樣可能會比較清晰。下面就看看實例解析吧。
三、AIDL介紹及解析
1.AIDL介紹
在Android中,每個應用(Application)執行在它自己的進程中,無法直接調用到其他應用的資源,這也符合“沙箱”的理念。所謂沙箱原理,一般來說用在移動電話業務中,簡單地說旨在部分地或全部地隔離應用程序。關於沙箱技術我們這裡就不多做介紹了。因此,在Android中,當一個應用被執行時,一些操作是被限制的,比如訪問內存,訪問傳感器,等等。這樣做可以最大化地保護系統,免得應用程序“為所欲為”。那我們有時需要在應用間交互,怎麼辦呢?於是,Android需要實現IPC協議。然而,這個協議還是有點復雜,主要因為需要實現數據管理系統(在進程或線程間傳遞數據)。Android為我們實現了自己的IPC,也就是AIDL。
2.定義AIDL接口
AIDL是IPC的一個輕量級實現,用了對於Java開發者來說很熟悉的語法。Android也提供了一個工具,可以自動創建Stub(類構架,類骨架)。當我們需要在應用間通信時,我們需要按以下幾步走:
a. 定義一個AIDL接口
b. 為遠程服務(Service)實現對應Stub
c. 將服務“暴露”給客戶程序使用
3.實例解析
AIDL的語法很類似Java的接口(Interface),只需要定義方法的簽名。
AIDL支持的數據類型與Java接口支持的數據類型有些不同
a. 所有基礎類型(int, char, 等)
b. String,List,Map,CharSequence等類
c. 其他AIDL接口類型
d. 所有Parcelable的類
下面以一個加法器為例來做解析。
(1)創建工程,創建AIDL接口:新建一個文件,命名為IAdditionService.aidl 即可
package com.czm.hellosumaidl;
interface IAdditionService{
int add(int value1,int value2);
}
一旦文件被保存,Android的AIDL工具會在 gen/com/android/hellosumaidl 這個文件夾裡自動生成對應的 IAdditionService.java 這個文件。因為是自動生成的,所以無需改動。這個文件裡就包含了 Stub ,我們接下來要為我們的遠程服務實現這個Stub。
(2)實現遠程服務
首先我們新建一個類,取名叫AdditionService.java 。為了 實現我們 的服務,我們需要讓 這個 類中的 onBind方法返回一個 IBinder 類的對象。這個 IBinder 類的對象就代表了遠程服務的實現。為了實現這個服務,我們要用到自動生成的子類 IAdditionService.Stub 。在其中,我們也必須實現我們之前在AIDL文件中定義的 add() 函 數。下面是我們遠程服務的代碼:
復制代碼
public class AdditionService extends Service{
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return new IAdditionService.Stub() {
@Override
public int add(int value1, int value2) throws RemoteException {
// TODO Auto-generated method stub
return value1 + value2;
}
};
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
復制代碼
(3)提供服務(“暴露”服務給使用者)
一旦實現了服務中的onBind方法 ,我們就可以把客戶程序(在這裡是MainActivity.java )與服務連接起來了。為了建立這樣的一個鏈接 ,我們需要實現 ServiceConnection 類。我們在 MainActivity.java 創建一個內部類 AdditionServiceConnection ,這個類繼承 ServiceConnection 類,並且重寫了它的兩個方法: onServiceConnected 和 onServiceDisconnected。下面給出 內部類的代碼:
復制代碼
class AdditionServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder boundService) {
// TODO Auto-generated method stub
service = IAdditionService.Stub.asInterface((IBinder) boundService);
Toast.makeText(MainActivity.this, "Service connected",
Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
service = null;
Toast.makeText(MainActivity.this, "Service disconnected",
Toast.LENGTH_SHORT).show();
}
}
復制代碼
(4)相關代碼及效果截圖
MainActivity.java的全部代碼如下:
復制代碼
package com.czm.hellosumaidl;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
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;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
IAdditionService service;
AdditionServiceConnection connection;
EditText value1;
EditText value2;
TextView result;
Button buttonCalc;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initService();
buttonCalc = (Button) findViewById(R.id.buttonCalc);
value1 = (EditText) findViewById(R.id.value1);
value1.setText("23");
value2 = (EditText) findViewById(R.id.value2);
value2.setText("24");
result = (TextView) findViewById(R.id.result);
buttonCalc.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int v1, v2, res = -1;
v1 = Integer.parseInt(value1.getText().toString());
v2 = Integer.parseInt(value2.getText().toString());
try {
res = service.add(v1, v2);
} catch (RemoteException e) {
e.printStackTrace();
}
result.setText(Integer.valueOf(res).toString());
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
releaseService();
}
/*
* This function connects the Activity to the service
*/
private void initService() {
connection = new AdditionServiceConnection();
Intent i = new Intent();
i.setClassName("com.czm.hellosumaidl",com.czm.hellosumaidl.AdditionService.class.getName());
boolean ret = this.bindService(i, connection, Context.BIND_AUTO_CREATE);
}
/*
* This function disconnects the Activity from the service
*/
private void releaseService() {
unbindService(connection);
connection = null;
}
class AdditionServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder boundService) {
// TODO Auto-generated method stub
service = IAdditionService.Stub.asInterface((IBinder) boundService);
Toast.makeText(MainActivity.this, "Service connected",
Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
service = null;
Toast.makeText(MainActivity.this, "Service disconnected",
Toast.LENGTH_SHORT).show();
}
}
}