Android IPC機制(二):AIDL的基本使用方法

JimDcf 8年前發布 | 26K 次閱讀 ipc Android開發 移動開發 Android

一、前言

        上一篇文章,講述了實現序列化和反序列化的基本方式,是實現進程間通訊的必要條件,而這篇文章主要來講一講IPC的主要方式之一——AIDL方式。除了AIDL方式,IPC還有其他進程間通訊方式,比如Messager、ContentProvider、Socket等,這些以后會講到。現在先說說AIDL的基本使用方法。

二、什么是AIDL?

        AIDL全稱:Android Interface Definition Language,即Android接口定義語言。由于不同的進程不能共享內存,所以為了解決進程間通訊的問題,Android使用一種接口定義語言來公開服務的接口,本質上,AIDL非常像一個接口,通過公開接口,讓別的進程調用該接口,從而實現進程間的通訊。

三、使用AIDL

以下結合一個具體實例來示范AIDL的使用方法。
1、建立.aidl文件
        為了方便AIDL的開發,建議把所有和AIDL相關的類和文件放入同一個包中,這樣方便把整個包復制,以便其他模塊或者應用需要用到同一個AIDL。在Android Studio下,專門為AIDL文件創建了一個文件夾,方便我們的管理:

項目結構


        可以看到,筆者新建了一個service模塊,該模塊在manifests的聲明如下:

<service android:name=".MyAidlService"  
            android:enabled="true"  
            android:exported="true"></service>

        這個模塊為app模塊提供服務,與app模塊處于不同進程,所以模擬了進程間通訊的場景。在service模塊,新建一個AIDL文件夾,然后新建一個包,這里包名為com.chenyu.service,然后新建AIDL文件:IMyAidl.aidl:

// IMyAidl.aidl
package com.chenyu.service;

// Declare any non-default types here with import statements
import com.chenyu.service.Person;
interface IMyAidl {
void addPerson(in Person person);
List<Person> getPersonList();
}</pre>

        這與定義一個接口的語法基本相同,都是以Interface為關鍵字定義。里面聲明了兩個方法,分別是addPerson(in Person person)與getPersonList()。AIDL中除了基本數據類型,其他類型的參數必須標上方向,in、out、或者inout,in表示輸入型參數,out表示輸出型參數,inout表示輸入輸出型參數,我們要根據需要實際指定參數類型,因為底層的數據處理開銷非常大,如果不指定類型,編譯將會無法通過。

        AIDL支持的數據類型
①基本數據類型(int,long,char,boolean,double)
②string和CharSequence
③List:只支持ArrayList,以及里面所有的元素必須能夠被AIDL支持 ④Map:只支持HashMap,以及里面所有的元素必須能夠被AIDL支持 ⑤Parcelable:所有實現了Parcelable接口的對象
⑥AIDL:所有AIDL接口本身也可以在AIDL文件中使用。

        注意一下:這里使用了自定義的Parcelable對象:Person類,但是AIDL不認識這個類,所以我們要創建一個與Person類同名的AIDL文件:Person.aidl

// IMyAidl.aidl  
package com.chenyu.service;  
//聲明Person
parcelable Person;

        只有這樣,IMyAidl.aidl才能知道其中的Person是使用了Parcelable接口的類,注意,Person類的包名與Person.aidl的包名一定要相同,即無論其他應用或者其他模塊,只要有AIDL,都應該保證AIDL的所有包結構一致,才能保證順利進行IPC通訊,減少不必要的麻煩。

2、Person類,實現Parcelable接口
        在java文件夾中,創建com.chenyu.service包,這樣就與上面的是相同包名了。

package com.chenyu.service;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable {
private String name;
private int age;
private int number;

public Person(Parcel source) {  
    this.name=source.readString();  
    this.age=source.readInt();  
    this.number=source.readInt();  
}  

//getter、setter method  
//...  
public Person(int age, String name, int number) {  
    this.age = age;  
    this.name = name;  
    this.number = number;  
}  

@Override  
public int describeContents() {  
    return 0;  
}  

@Override  
public void writeToParcel(Parcel dest, int flags) {  
    dest.writeString(name);  
    dest.writeInt(age);  
    dest.writeInt(number);  
}  
public static final Parcelable.Creator<Person> CREATOR=new Creator<Person>() {  
    @Override  
    public Person createFromParcel(Parcel source) {  
        return new Person(source);  
    }  

    @Override  
    public Person[] newArray(int size) {  
        return new Person[size];  
    }  
};  

@Override  
public String toString() {  
    return "Person{" +  
            "name='" + name + '\\\\'' +  
            ", age=" + age +  
            ", number=" + number +  
            '}';  
}  

}</pre>

對于Parcelable接口的詳細解析,可參考上一篇文章,這里不再贅述。

3、實現服務端
        上面我們定義了一個AIDL接口,接下來要做的是實現這個AIDL接口,在java/com.chenyu.service中,創建MyAidlService.java文件:

package com.chenyu.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class MyAidlService extends Service {
private ArrayList<Person> persons;
@Override
public IBinder onBind(Intent intent) {
persons=new ArrayList<Person>();
Log.d("cy", "success bind");
return iBinder;
}
private IBinder iBinder= new IMyAidl.Stub() {
@Override
public void addPerson(Person person) throws RemoteException {
persons.add(person);
}

    @Override  
    public List<Person> getPersonList() throws RemoteException {  
        return persons;  
    }  
};  

@Override  
public void onCreate() {  
    super.onCreate();  
    Log.d("cy", "onCreate ");  
}  

}</pre>

        我們來看一下服務端是如何實現接口的:
        (1)為了實現來自.aidl文件生成的接口,需要繼承Binder接口(例如Ibinder接口),并且實現從.aidl文件中繼承的方法,在上面代碼中,使用匿名實例實現一個叫IMyAidl(定義在IMyAidl.aidl中)的接口,實現了兩個方法,addPerson和getPersonList.
        (2)onBind方法:該方法在客戶端與服務端連接的時候回調,實現客戶端和服務端的綁定,并返回一個Binder實例,這里返回的是iBinder,而IBinder是(1)中實現了接口的匿名實例,即客戶端拿到的實際上實現了接口的一個實例,這樣,客戶端通過Binder就與服務端建立了連接,客戶端通過Binder遠程調用服務端的實例方法,這樣也即實現了進程間通訊。

4、實現客戶端

在實現客戶端之前,先確保把aidl的包復制過來,就相上面筆者所給出的結構圖一樣,包括Person類也應該復制過來。顯示界面比較簡單,就不貼出來了,主要看Activity的代碼:

package com.chenyu.myaidl;

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.util.Log;
import android.view.View;
import android.widget.Button;
import com.chenyu.service.IMyAidl;
import com.chenyu.service.Person;

import java.util.List;

public class MainActivity extends Activity implements View.OnClickListener {

private Button btn;  
IMyAidl iMyAidl;  
private ServiceConnection conn=new ServiceConnection() {  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        Log.d("cylog", "onServiceConnected success");  
        iMyAidl=IMyAidl.Stub.asInterface(service);  //  1  

    }  

    @Override  
    public void onServiceDisconnected(ComponentName name) {  
        Log.d("cylog", "onServicedisConnected ");  
        iMyAidl=null;  
    }  
};  

@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    initView();  
    bindService();  
}  

private void initView() {  
    btn= (Button) findViewById(R.id.cal);  
    btn.setOnClickListener(this);  
}  

@Override  
public void onClick(View v) {  
    try {  
        iMyAidl.addPerson(new Person(21, "陳育", 22255));  
        List<Person> persons = iMyAidl.getPersonList();  //   2  
        Log.d("cylog",persons.toString());  
    } catch (RemoteException e) {  
        e.printStackTrace();  
    }  
}  

private void bindService() {  
    Intent intent=new Intent();  
    intent.setComponent(new ComponentName("com.chenyu.service","com.chenyu.service.MyAidlService"));  
    bindService(intent,conn, Context.BIND_AUTO_CREATE);  
}  

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

}</pre>

        與綁定一般Service的語法差不多,但是在安卓5.0之后,必須顯式指定Service的包名和類名即:

private void bindService() {  
        Intent intent=new Intent();  
        intent.setComponent(new ComponentName("com.chenyu.service","com.chenyu.service.MyAidlService"));  
        bindService(intent,conn, Context.BIND_AUTO_CREATE);  
    }

        在bindService(intent,conn,Context.BIND_AUTO_CREATE)方法中有幾個參數需要說明一下,
①conn:該參數代表了與服務端的連接,即ServiceConnection.
②Context.BIND_AUTO_CREATE:該參數表示綁定的同時創建一個Service。
        在發出請求綁定成功之后,會回調①處的代碼,此時,可在回調方法onServiceConnected()方法中,獲取服務端返回的IMyAidl實例,在客戶端拿到該實例之后,就可以通過調用相應的方法進行遠程通訊了,比如上述的②處代碼。
最后,看一下運行結果,先運行service,然后運行app:

運行結果

進程顯示


        可以看出,app端和service端的確是構成了進程間通訊,并且完成了進程間通訊。
        以上是利用AIDL實現進程通訊的基本方法,希望對大家有所幫助。關于AIDL的核心原理以及Binder,AIDL優化,會在下一篇文章詳細講述。

來自: http://www.jianshu.com/p/b9b15252b3d6

 本文由用戶 JimDcf 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!