Android IPC之AIDL看這一篇還不夠

一、AIDL是什么?

如果你想深入學(xué)習(xí)基于Binder通信的知識(shí),請(qǐng)看我之前的文章:

Android IPC之代理模式
Android IPC之Binder機(jī)制分析

AIDL(Android Interface Define Language) 是IPC進(jìn)程間通信方式的一種.用于生成可以在Android設(shè)備上兩個(gè)進(jìn)程之間進(jìn)行進(jìn)程間通信(interprocess communication, IPC)的代碼。
AIDL規(guī)定了兩個(gè)進(jìn)程之間如何使用Binder來通信,AIDL文件就是接口和數(shù)據(jù)聲明。Android Studio利用我們寫好的AIDL文件生成Java文件,真正在打包成apk文件時(shí)會(huì)把a(bǔ)idl文件生成的java代碼打包進(jìn)去,不會(huì)打包AIDL文件,那么很好理解,Android Studio利用我們寫好的AIDL文件生成基于Binder進(jìn)程通信java代碼。binder進(jìn)程通信是C/S和代理模式實(shí)現(xiàn)的。ADIL文件生成的java文件中可以看出來。
一句話總結(jié):

Android Studio利用我們寫好的AIDL文件生成基于Binder進(jìn)程通信java代碼。生成的java代碼會(huì)被打包到apk文件,AIDL文件只是一個(gè)模具而已。

基于Binder的AIDL數(shù)據(jù)通信流程圖:

這里寫圖片描述
這里寫圖片描述

二、AIDL通信支持的數(shù)據(jù)傳送類型

2.1 Android Binder支持傳送數(shù)據(jù)類型

進(jìn)程通信就是交換數(shù)據(jù),AIDL通信傳送支持的類型其實(shí)就是Binder支持的,AIDL并不是支持所有的數(shù)據(jù)類型,java中有很多數(shù)據(jù)類型,java基本的數(shù)據(jù)類型,集合數(shù)據(jù)類型,還有我們用戶自己定義的數(shù)據(jù)類型。那么AIDL支持哪些列?

共 4 種:

  1. Java 的基本數(shù)據(jù)類型
  2. List 和 Map
    元素必須是 AIDL 支持的數(shù)據(jù)類型
    Server 端具體的類里則必須是 ArrayList 或者 HashMap
  3. 其他 AIDL 生成的接口
  4. 實(shí)現(xiàn) Parcelable 的實(shí)體

2.2 Android序列化方式解析

其中List和Map集合裝的實(shí)體也是要實(shí)現(xiàn)Parcelable 接口的,或者直接傳遞一個(gè)實(shí)現(xiàn)了Parcelable 接口的實(shí)體類。Parcelable 是Android自帶的序列化接口,那么相比Java自帶序列化接口有什么異同列?

1. Serializable(Java自帶):
Serializable是序列化的意思,表示將一個(gè)對(duì)象轉(zhuǎn)換成可存儲(chǔ)或可傳輸?shù)臓顟B(tài)。序列化后的對(duì)象可以在網(wǎng)絡(luò)上進(jìn)行傳輸,也可以存儲(chǔ)到本地。實(shí)現(xiàn)方式很簡(jiǎn)單,只需要實(shí)現(xiàn)Serializable就可以了。

2. Parcelable(android 專用):
Serializable是序列化的意思,表示將一個(gè)對(duì)象轉(zhuǎn)換成可存儲(chǔ)或可傳輸?shù)臓顟B(tài)。序列化后的對(duì)象可以在網(wǎng)絡(luò)上進(jìn)行傳輸,也可以存儲(chǔ)到本地。實(shí)現(xiàn)方式復(fù)雜,需要復(fù)寫兩個(gè)方法,分別是describeContents和writeToParcel,實(shí)例化靜態(tài)內(nèi)部對(duì)象CREATOR,實(shí)現(xiàn)接口Parcelable.Creator 。這兩個(gè)Android Studio可以幫我智能實(shí)現(xiàn),另外左右的變量都需要實(shí)現(xiàn)set和get方法,Android Studio的快捷方式也是很方便實(shí)現(xiàn)的額。

選擇序列化方法的原則:

1) 在使用內(nèi)存的時(shí)候,Parcelable比Serializable性能高,所以推薦使用Parcelable。

2) Serializable在序列化的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,從而引起頻繁的GC。

3) Parcelable不能使用在要將數(shù)據(jù)存儲(chǔ)在磁盤上的情況,因?yàn)镻arcelable不能很好的保證數(shù)據(jù)的持續(xù)性在外界有變化的情況下。盡管Serializable效率低點(diǎn),但此時(shí)還是建議使用Serializable 。

注意:
需要在多個(gè)部件(Activity或Service)之間通過Intent傳遞一些數(shù)據(jù),簡(jiǎn)單類型(如:數(shù)字、字符串)的可以直接放入Intent。復(fù)雜類型必須實(shí)現(xiàn)Parcelable接口。需要傳輸?shù)膶?shí)現(xiàn)了Parcelable接口的實(shí)體類,在Client端和Server端都需要用一個(gè).aidl文件上聲明一下。

可以先看一下我們需要傳輸?shù)囊呀?jīng)實(shí)現(xiàn)了Parcelable接口的實(shí)體類:

package com.server.data;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
/**
 * Created by dawish on 2017/8/22.
 * Binder傳遞實(shí)體類數(shù)據(jù)必須實(shí)現(xiàn)Parcelable接口,也可以更高效地在內(nèi)存中傳輸
 */
public class ServiceData implements Parcelable {
    public String id;
    public String name;
    public String price;
    public String type;
    public ServiceData( ) {
    }
    /**
     * 讀數(shù)據(jù)恢復(fù)
     * 給createFromParcel里面用,IDE自動(dòng)生成
     * @param in
     */
    protected ServiceData(Parcel in) {
        id = in.readString();
        name = in.readString();
        price = in.readString();
        type = in.readString();
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPrice() {
        return price;
    }
    public void setPrice(String price) {
        this.price = price;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getId() {
        return id;
    }

    /**
     *  IDE自動(dòng)生成
     *  讀取接口,目的是要從Parcel中構(gòu)造一個(gè)實(shí)現(xiàn)了Parcelable的類的實(shí)例處理。
     *  因?yàn)閷?shí)現(xiàn)類在這里還是不可知的,所以需要用到模板的方式,繼承類名通過模板
     *  參數(shù)傳入。
     *  為了能夠?qū)崿F(xiàn)模板參數(shù)的傳入,這里定義Creator嵌入接口,內(nèi)含兩個(gè)接口函數(shù)
     *  分別返回單個(gè)和多個(gè)繼承類實(shí)例。
     */
    public static final Creator<ServiceData> CREATOR = new Creator<ServiceData>() {
        @Override
        public ServiceData createFromParcel(Parcel in) {
            return new ServiceData(in);
        }

        @Override
        public ServiceData[] newArray(int size) {
            Log.i("danxx", "newArray size--->"+size);
            return new ServiceData[size];
        }
    };

    /**
     * 內(nèi)容描述接口,基本不用管,IDE自動(dòng)生成
     */
    @Override
    public int describeContents() {
        return 0;
    }
    /**
     * 寫入接口函數(shù),打包,IDE自動(dòng)生成
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(id);
        dest.writeString(name);
        dest.writeString(price);
        dest.writeString(type);
    }
}

三、aidl文件編寫

3.1 aidl文件編寫注意事項(xiàng)

Android Studio寫aidl文件還不是很智能的,寫aidl文件就像是寫txt文本文件,沒有智能提示的,所以很很關(guān)鍵的是里面的package 包名和import導(dǎo)包都要十分小心,不能寫錯(cuò)了,Android Studio里面會(huì)有一個(gè)aidl文件夾,新建的aidl文件都在這個(gè)文件夾里面,這個(gè)文件夾里面還可以建立包名。包名跟java實(shí)體類的包名也要完全一致,不然也會(huì)報(bào)錯(cuò)??偨Y(jié)需要注意的兩點(diǎn):

3.1.1 Android Studio aidl文件夾里的包名要跟對(duì)應(yīng)java實(shí)體類包名一致

如果不一致會(huì)報(bào)錯(cuò),無法找到對(duì)應(yīng)的java類

這里寫圖片描述

3.1.2 aidl文件里面package 包名和import導(dǎo)包都要跟java實(shí)體類包名一致

不一致也是會(huì)報(bào)錯(cuò)的,導(dǎo)包不對(duì)直接無法識(shí)別類

這里寫圖片描述

3.1.3 注意細(xì)節(jié)

1.接口名和aidl文件名相同。
2.接口和方法前不用加訪問權(quán)限修飾符public,private,protected等,也不能用final,static。
3.Aidl默認(rèn)支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),
使用這些類型時(shí)不需要import聲明。對(duì)于List和Map中的元素類型必須是Aidl支持的類型。
如果使用自定義類型作為參數(shù)或返回值,自定義類型必須實(shí)現(xiàn)Parcelable接口。
4.自定義類型和AIDL生成的其它接口類型在aidl描述文件中,應(yīng)該顯式import,即便在該類和定義的包在同一個(gè)包中。
5.在aidl文件中所有非Java基本類型(需要實(shí)現(xiàn)Parcelable接口)參數(shù)必須加上in、out、inout標(biāo)記,
以指明參數(shù)是輸入?yún)?shù)、輸出參數(shù)還是輸入輸出參數(shù)。
6.Java原始類型默認(rèn)的標(biāo)記為in,不能為其它標(biāo)記。

注意:
我們想一下aidl文件最后都是要變成java文件的,文件的命名和導(dǎo)包的包名都要跟實(shí)際java類一模一樣。不然生成的java類里面也是有錯(cuò)誤的,那么有可能導(dǎo)致直接無法生成R.java類,那么我們項(xiàng)目中在使用id的時(shí)候,R類都是找不到的。在我們的apk打包aidl工具在處理aidl文件時(shí)也是無法通過的。Android apk打包請(qǐng)看我之前的文章:Android apk打包流程

3.2編寫AIDL文件

主要的aidl文件,會(huì)根據(jù)此aidl文件生成基于binder夸進(jìn)程通信的java代碼:

// MyAIDLService.aidl
//生產(chǎn)的MyAIDLService.java類會(huì)使用這個(gè)包名
package com.server.service.aidl;
//導(dǎo)入數(shù)據(jù)javabean類,注意包名不要錯(cuò)
import com.server.service.data.ServiceData;
// Declare any non-default types here with import statements
interface MyAIDLService {
    /**
     * 除了基本數(shù)據(jù)類型,其他類型的參數(shù)都需要標(biāo)上方向類型:in(輸入), out(輸出), inout(輸入輸出)
     */
    void addData(in ServiceData data);

    List<ServiceData> getDataList();
}

主要里面使用到的java數(shù)據(jù)類,這個(gè)累必須實(shí)現(xiàn)Parcelable接口,首先看一下用aidl文件聲明一下這個(gè)java類:

// 實(shí)體類包名,要跟實(shí)際的ServiceData.java包名一致
package com.server.service.data;

//實(shí)現(xiàn)了Parcelable序列化接口的實(shí)體類
parcelable ServiceData;

實(shí)現(xiàn)Parcelable接口的java數(shù)據(jù)類:

package com.server.service.data;

import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

/**
 * Created by dawish on 2017/8/22.
 */

public class ServiceData implements Parcelable {
    public String id;
    public String name;
    public String price;
    public String type;
    public ServiceData( ) {

    }
    /**
     * 讀數(shù)據(jù)恢復(fù)
     * 系統(tǒng)自動(dòng)添加,給createFromParcel里面用
     * @param in
     */
    protected ServiceData(Parcel in) {
        id = in.readString();
        name = in.readString();
        price = in.readString();
        type = in.readString();
    }
    public String getName() {
        return name;
    }

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

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    /**
     *  IDE自動(dòng)生成
     *  讀取接口,目的是要從Parcel中構(gòu)造一個(gè)實(shí)現(xiàn)了Parcelable的類的實(shí)例處理。
     *  因?yàn)閷?shí)現(xiàn)類在這里還是不可知的,所以需要用到模板的方式,繼承類名通過模板
     *  參數(shù)傳入。
     * 為了能夠?qū)崿F(xiàn)模板參數(shù)的傳入,這里定義Creator嵌入接口,內(nèi)含兩個(gè)接口函數(shù)
     * 分別返回單個(gè)和多個(gè)繼承類實(shí)例。
     */
    public static final Creator<ServiceData> CREATOR = new Creator<ServiceData>() {
        @Override
        public ServiceData createFromParcel(Parcel in) {
            return new ServiceData(in);
        }

        @Override
        public ServiceData[] newArray(int size) {
            Log.i("danxx", "newArray size--->"+size);
            return new ServiceData[size];
        }
    };

    /**
     * 內(nèi)容描述接口,基本不用管,IDE自動(dòng)生成
     */
    @Override
    public int describeContents() {
        return 0;
    }
    /**
     * 寫入接口函數(shù),打包,IDE自動(dòng)生成
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(id);
        dest.writeString(name);
        dest.writeString(price);
        dest.writeString(type);
    }
}

相關(guān)的解釋已經(jīng)在代碼中了,我這里就不解釋了。
上面的兩個(gè)aidl文件和一個(gè)java數(shù)據(jù)類是Server端和Client端共有的 ,上面寫好的aidl文件和用到的java數(shù)據(jù)類都復(fù)制一份在Server端和Client端,這些是進(jìn)程通信的鏈接器。
接下來就是便攜Server端的Service和Client的服務(wù)連接和請(qǐng)求代碼了:

3.2.1 Server端代碼編寫

public class AidlService extends Service {

    public static final String TAG = "MyService";

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand() executed");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy() executed");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return remoteBinder;
    }

    /**
     * 本地service用法 ;
     */
    /***/
    private List<ServiceData> mData = new ArrayList<>();
    /**
     * 遠(yuǎn)程service需要使用AIDL來通訊,其實(shí)也是基于Binder,只是Google規(guī)定了寫法
     */
    MyAIDLService.Stub remoteBinder = new MyAIDLService.Stub() {

        @Override
        public void addData(ServiceData data) throws RemoteException {
            mData.add(data);
        }

        @Override
        public List<ServiceData> getDataList() throws RemoteException {
            return mData;
        }
    };

}

Server的service需要在當(dāng)前項(xiàng)目中的AndroidManifest.xml文件中聲明一下:

        <service android:name=".service.binder.BinderService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.danxx.binderService" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>

需要提供intent-filter,這樣方便遠(yuǎn)程的Client端連接我們的服務(wù)。

3.2.2 Client端代碼編寫

Client需要注意就是遠(yuǎn)程的Server端的Service進(jìn)程是有可能被殺死的,我們需要在Client端監(jiān)聽遠(yuǎn)程的Binder是不是被殺死了,使用DeathRecipient,遠(yuǎn)程的Binder被殺死會(huì)被回調(diào):

//遠(yuǎn)程服務(wù)連接成功后監(jiān)聽遠(yuǎn)程的Binder死亡回調(diào)
new ServiceConnection() {
        /**
         * service連接成功后的回調(diào)
         * @param name
         * @param service  通訊接口
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            showMessage("-----遠(yuǎn)程服務(wù)鏈接成功-----", R.color.praised_num);
            isBound = true;
            myAIDLService = MyAIDLService.Stub.asInterface(service); //遠(yuǎn)程service寫法
            try {
                //設(shè)置死亡代理
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
           }
      }
  }
    /**
     * 監(jiān)聽Binder是否死亡
     */
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (myAIDLService == null) {
                return;
            }
            //死亡后解除綁定
            myAIDLService.asBinder().unlinkToDeath(mDeathRecipient, 0);
            myAIDLService = null;
            //重新綁定
            doBindService();
            showMessage("--DeathRecipient后重鏈遠(yuǎn)程服務(wù)--", R.color.red_wine);
        }
    };

Client的完整代碼:

public class ActivityAidlClient extends AppCompatActivity {

    @BindView(R.id.bind_service)
    public Button bindService;

    @BindView(R.id.unbind_service)
    public Button unbindService;

    @BindView(R.id.addData)
    public Button addData;

    @BindView(R.id.getData)
    public Button getData;

    @BindView(R.id.txt)
    TextView tvInfo;

    @BindView(R.id.scrollView)
    ScrollView scrollView;

    /**服務(wù)是否連接*/
    public boolean isBound = false;
    /**遠(yuǎn)程service使用*/
    public MyAIDLService myAIDLService;
    private SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
    /**
     *  ServiceConnection is Interface for monitoring the state of an application service
     *  ServiceConnection是一個(gè)觀察程序service的回調(diào)接口
     */
    ServiceConnection serviceConnection = new ServiceConnection() {
        /**
         * service連接成功后的回調(diào)
         * @param name
         * @param service  通訊接口
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            showMessage("-----遠(yuǎn)程服務(wù)鏈接成功-----", R.color.praised_num);
            isBound = true;
            myAIDLService = MyAIDLService.Stub.asInterface(service); //遠(yuǎn)程service寫法
            try {
                //設(shè)置死亡代理
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
            myAIDLService = null;
            showMessage("-----遠(yuǎn)程服務(wù)已斷開連接-----", R.color.red_deep);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl_client);
        ButterKnife.bind(ActivityAidlClient.this);
        showMessage("-----遠(yuǎn)程服務(wù)等待連接-----", R.color.green_pure);
    }

    /**
     * Service 的兩種啟動(dòng)方法和區(qū)別
     Service的生命周期方法onCreate, onStart, onDestroy
     有兩種方式啟動(dòng)一個(gè)Service,他們對(duì)Service生命周期的影響是不一樣的。
     1 通過startService
       Service會(huì)經(jīng)歷 onCreate -> onStart
      stopService的時(shí)候直接onDestroy
     如果是調(diào)用者自己直接退出而沒有調(diào)用stopService的話,Service會(huì)一直在后臺(tái)運(yùn)行。下次調(diào)用者再起來可以stopService。
     2 通過bindService
       Service只會(huì)運(yùn)行onCreate, 這個(gè)時(shí)候服務(wù)的調(diào)用者和服務(wù)綁定在一起
     調(diào)用者退出了,Srevice就會(huì)調(diào)用onUnbind->onDestroyed所謂綁定在一起就共存亡了。并且這種方式還可以使得
     *
     */

    @OnClick({R.id.bind_service, R.id.unbind_service,R.id.addData,R.id.getData})
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bind_service:
                if (!isBound) {
                    //用Intent匹配的方式綁定service
                    doBindService();
                }
                break;
            case R.id.unbind_service:
                if(isBound && myAIDLService != null && serviceConnection!=null) {
                    unbindService(serviceConnection);
                    isBound = false;
                    showMessage("-----主動(dòng)斷開遠(yuǎn)程連接-----", R.color.red_deep);
                }
                break;
            case R.id.addData:
                addData();
                break;
            case R.id.getData:
                getData();
                break;
            default:
                break;
        }
    }

    private void addData(){
        if(isBound){
            ServiceData serviceData = new ServiceData();
            try {
                int size = myAIDLService.getDataList().size();
                serviceData.setName("no-"+size);
                serviceData.setId(String.valueOf(size));
                serviceData.setPrice(String.valueOf(size+2));
                serviceData.setType(String.valueOf(size%8));

                StringBuffer stringBuffer = new StringBuffer();
                stringBuffer.append("Name:"+serviceData.getName());
                stringBuffer.append("  Id:"+serviceData.getId());
                stringBuffer.append("  Type:"+serviceData.getType());
                stringBuffer.append("  Price:"+serviceData.getPrice());
                showMessage(stringBuffer.toString(), R.color.blue_color);
                myAIDLService.addData(serviceData);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }else {
            Toast.makeText(this,"服務(wù)還未連接!", Toast.LENGTH_SHORT).show();
        }

    }

    private void getData(){
        if(isBound){
            try {
                int size = myAIDLService.getDataList().size();
//                tvInfo.setText("Data size: "+ size);
                showMessage("Data size: "+ size, R.color.blue_color);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }else {
            Toast.makeText(this,"服務(wù)還未連接!", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 顯示文字
     *
     * @param info 提示信息
     */
    private void showMessage(final String info, final int color) {
        tvInfo.post(new Runnable() {
            @Override
            public void run() {
                int startPos = stringBuilder.length();
                stringBuilder.append("\n"+info);
                tvInfo.setText(DisplayUtil.changeTextColor(ActivityAidlClient.this, stringBuilder, color, startPos));
            }
        });
        tvInfo.postDelayed(new Runnable() {
            @Override
            public void run() {
                if(null != scrollView) scrollView.fullScroll(ScrollView.FOCUS_DOWN);
            }
        },100);
    }

    protected void showToast(String msg){
        Toast.makeText(ActivityAidlClient.this, msg,Toast.LENGTH_SHORT).show();
    }

    //客戶端使用死亡代理,可以重啟service
    //http://blog.csdn.net/liuyi1207164339/article/details/51706585
    //服務(wù)端使用死亡回調(diào)回收數(shù)據(jù)
    //http://www.cnblogs.com/punkisnotdead/p/5158016.html
    //死亡通知原理分析
    //http://light3moon.com/2015/01/28/Android%20Binder%20%E5%88%86%E6%9E%90%E2%80%94%E2%80%94%E6%AD%BB%E4%BA%A1%E9%80%9A%E7%9F%A5[DeathRecipient]/
    /**
     * 監(jiān)聽Binder是否死亡
     */
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (myAIDLService == null) {
                return;
            }
            //死亡后解除綁定
            myAIDLService.asBinder().unlinkToDeath(mDeathRecipient, 0);
            myAIDLService = null;
            //重新綁定
            doBindService();
            showMessage("--DeathRecipient后重鏈遠(yuǎn)程服務(wù)--", R.color.red_wine);
        }
    };

    private void doBindService(){
        showMessage("-----開始鏈接遠(yuǎn)程服務(wù)-----", R.color.light_yellow);
        Intent intent = new Intent();
        intent.setAction("com.danxx.aidlService");
        intent.setPackage("com.server");
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }
}

對(duì)應(yīng)的XML文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:background="#f2f2f2"
        android:layout_margin="8dp">
        <TextView
            android:id="@+id/txt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="22sp"
            android:gravity="left"
            android:padding="8dp"/>
    </ScrollView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="10dp">
        <Button
            android:id="@+id/bind_service"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:textAllCaps="false"
            android:background="@drawable/btn_selector_done"
            android:layout_marginRight="4dp"
            android:text="Bind Service" />

        <Button
            android:id="@+id/unbind_service"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:background="@drawable/btn_selector_done"
            android:layout_height="wrap_content"
            android:textAllCaps="false"
            android:layout_marginLeft="4dp"
            android:text="Unbind Service" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="10dp">
        <Button
            android:id="@+id/addData"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:textAllCaps="false"
            android:background="@drawable/btn_selector_done"
            android:layout_marginRight="4dp"
            android:text="add data" />

        <Button
            android:id="@+id/getData"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:background="@drawable/btn_selector_done"
            android:textAllCaps="false"
            android:layout_marginLeft="4dp"
            android:text="get data" />
    </LinearLayout>

</LinearLayout>

四、AIDL生成的Java代碼分析

代碼中的幾個(gè)方法:
1、DESCRIPTION
Binderd的唯一標(biāo)識(shí),一般用當(dāng)前的類名表示。
2、asInterface(android.os.IBinder obj)
用于將服務(wù)端的Binder對(duì)象轉(zhuǎn)換為客戶端需要的AIDL接口類型的對(duì)象,轉(zhuǎn)換區(qū)分進(jìn)程,客戶端服務(wù)端位于同一進(jìn)程,返回服務(wù)端的 //Stub對(duì)象本身;否則返回的是系統(tǒng)的封裝后的Stub.proxy對(duì)象。
3、asBInder
返回Binder對(duì)象
4、onTransact
此方法運(yùn)行在服務(wù)端中的Binder線程池中,當(dāng)客戶端發(fā)起跨進(jìn)程請(qǐng)求時(shí),遠(yuǎn)程請(qǐng)求會(huì)通過系統(tǒng)底層封裝后交由此方法處理。
5、Proxy#add
此 方法運(yùn)行在客戶端,當(dāng)客戶端遠(yuǎn)程調(diào)用此方法時(shí),它的內(nèi)部實(shí)現(xiàn)是這樣的:首先創(chuàng)建該方法所需要的輸入型Parcel對(duì)象_data、輸出型Parcel對(duì)象 _reple和返回值對(duì)象_result,然后將該方法的參數(shù)信息寫入_data中;接著調(diào)用transact方法來發(fā)RPC請(qǐng)求,同時(shí)當(dāng)前線程掛起;然 后服務(wù)端的onTransact方法會(huì)被調(diào)用,直到RPC過程返回后,當(dāng)前線程繼續(xù)執(zhí)行,并從_reply中取出RPC過程返回的結(jié)果,寫入 _result中。
AIDL文件生成java文件詳細(xì)解釋:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\GitHubCode\\AndReservoir\\Server\\src\\main\\aidl\\com\\server\\service\\aidl\\MyAIDLService.aidl
 */
package com.server.service.aidl;
// Declare any non-default types here with import statements
//為了讓大家看的更清楚 我把生成的binder代碼 給拷貝到另外一個(gè)工程下面了,并且用ide 給他format
public interface MyAIDLService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     * 并且這個(gè)接口里 有一個(gè)靜態(tài)的抽象類Stub(注意這個(gè)名字是固定的 永遠(yuǎn)都是Stub 不會(huì)是其他)
     * 并且這個(gè)Stub是Binder的子類,并且實(shí)現(xiàn)了 MyAIDLService 這個(gè)接口
     * 這是Server服務(wù)端真是對(duì)象,具備真正的功能。
     */
    public static abstract class Stub extends android.os.Binder implements com.server.service.aidl.MyAIDLService {
        /**
         * 這個(gè)東西就是唯一的binder標(biāo)示 可以看到就是IPersonManager的全路徑名
         * SM是DNS Binder驅(qū)動(dòng)是路由器,這個(gè)就是類似一個(gè)ip地址。
         */
        private static final java.lang.String DESCRIPTOR = "com.server.service.aidl.MyAIDLService";

        /**
         * Construct the stub at attach it to the interface.
         * 這個(gè)就是Stub的構(gòu)造方法,供服務(wù)端調(diào)用,創(chuàng)建一個(gè)Binder類
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.server.service.aidl.MyAIDLService interface,
         * generating a proxy if needed.
         *這個(gè)方法 其實(shí)就做了一件事,如果是同一個(gè)進(jìn)程,那么就返回Stub對(duì)象本身
         * 如果不是同一個(gè)進(jìn)程,就返回Stub.Proxy這個(gè)代理對(duì)象了
         */
        public static com.server.service.aidl.MyAIDLService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //如果是同1個(gè)進(jìn)程,也就是說進(jìn)程內(nèi)通信的話 我們就返回括號(hào)內(nèi)里的對(duì)象
            if (((iin != null) && (iin instanceof com.server.service.aidl.MyAIDLService))) {
                return ((com.server.service.aidl.MyAIDLService) iin);
            }
            //如果不是同一進(jìn)程,是2個(gè)進(jìn)程之間相互通信,那我們就得返回這個(gè)Stub.Proxy 看上去叫Stub 代理的對(duì)象了
            return new com.server.service.aidl.MyAIDLService.Stub.Proxy(obj);
        }

        //返回當(dāng)前對(duì)象 服務(wù)端調(diào)用
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        //只有在多進(jìn)程通信的時(shí)候 才會(huì)調(diào)用這個(gè)方法 ,同一個(gè)進(jìn)程是不會(huì)調(diào)用的。

        //首先 我們要明白 這個(gè)方法 一般情況下 都是返回true的,也只有返回true的時(shí)候才有意義,如果返回false了 就代表這個(gè)方法執(zhí)行失敗,
        //所以我們通常是用這個(gè)方法來做權(quán)限認(rèn)證的,其實(shí)也很好理解,既然是多進(jìn)程通信,那么我們服務(wù)端的進(jìn)程當(dāng)然不希望誰都能過來調(diào)用
        //所以權(quán)限認(rèn)證是必須的,關(guān)于權(quán)限認(rèn)證的代碼 以后我再講 先略過。

        //除此之外 ,onTransact 這個(gè)方法 就是運(yùn)行在Binder線程池中的,一般就是客戶端發(fā)起請(qǐng)求,然后android底層代碼把這個(gè)客戶端發(fā)起的
        //請(qǐng)求 封裝成3個(gè)參數(shù) 來調(diào)用這個(gè)onTransact方法,第一個(gè)參數(shù)code 就代表客戶端想要調(diào)用服務(wù)端 方法的 標(biāo)志位。
        //其實(shí)也很好理解 服務(wù)端可能有n個(gè)方法 每個(gè)方法 都有一個(gè)對(duì)應(yīng)的int值來代表,這個(gè)code就是這個(gè)int值,用來標(biāo)示客戶端想調(diào)用的服務(wù)端的方法
        //data就是方法參數(shù),reply就是方法返回值。都很好理解

        //其實(shí)隱藏了很重要的一點(diǎn),這個(gè)方法既然是運(yùn)行在binder線程池中的,所以在這個(gè)方法里面調(diào)用的服務(wù)器方法也是運(yùn)行在Binder線程池中的,
        //所以我們要記得 如果你的服務(wù)端程序 有可能和多個(gè)客戶端相聯(lián)的話,你方法里使用的那些參數(shù) 必須要是支持異步的,否則的話
        //值就會(huì)錯(cuò)亂了!這點(diǎn)一定要記??!結(jié)論就是Binder方法 一定要是同步方法!?。。。?!

        /**
         *@param code  唯一標(biāo)識(shí),客戶端傳遞標(biāo)識(shí)執(zhí)行服務(wù)端代碼
         *@param data  客戶端傳遞過來的參數(shù)
         *@param reply 服務(wù)器返回回去的值
         *@param flags 是否有返回值 0:有 1:沒有
         *@return
         *@throws RemoteException 異常
         */
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {  //開始連接
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_addData: { //客服端請(qǐng)求區(qū)分1
                    data.enforceInterface(DESCRIPTOR);
                    com.server.service.data.ServiceData _arg0;
                    if ((0 != data.readInt())) {
                        //讀取客戶端傳遞過來的參數(shù)
                        _arg0 = com.server.service.data.ServiceData.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addData(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getDataList: { //客服端請(qǐng)求區(qū)分2
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.server.service.data.ServiceData> _result = this.getDataList();
                    reply.writeNoException();
                    //想客戶端寫入?yún)?shù)
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            //利用Binder驅(qū)動(dòng)發(fā)送數(shù)據(jù)
            return super.onTransact(code, data, reply, flags);
        }
        //注意這里的Proxy 這個(gè)類名也是不變的,從前文我們知道 只有在多進(jìn)程通信的情況下  才會(huì)返回這個(gè)代理的對(duì)象
        //這個(gè)代理并不是Binder的子類,只是實(shí)現(xiàn)了MyAIDLService接口的一個(gè)影子,客戶端通過操作這個(gè)影子來實(shí)現(xiàn)對(duì)服務(wù)端的通信
        //這個(gè)代理端只是告訴客戶端 服務(wù)端擁有什么樣的功能可以提供。正真完整通信實(shí)現(xiàn)數(shù)據(jù)交換的還是服務(wù)端的Binder子類
        private static class Proxy implements com.server.service.aidl.MyAIDLService {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * 除了基本數(shù)據(jù)類型,其他類型的參數(shù)都需要標(biāo)上方向類型:in(輸入), out(輸出), inout(輸入輸出)
             * 客戶端調(diào)用
             */
            @Override
            public void addData(com.server.service.data.ServiceData data) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((data != null)) {
                        _data.writeInt(1);
                        //寫入請(qǐng)求參數(shù)
                        data.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    //想服務(wù)端發(fā)送請(qǐng)求
                    mRemote.transact(Stub.TRANSACTION_addData, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            /**
             * 客戶端調(diào)用
             * @return
             * @throws android.os.RemoteException
             */
            @Override
            public java.util.List<com.server.service.data.ServiceData> getDataList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.server.service.data.ServiceData> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getDataList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.server.service.data.ServiceData.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        //客服端請(qǐng)求區(qū)分1
        static final int TRANSACTION_addData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        //客服端請(qǐng)求區(qū)分2
        static final int TRANSACTION_getDataList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    /**
     * 除了基本數(shù)據(jù)類型,其他類型的參數(shù)都需要標(biāo)上方向類型:in(輸入), out(輸出), inout(輸入輸出)
     */
    public void addData(com.server.service.data.ServiceData data) throws android.os.RemoteException;

    public java.util.List<com.server.service.data.ServiceData> getDataList() throws android.os.RemoteException;
}

五、測(cè)試

我們?cè)诎裇erver端app和Client端app都安裝啟動(dòng)并在Client操作數(shù)據(jù)后,打開ADM:

這里寫圖片描述

選中遠(yuǎn)程的server進(jìn)程,點(diǎn)擊紅色的進(jìn)程停止stop按鈕,看看我們的死亡遠(yuǎn)程Binder死亡監(jiān)聽重連是否有效:

這里寫圖片描述
這里寫圖片描述

詳細(xì)代碼請(qǐng)參考:
https://github.com/Dawish/AndReservoir
看這一篇真的還不夠:
后面我還會(huì)寫手動(dòng)Client在Server端注冊(cè)監(jiān)聽回調(diào)接口,這樣在服務(wù)端支持多線程的情況下每一個(gè)客戶端都可以知道數(shù)據(jù)在變化以及變化后的結(jié)果。
手動(dòng)首先Binder機(jī)制通信,不依靠AIDL文件來實(shí)現(xiàn),雖然這里我已經(jīng)把AIDL實(shí)現(xiàn)IPC說得很清楚了,手動(dòng)實(shí)現(xiàn)Binder無非就是吧aidl文件生產(chǎn)的java代碼文件手動(dòng)實(shí)現(xiàn)一遍,按照C/S和代理模式來操作就可以了。也是難事!

參考:

  1. Android 進(jìn)階7:進(jìn)程通信之 AIDL 的使用
  2. Android 多進(jìn)程編程 15問15答!
  3. AIDL使用解析
  4. 序列化Serializable和Parcelable的理解和區(qū)別
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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