Android開發之AIDL實現遠端服務程序通訊(IPC)
首先什麼是AIDL呢,它是Android系統中的一種介面定義語言,用於約束兩個程序間的通訊規則,供編譯器生成程式碼。
實現Android裝置上的兩個程序間通訊(IPC),程序之間的通訊資訊首先會被轉換成AIDL協議資訊,然後傳送給對方;對方接收到AIDL協議資訊後再轉換成相應的物件,由於程序之間的通訊資訊需要雙向的轉換,Android系統採用代理類在背後實現了資訊的雙向轉換,代理類是由Android編譯器生成,對於開發人員來數是透明個。
兩個程序間也就是應用間的通訊,一個是訪問者A應用 client另一個則是B應用的service,A要想訪問B的service是不可能直接的去呼叫B的方法的,但是如果要實現AIDL規則的話,則可以間接的訪問。這就類似於在開發webA應用的時候我們的應用要獲取另一個webB應用的資料,但是我們不知道webB應用在哪裡,但是如果WebB給給我提供一個WSDL檔案,我們就可以通過webservice去訪問應用中的方法,其實就是一個道理只是系統的不同。
首先我們來介紹下整個過程
A應用請求B應用的 service ,如果A應用與B應用的 service綁定了以後那麼 B應用就會返回一個Binder ,A應用可以拿著這個Binder 去呼叫 B應用service中的方法。但是這個Binder 這是一個代理。
接下來我們看一下具體的程式碼:
我們先來配置一下B應用的 service
建立一個QueryPerson.aidl檔案,這時候編譯器會自動的給我們在gen下相同的包生成一個java檔案。
package com.my.aidl;
//aidl的語法和我們定義介面很是類似,但是aidl不可以有任何的修飾符
interface QueryPerson {
String query(int num);
}
接下來建立service
public class QuerypersonService extends Service {
private IBinder iBinder = new QuerypersonBinder();
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
/**
* 這個方法裡我們就不能再繼承IBinder了, 因為我們要讓這個service具有遠端通訊的能力
*
* 所以這我們要繼承剛才系統幫我們生成的aidl 類
*
* 這個方法繼承的是系統的android.os.Binder
* public static abstract class Stub extends android.os.Binder
*
*/
private final class QuerypersonBinder extends QueryPerson.Stub{
//這裡就簡單的放一些資料測試
@Override
public String queryPerson(int num) throws RemoteException {
String[] names = new String[]{"張三","李四","王五","z趙六"};
String name = names[num];
return name;
}
}
}
最後別忘了在AndroidMainifest.xml配置service
<service android:name=".QuerypersonService" >
<intent-filter>
<action android:name="com.my.aidlservice" />
</intent-filter>
</service>
到這我們的service端的就寫完了
接下來A應用的程式碼
同樣的必須將service中的aidl檔案複製到A應用中
配置activity程式碼:
public class MainActivity extends Activity {
private EditText mEditText;
private TextView mText;
private PersonServiceConnection conn = new PersonServiceConnection();
private QueryPerson queryPerson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView) findViewById(R.id.Text);
mEditText = (EditText) findViewById(R.id.content);
// 採用隱世的intent 去啟動service
Intent service = new Intent("com.my.aidlservice");
// 繫結service
bindService(service, conn, BIND_AUTO_CREATE);
}
//按鈕的點選事件
public void QueryPerson(View v) {
String number = mEditText.getText().toString();
int num = Integer.parseInt(number);
try {
String name = queryPerson.queryPerson(num);
mText.setText(name);
} catch (RemoteException e) {
e.printStackTrace();
}
}
// 建立一個ServiceConnection
private final class PersonServiceConnection implements ServiceConnection {
// 當service繫結時
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//service 返回的IBinder 必須要進行轉換才可以使用,用asInterface()方法就可以轉換
queryPerson = QueryPerson.Stub.asInterface(service);
}
// 當service撤銷繫結時
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
// 當activity銷燬時 會取消繫結service
@Override
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
}
佈局檔案:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="QueryPerson"
android:text="Click"/>
<TextView
android:id="@+id/Text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
實際的效果就是下圖所示的:
這就是簡單的遠端服務程序間通訊,很簡答吧!
之前想過不同應用之間為啥不直接用contentprovider呢 ,因為contentprovider只是向呼叫者暴露了你的資料庫而已,而且還不是實時的資料;但是用AIDL的話可是實時去訪問其他應用的資料。