AIDL實現IPC代碼實例

本例要實現的效果是兩個app,client和server,客戶端能調用server端的Service提供的方法。

本次示例分爲客服端和服務器端,兩端的代碼目錄如下:

服務器端代碼目錄:

 

 

客戶端代碼目錄:

 

代碼說明

--->服務器端代碼:

首先,要創建一個AIDL接口,IPersonManager.aidl,裏面定義了服務器端提示給客戶端的方法。

// IPersonManager.aidl
package com.demo.aidlprojectserver;

import com.demo.aidlprojectserver.Person;  //要傳遞的自定義對象需要顯示的import,即使是在一個目錄

interface IPersonManager {
    List<Person> getPersons();

    void addPerson(in Person person); //除了基本數據類型,要添加 in out inout

    String greet(String name);

}

 

系統自動編譯會根據這個IPersonManager.aidl生成一個IPersonManager.java類。如圖所示:

這個IPersonManager.java裏的內部內IPersonManager.Stub就是一個Binder 

public static abstract class Stub extends android.os.Binder

提示服務的Service裏new一個這樣的Binder,通過這個Binder向外提示服務。

AIDLService.java

package com.demo.aidlprojectserver;

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 AIDLService extends Service {

    public static final String TAG = AIDLService.class.getSimpleName();

    @Override
    public void onCreate() {
        super.onCreate();
        try {
            mManage.addPerson(new Person("張三", "男", 22));
            mManage.addPerson(new Person("李四", "男", 30));
            mManage.addPerson(new Person("婉兒", "女", 18));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private List<Person> mPersons = new ArrayList<>();

    private IPersonManager.Stub mManage = new IPersonManager.Stub() {
        @Override
        public List<Person> getPersons() throws RemoteException {
            return mPersons;
        }

        @Override
        public void addPerson(Person person) throws RemoteException {
            Log.d(TAG, "client add person-----" + person.toString());
            mPersons.add(person);
        }

        @Override
        public String greet(String name) {
            return "熱烈祝賀" + name + "來我院視察";
        }
    };

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

由於客戶端也要定義同樣的IPersonManager.aidl(所屬的包目錄結構一樣,裏面的代碼也一樣),生成的IPersonManager.java類也一樣,所以客戶端就可以通過客戶端的IPersonManger來調用服務器端的Binder提示的方法,續而調用到Service提示的操作。

 

服務器端還有兩個文件沒有介紹:Person.java和Person.aidl。

默認情況下,AIDL 支持下列數據類型:
Java 編程語言中的所有原語類型(如 int、long、char、boolean 等等)
String/CharSequence
List

List 中的所有元素都必須是以上列表中支持的數據類型、其他 AIDL 生成的接口或您聲明的可打包類型。 可選擇將 List 用作“通用”類(例如,List)。另一端實際接收的具體類始終是 ArrayList,但生成的方法使用的是 List 接口。

Map

Map 中的所有元素都必須是以上列表中支持的數據類型、其他 AIDL 生成的接口或您聲明的可打包類型。 不支持通用 Map(如 Map< String,Integer> 形式的 Map)。 另一端實際接收的具體類始終是 HashMap,但生成的方法使用的是 Map 接口。 
您必須爲以上未列出的每個附加類型加入一個 import 語句,即使這些類型是在與您的接口相同的軟件包中定義。

通過 IPC 傳遞對象
有時一些簡單的數據無法滿足我們的需求,我們需要的一個自定義實體,這時就需要使用Parcelable,要傳遞的實體類必須支持 Parcelable 接口。

//Person.java
package com.demo.aidlprojectserver;

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

public class Person implements Parcelable {

    private String name;
    private String sex;
    private int age;

    protected Person(Parcel in) {
        this.name = in.readString();  //讀取的順序要和writeToParcel的write順序一致
        this.sex = in.readString();
        this.age = in.readInt();
    }

    public Person(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeString(sex);
        dest.writeInt(age);
    }

    @Override
    public String toString() {
        return name + ":" + sex + ":" + age;
    }
}

除了實現Parcelable接口外,系統還要求我們自定義的對象需要寫一個aidl文件。如Person.aidl

// Person.aidl
package com.demo.aidlprojectserver;

parcelable Person;

到此,服務器端代碼介紹完畢

 

--->客戶端代碼

和服務器端一樣,我們需要編寫IPersonManager.aidl(生成IPersonManager.java)文件,編寫Person.java,Person.aidl文件,並且package要和服務器端一樣。代碼目錄回看上面介紹。

最後直接看調用的代碼MainActivity.java

package com.demo.aidlprojectclient;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.demo.aidlprojectserver.IPersonManager;
import com.demo.aidlprojectserver.Person;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();
    private boolean connected = false;
    private IPersonManager manager;

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

        findViewById(R.id.bt_bind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClassName("com.demo.aidlprojectserver", "com.demo.aidlprojectserver.AIDLService");
                connected = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
            }
        });

        findViewById(R.id.bt_add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (connected) {
                    Person person = new Person("lili", "女", 18);
                    try {
                        manager.addPerson(person);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        findViewById(R.id.bt_greet).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (connected && manager != null) {
                    try {
                        Toast.makeText(MainActivity.this, manager.greet("曹操"), 0).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            manager = IPersonManager.Stub.asInterface(service);
            try {
                List<com.demo.aidlprojectserver.Person> personList = manager.getPersons();
                for (Person person : personList) {
                    Log.d(TAG, person.toString());
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

上面代碼中,就是AIDL通過Binder機制,讓客戶端拿到了服務器端的Binder對象,從而用服務器端的Binder去調用服務器端的Service提示的方法。

本例子的效果就是:客戶端通過調用 manager.greet("曹操"),調用到了另外一個進程(App)的方法:

    public String greet(String name) {
            return "熱烈祝賀" + name + "來我院視察";
        }
    };

返回結果是:熱烈祝賀曹操來我院視察

=========================================================================

 

 

至此完成了一次通過Binder(AIDL)實現跨進程通信。這和App通過跨進程通信調用系統服務是一樣的道理。如下實例:

Binder運行的實例解釋

首先我們看看我們的程序跨進程調用系統服務的簡單示例,實現浮動窗口部分代碼:

//獲取WindowManager服務引用
WindowManager wm = (WindowManager) getSystemService(getApplication().WINDOW_SERVICE);
//佈局參數layoutParams相關設置略...
View view = LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);

註冊服務(addService): 在Android開機啓動過程中,Android會初始化系統的各種Service,並將這些Service向ServiceManager註冊(即讓ServiceManager管理)。這一步是系統自動完成的。

獲取服務(getService): 客戶端想要得到具體的Service直接向ServiceManager要即可。客戶端首先向ServiceManager查詢得到具體的Service引用,通常是Service引用的代理對象,對數據進行一些處理操作。即第2行代碼中,得到的wm是WindowManager對象的引用。

+

使用服務: 通過這個引用向具體的服務端發送請求,服務端執行完成後就返回。即第6行調用WindowManager的addView函數,將觸發遠程調用,調用的是運行在systemServer進程中的WindowManager的addView函數。

代碼下載地址:https://github.com/tomyZhou/AidlDemo

 

更多Binder相關知識推薦如下:

Android 中的 IPC 方式  https://lrh1993.gitbooks.io/android_interview_guide/content/android/basis/ipc.html 

Android Binder機制及AIDL https://lrh1993.gitbooks.io/android_interview_guide/content/android/advance/binder.html

 從getSystemService()開始,開擼Binder通訊機制 https://www.jianshu.com/p/1050ce12bc1e

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章