在Andorid平臺中,各個組件運行在自己的進程中,他們之間是不能相互訪問的,但是在程序之間是不可避免的要傳遞一些對象,在進程之間相互通信。爲了實現進程之間的相互通信,Andorid採用了一種輕量級的實現方式RPC(Remote
Procedure Call 遠程進程調用)來完成進程之間的通信,並且Android通過接口定義語言(Andorid
Interface Definition Language ,AIDL)來生成兩個進程之間相互訪問的代碼,例如,你在Activity裏的代碼需要訪問Service中的一個方法,那麼就可以通過這種方式來實現了。
AIDL是Android的一種接口描述語言; 編譯器可以通過aidl文件生成一段代碼,通過預先定義的接口達到兩個進程內部通信進程的目的. 如果需要在一個Activity中, 訪問另一個Service中的某個對象, 需要先將對象轉化成 AIDL可識別的參數(可能是多個參數), 然後使用AIDL來傳遞這些參數, 在消息的接收端, 使用這些參數組裝成自己需要的對象。
AIDL RPC機制是通過接口來實現的,類似Windows中的COM或者Corba,但他是輕量級的,客戶端和被調用實現之間是通過代理模式實現的,代理類和被代理類實現同一個接口Ibinder接口。
下面是實現Activity訪問Service例子的步驟:
一.創建.aidl文件
AIDL使用簡單的語法來聲明接口,描述其方法以及方法的參數和返回值。這些參數和返回值可以是任何類型,甚至是其他AIDL生成的接口。重要的是必須導入導入除了內建類型(例如:int,boolean等)外的任何其他類型,哪怕是這些類型是在與接口相同的包中。具體的要求如下:
- JAVA基本數據類型不需要導入
- String,List,Map和CharSequence不需要導入
使用Eclipse的ADT插件創建一個BookInfo.aidl文件,該文件有4個方法:
setName(String name)設置圖書的書名,setPrice(int price)設置圖書的價格,setPublish(String pname)設置圖書的出版社和String display()顯示圖書的信息.
BookInfo.aidl文件
- package com.android.aidl;
-
- //BookInfo接口
- interface BookInfo{
-
- void setName(String name);
- void setPrice(int price);
- void ssetPublish(String pname);
- //顯示圖書的信息
- String display();
- }
創建好BookInfo.aidl文件,系統會自動在gen目錄下生成Java接口文件BookInfo.java
二.實現AIDL文件生成的JAVA接口
AIDL會生成一個和.aidl文件同名的JAVA接口文件,該接口中有一個靜態抽象內部類Stub,該類中聲明瞭AIDL文件中定義的所有方法,其中有一個重要的方法是asInterface(),該方法通過代理模式返回JAVA接口的實現我們可以定義一個實現類,BookImpl,該類繼承Stub類,實現我們定義的4個方法
- package com.android.aidl;
- import android.os.RemoteException;
-
- public class BookInfoImpl extends BookInfo.Stub {
-
- private int price;
- private String name,pname;
-
- public String display() throws RemoteException{
- return "書名:"+name+";價格:"+price+";出版社:"+price;
- }
- @Override
-
- public void setName(String name) throws RemoteException {
-
- this.name= name;
- }
-
- @Override
-
- public void setPrice(int price) throws RemoteException {
-
- this.price = price;
- }
- @Override
-
- public void setPublish(String pname) throws RemoteException {
-
- this.pname= pname;
- }
- }
三.向客戶端暴露接口
現在已經實現了BookInfo接口,接下來要將該接口暴露給客戶端調用。一般通過定義一個Service來實現,在Service的onBind()方法中返回該接口,當我們綁定該接口時調用該方法。
- package com.android.aidl;
-
- import com.android.aidl.BookInfo.Stub;
- import android.app.Service;
- import android.content.Intent;
- import android.os.IBinder;
-
- public class RemoteService extends Service {
-
- private Stub bookifo = new BookInfoImpl();
- public IBinder onBind(Intent intent){
- return bookifo;
- }
- }
四.在客戶端調用
定義一個Activity來綁定遠程Service,獲得BookInfo接口,通過RPC機制調用接口中的方法。
- package com.android.aidl;
-
- import android.app.Activity;
- import android.app.Service;
- import android.content.ComponentName;
- 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.Toast;
-
- public class MainActivity extends Activity {
-
- private BookInfo bookInfo;
-
- private Button btn;
-
- private ServiceConnection conn = new ServiceConnection() {
- @Override
- synchronized public void onServiceConnected(ComponentName name, IBinder service) {
-
- bookInfo = BookInfo.Stub.asInterface(service);
- if (bookInfo != null)
- try {
-
- bookInfo.setName("Google Android SDK開發範例大全");
- bookInfo.setPrice(55);
- bookInfo.setPublish("人民郵電出版社");
- String msg = bookInfo.display();
-
- Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG)
- .show();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
-
- }
- };
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.main);
-
- btn = (Button) findViewById(R.id.Button1);
-
- btn.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
-
- Intent intent = new Intent();
-
- intent.setAction("com.android.aidl.action.MY_REMOTE_SERVICE");
-
- bindService(intent, conn, Service.BIND_AUTO_CREATE);
- }
- });
- }
- }
五.main.xml和AndroidManifest.xml文件
main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <Button
- android:text="遠程調用Service"
- android:id="@+id/Button1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- </LinearLayout>
在AndroidManifest.xml文件16~20聲明Service
爲什麼必須用隱式的方式啓動Service,是因爲運行在Android中的程序相當於多個運行在Linux上的用戶,他們之間是不能互相訪問的,要啓動必須要以在系統中註冊的靜態的action去啓動。
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.aidl"
- android:versionCode="1"
- android:versionName="1.0">
- <uses-sdk android:minSdkVersion="10" />
-
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name=".MainActivity"
- android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <service android:name="RemoteService"
- android:exported ="true">
- <intent-filter>
- <action android:name="com.android.aidl.action.MY_REMOTE_SERVICE"/>
- </intent-filter>
- </service>
- </application>
- </manifest>
效果圖: