1. 程式人生 > >Android 程序間通訊(AIDL)

Android 程序間通訊(AIDL)

在Android平臺,一個程序通常不能訪問另一個程序的記憶體空間,所以要想對話,需要將物件分解成作業系統可以理解的基本單元,並且有序的通過程序邊界。通過程式碼來實現這個資料傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工作。
AIDL (Android Interface Definition Language) 是一種IDL 語言,用於生成可以在Android裝置上兩個程序之間進行程序間通訊(interprocess communication, IPC)的程式碼。如果在一個程序中(例如Activity)要呼叫另一個程序中(例如Service)物件的操作,就可以使用AIDL生成可序列化的引數。

定義AIDL介面

AIDL介面檔案,和普通的介面內容沒有什麼特別,只是它的副檔名為.aidl。儲存在src目錄下。如果其他應用程式需要IPC,則那些應用程式的src也要帶有這個檔案。Android SDK tools就會在gen目錄自動生成一個IBinder介面檔案。service必須適當地實現這個IBinder介面。那麼客戶端程式就能繫結這個service並在IPC時從IBinder呼叫方法。

注意:每個aidl檔案只能定義一個介面,而且只能是介面的宣告和方法的宣告。

我們舉例說明:首先我們做一個在一個專案裡開啟多程序的例子。

1.專案工程列表如下:

這裡寫圖片描述
MainActivity是執行在主程序的,ProcessService是執行在另一個程序中(實現方式看我的部落格 Android 多程序基礎)。

2.實現.aidl檔案

介面描述檔案
a、匯入的包名
b、如果有使用Object物件,需要該物件 implement Parcelable 介面,並且需要匯入該介面包名+類名;
如果是primitive type 不需要這步。
c、定義方法名稱。
d、所有的.aidl檔案已經需要傳遞的物件介面需要在Service 與Client中各一份

IStockService.aidl檔案

package com.example.processtest.aidl;
import com.example.processtest.aidl.Person;
interface IStockService {
  double getResult(double a,double b);
com.example.processtest.aidl.Person getPerson(); }

如果aidl檔案中需要傳遞Object物件,需要新增對應的.aidl檔案
Person.aidl檔案
a、定義該物件Data,並實現Parcelable
b、新增Data.aidl檔案,並採用如下方式編寫匯入Data
c、需要在服務端和 客戶端都新增 Data.aidl與 Data.java檔案 並且需要一致。

package com.example.processtest.aidl;
parcelable Person;

新增 對應的Object類,並且實現Parcelable介面

public class Person implements Parcelable {
    private String name;
    private String age;
    public Person(){

    }
    private Person(Parcel in){
        readFromParcel(in);
    }

    public void readFromParcel(Parcel in)
    {
        name = in.readString();        
        age = in.readString();
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
 public static final Parcelable.Creator<Person> CREATOR=new Parcelable.Creator<Person>() {

    @Override
    public Person createFromParcel(Parcel source) {
        // TODO 自動生成的方法存根
        return new Person(source);
    }

    @Override
    public Person[] newArray(int size) {
        // TODO 自動生成的方法存根
        return new Person[size];
    }
};
    @Override
    public int describeContents() {
        // TODO 自動生成的方法存根
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO 自動生成的方法存根
        dest.writeString(name);
        dest.writeString(age);
    }

}

3.在Service中實現.aidl 介面

實際實現的介面是在 gen中自動生成的 IaidlData.java的抽象內部類 Stub。
建立好.aidl檔案後,重新整理專案,在gen包對應的包下會出現與aidl檔案相同的Java檔案

AIDL工具生成的Java檔案中:
public static abstract class Stub extends android.os.Binder implements com.example.processtest.aidl.IStockService //靜態抽象內部類

private static class Proxy implements com.example.processtest.aidl.IStockService //AIDL服務代理類

public double getResult(double a, double b) throws android.os.RemoteException //AIDL公佈出的介面,就是我們定義的介面方法。

AIDL只是一個契約,我們需要一個服務來提供服務。

提供服務的類ProcessService

public class ProcessService extends Service {
private class IStockServiceImp extends IStockService.Stub{

    @Override
    public double getResult(double a, double b) throws RemoteException {
        // TODO 自動生成的方法存根
        return a+b;
    }

    @Override
    public Person getPerson() throws RemoteException {
        // TODO 自動生成的方法存根
        Person person=new Person();
        person.setAge("20");
        person.setName("zhangsan");
        return person;
    }

}
    @Override
    public IBinder onBind(Intent intent) {
        // TODO 自動生成的方法存根
        return new IStockServiceImp();
    }
 @Override
    public boolean onUnbind(Intent intent) {
        // TODO 自動生成的方法存根
        //關閉服務所在的程序
         android.os.Process.killProcess(android.os.Process.myPid());
        return super.onUnbind(intent);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO 自動生成的方法存根
        Log.d("xxx",
                android.os.Process.myPid() + ";"
                        + Tools.getCurProcessName(getApplicationContext()));
        Log.d("xxS", MyApplication.name);
        return super.onStartCommand(intent, flags, startId);
    }
}

4.程序間的通訊

public class MainActivity extends Activity {
    private IStockService service2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("xxM", android.os.Process.myPid() + "");
        Intent intent = new Intent(getApplicationContext(),
                ProcessService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
        Log.d("xxM", MyApplication.name);
    }

    ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO 自動生成的方法存根

        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO 自動生成的方法存根
            service2 = IStockService.Stub.asInterface(service);
            try {
                Log.d("xx", service2.getResult(5, 9) + "");
                Log.d("xxp", service2.getPerson().getAge() + ";"
                        + service2.getPerson().getName());
            } catch (RemoteException e) {
                // TODO 自動生成的 catch 塊
                e.printStackTrace();
            }
        }
    };

    public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
            finish();
//          退出程式時我們一般只調用finish()函式殺死當前Activity,Application退到幕後,由系統自動維護。
//          再次啟動程式時就不會執行Application.onCreate(),而是直接執行Activity.onCreate()。

            //殺掉程序的方法
            android.os.Process.killProcess(android.os.Process.myPid());
            return true;
        }
        return false;
    };

    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    };
}