IPC機制之AIDL的學習筆記(二)

前言

上篇文章已經寫了aidl的使用步驟,記錄了在編寫過程中踩過的坑。使用還是比較簡單的,雖然步驟中的細節(jié)比較多。但是有沒有想過這個aidl到底是怎么工作的呢。我們可不可以脫離aidl文件來實現我們的跨進程通信功能呢?答案當然是可以的。這篇文章我們主要去分析一下aidl的大體實現步驟,最后我們自己脫離aidl文件,寫一個java文件來代替它。實現我們上一篇實現的功能。

正文

  • 效果圖展示
    gif

    通過上篇的編寫,我們可以看到,aidl的工作流程圖這樣的。
    1.在服務端的aidl文件中定義提供給客戶端調用的接口方法
    2.在服務端的service定義一個binder,binder里面實現這個接口方法。然后在onbind方法中返回這個binder
    3.在客戶端的ServiceConnection中拿到這個binder,然后調用binder的方法,這樣就實現了客戶端調用服務端的方法了。
    我們從使用的地方開始看一下,首先看一下這個在客戶端拿到的這個IMyAidlInterface
binder = IMyAidlInterface.Stub.asInterface(service);

點擊去看

public static com.example.newserver.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//這里是從本地查看是否有這個IMyAidlInterface ,有的話直接返回
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.newserver.IMyAidlInterface))) {
return ((com.example.newserver.IMyAidlInterface)iin);
}
//沒有的話返回這個Proxy,一開始都是沒有的,我們去看看這個Proxy
return new com.example.newserver.IMyAidlInterface.Stub.Proxy(obj);
}

接著我們來看看這個Proxy

private static class Proxy implements com.example.newserver.IMyAidlInterface{

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

@Override
 public android.os.IBinder asBinder()//這個方法就是返回這個Binder
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
@Override public void addPerson(com.example.newserver.Person person) 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 ((person!=null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}

//可以看到,客戶端調用的getPersonList就是這個方法
@Override 
public java.util.List<com.example.newserver.Person> getPersonList() throws android.os.RemoteException
{
//_data是客戶端寫進去的參數,_reply是通過計算,服務返回來的值
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.newserver.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 參數說明:
// code:Client進程請求方法標識符。即Server進程根據該標識確定所請求的目標方法
// data:目標方法的參數。(Client進程傳進來的)
// reply:目標方法執(zhí)行后的結果(返回給Client進程)
//第四個參數是一個 int 值,它的作用是設置進行 IPC 的模式,為 0 表示數據可以雙向流通,即 _reply 流可以正常的攜帶數據回來,如果為 1 的話那么數據將只能單向流通,從服務端回來的 _reply 流將不攜帶任何數據。
mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
//讀取_reply值
_reply.readException();
//將reply值進行解序列化得到person的list,然后返回給客戶端
_result = _reply.createTypedArrayList(com.example.newserver.Person.CREATOR);
}
finally {
//data和reply的回收
_reply.recycle();
_data.recycle();
}
return _result;
}
}

上面我就拿getPersonList這個方法做了注釋,addPerson的流程也是一樣的。主要的方法就是調用了服務端傳過來的Binder的transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0),那為什么執(zhí)行了這個方法這個方法就執(zhí)行了服務的對應的接口方法了呢?其實是調用了IMyAidlInterface這個類里面的onTransact(code, data, reply, flags)方法,方法的參數值和Transact方法是一樣的。至于為什么是調用了這個onTrasact方法,這個就是系統(tǒng)底層的東西,這個并不得而知。這里就不去分析它了。
我們來看看onTransact(code, data, reply, flags)這個方法

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_addPerson:
{
data.enforceInterface(DESCRIPTOR);
com.example.newserver.Person _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.newserver.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
//這個TRANSACTION_getPersonList就是transact方法的code參數,標志識別哪個方法
case TRANSACTION_getPersonList:
{
data.enforceInterface(DESCRIPTOR);
//這里就是關鍵了,是調用了真正實現的方法,也就是Service里面的getPersonLisst
java.util.List<com.example.newserver.Person> _result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

這里同樣分析getPersonList這個方法,由于這個IMyAidlInterface是一個接口

public interface IMyAidlInterface extends android.os.IInterface

當執(zhí)行getPersonList的時候,執(zhí)行了this.getPersonList()這個方法,這個方法是IMyAidlInterface這個接口定義的沒有實現的方法。

public void addPerson(com.example.newserver.Person person) throws android.os.RemoteException;
public java.util.List<com.example.newserver.Person> getPersonList() throws android.os.RemoteException;
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

那問題來了,到底哪里實現它呢?答案就是SerVice里面的IMyAidlInterface.Stub這個實現類,這個實現類是繼承IMyAidlInterface這個接口的。

public static abstract class Stub extends android.os.Binder implements com.example.newserver.IMyAidlInterface

來看看Service里面的這個實現類

   IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public void addPerson(Person person) throws RemoteException {
            if (null == mPersonArrayList) {
                mPersonArrayList = new ArrayList<>();
            }
            mPersonArrayList.add(person);
        }

        @Override
        public List<Person> getPersonList() throws RemoteException {
            if (null != mPersonArrayList) {
              return mPersonArrayList;
            }
            return null;
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    };

看到這里,終于就明白了吧,就是調用了Service里面的方法。
來寫一個總結流程:
1.首先客戶端先通過ServiceConnection獲取了服務端的定義的一個binder
2.拿到binder,然后通過一個代理類Proxy調用服務端定義的方法。服務端定義的方法就會執(zhí)行Transact方法
3.Transact方法又會執(zhí)行Binder類的的方法onTransact方法
4.onTrsact方法調用對應服務端的實現類IMyAidlInterface.Stub里面的方法。
我們看到,這其實就是binder在中間起到了連接的作用,實際上實現了binder就實現了跨進程通信的能力,中間就是Transact方法和onTransact的執(zhí)行。我們也完全可以通過這個步驟來脫離aidl文件來實現這個功能。
來看一下怎么實現。

1.首先客戶端先通過ServiceConnection獲取了服務端的定義的一個binder

 ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            binder = MyProXy.getInstance(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

2.拿到binder,然后通過一個代理類Proxy調用服務端定義的方法。服務端定義的方法就會執(zhí)行Transact方法
這個代理類是在客戶端調用的。

public class MyProXy {

    public static final int ADD = 0;
    public static final int GET = 1;

    private static IBinder mRemote;
    private static MyProXy myProXy;

    public MyProXy() {
    }

    public static MyProXy getInstance(IBinder binder) {
        myProXy = new MyProXy();
        mRemote = binder;
        return myProXy;
    }

    /**
     * 添加一個英雄
     * @param person
     */
    public void addPerson(Person person) {

        Parcel _write = Parcel.obtain();
        Parcel _reply = Parcel.obtain();
        if (null != person) {
            _write.writeInt(1);
            person.writeToParcel(_write, 0);
        } else {
            _write.writeInt(0);
        }
        try {
            mRemote.transact(ADD, _write, _reply, 0);
            _reply.readException();
        } catch (RemoteException e) {
            e.printStackTrace();
        }finally {
            _write.recycle();
            _reply.recycle();
        }
    }

    /**
     * 獲取英雄列表
     * @return
     */
    public List<Person> getPersonList(){
        Parcel _data = Parcel.obtain();
        Parcel _reply = Parcel.obtain();
        List<Person> mList = new ArrayList<>();

        try {
            mRemote.transact(GET,_data,_reply,0);
            _reply.readException();
            mList = _reply.createTypedArrayList(Person.CREATOR);
        } catch (RemoteException e) {
            e.printStackTrace();
        }finally {
            _data.recycle();
            _reply.recycle();
        }
        return mList;
    }
}

3.Transact方法又會執(zhí)行Binder類的的方法onTransact方法
這個類是寫在服務端的,里面有抽象方法,供Service的實現類實現

public abstract class Stub extends Binder {

    public static final int ADD = 0;
    public static final int GET = 1;

    public abstract void addPerson(Person person);
    public abstract List<Person> getPerson();

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code){
            case ADD:
                Person person;
                if (0 != data.readInt()) {
                    person = Person.CREATOR.createFromParcel(data);
                } else {
                    person = null;
                }
                this.addPerson(person);
                reply.writeInt(1);
                reply.writeNoException();
                return true;
            case GET:
                List<Person> mList;
                mList = this.getPerson();
                reply.writeNoException();
                reply.writeTypedList(mList);
                return true;
            default:
                return super.onTransact(code, data, reply, flags);
        }
    }
}

4.onTrsact方法調用對應服務端的實現類IMyAidlInterface.Stub里面的方法

public class MyService extends Service {

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

    Stub mStub = new Stub() {
        @Override
        public void addPerson(Person person) {
            if (null == mPersonList) {
                mPersonList = new ArrayList<>();
            }
            if (null != person) {
                mPersonList.add(person);
            }
        }

        @Override
        public List<Person> getPerson() {
            if (null != mPersonList) {
                return mPersonList;
            }
            return null;
        }
    };

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

到這里就結束了,可以看到實現還是挺簡單的。

結語

Android中跨進程通信的方法還是挺多的,這兩篇只是講了其中aidl方式。

image.png

附上Demo傳送門~

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容