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

一、前言

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

二、什么是AIDL?

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

三、使用AIDL

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

項目結(jié)構(gòu)

  可以看到,筆者新建了一個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();  
} 

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

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

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

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

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

2、Person類,實現(xiàn)Parcelable接口
  在java文件夾中,創(chuàng)建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 +  
                '}';  
    }  
}

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

3、實現(xiàn)服務端
  上面我們定義了一個AIDL接口,接下來要做的是實現(xiàn)這個AIDL接口,在java/com.chenyu.service中,創(chuàng)建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 ");  
    }  
}

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

4、實現(xiàn)客戶端
  在實現(xiàn)客戶端之前,先確保把aidl的包復制過來,就相上面筆者所給出的結(jié)構(gòu)圖一樣,包括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);  
    }  
}

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

運行結(jié)果
進程顯示

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

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容