1. 程式人生 > >理解AIDL中的in,out,inout

理解AIDL中的in,out,inout

最後一次更新:2018-3-25

  • android測試版本:Android O
  • 內容:驗證Android AIDL中的tag(in out inout)

前言

android AIDL有3種不同的tag,在總結之前先參考了這篇部落格 https://blog.csdn.net/luoyanglizi/article/details/51958091
但是發現還是不夠,於是又自己研究了一下,總結出這篇文件。如果有錯誤的話希望指正。

驗證

IPostMsg.aidl

package aidl;
import aidl.MsgBody;
interface IPostMsg {
    void
setMsg(String msg); //(1)沒有宣告out能返回嗎? == 能 MsgBody getMsg1(); //(2)out 不能傳入,服務端能修改客戶端的b引數 MsgBody getMsg2(out MsgBody b); //(3)in 能傳入,服務端不能修改客戶端的b引數 MsgBody getMsg3(in MsgBody b); //(4)inout 能傳入,服務端能修改客戶端的b引數 MsgBody getMsg4(inout MsgBody b); }

MsgBody.aidl

package aidl;
parcelable MsgBody;
package aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class MsgBody implements Parcelable {
    public String msgContext;
    public int msgId;

    public MsgBody() {}

    protected MsgBody(Parcel in) {
        readFromParcel(in);
    }

    public static final Creator<MsgBody> CREATOR = new
Creator<MsgBody>() { @Override public MsgBody createFromParcel(Parcel in) { return new MsgBody(in); } @Override public MsgBody[] newArray(int size) { return new MsgBody[size]; } }; @Override public int describeContents() { return 0; } /** * @param dest * @param flags flags=PARCELABLE_WRITE_RETURN_VALUE:表示該物件從服務端返回客戶端 * flags=PARCELABLE_ELIDE_DUPLICATES 程式內部複製變數的時候 */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(msgContext); dest.writeInt(msgId); } public void readFromParcel(Parcel in) { msgContext = in.readString(); msgId = in.readInt(); } @Override public String toString() { return "id = " + msgId + " msgContext = " + msgContext; } }

上面定義了MsgBody.aidl,IPostMsg.aidl,

值得注意的是:
1. MsgBody.aidl,IPostMsg.aidl即使在一個包下,IPostMsg.aidl中引用MsgBody時,也要寫import aidl.MsgBody;
2. public MsgBody() {}必須有一個空的構造方法,否則使用out tag的時候會報錯。

Error:(72, 9) 錯誤: 無法將類 MsgBody中的構造器 MsgBody應用到給定型別;
需要: Parcel
找到: 沒有引數
原因: 實際引數列表和形式引數列表長度不同
  1. ,使用out tag序列化的時候一定要有下面的函式
   public void readFromParcel(Parcel in) {
        msgContext = in.readString();
        msgId = in.readInt();
    }

否則會報

Error:(215, 2) 錯誤: 找不到符號
符號:   方法 readFromParcel(Parcel)
位置: 型別為MsgBody的變數 b

現在客服務都開啟服務,客戶端獲取

測試MsgBody getMsg1();

客戶端先setMsg(String msg)
服務端用getMsg1()返回MsgBody

客戶端

    log("setMsg = " + outStr);
    postMsg.setMsg(outStr);
    MsgBody b = postMsg.getMsg1();
    log("getMsg1 " + b.toString());

服務端

        private String msg;

        @Override
        public void setMsg(String msg) throws RemoteException {
            log("server get " + msg);
            this.msg = msg;
        }

        @Override
        public MsgBody getMsg1() throws RemoteException {
            MsgBody msgBody = new MsgBody();
            int id = 10;
            msgBody.msgId = id;
            msgBody.msgContext = msg;
            log("out getMsg " + msgBody.toString());
            return msgBody;
        }

輸出log

01-01 19:16:58.602 30532-30532/com.example.root.aidlclient I/wangcan: client setMsg = dffff
01-01 19:16:58.604 30263-30281/com.example.root.aidlservice:remote I/wangcan: service server get dffff
01-01 19:16:58.612 30263-30281/com.example.root.aidlservice:remote I/wangcan: service out getMsg id = 10 msgContext = dffff
01-01 19:16:58.616 30532-30532/com.example.root.aidlclient I/wangcan: client getMsg1 id = 10 msgContext = dffff

結論:直接retrun 返回不需要什麼tag,跟一般java函式呼叫沒有區別

MsgBody getMsg2(out MsgBody b);

客戶端

//測試getMsg2
log("測試getMsg2");
 MsgBody test = new MsgBody();
        test.msgId = 999;
        test.msgContext = "test";

log("傳入" + test.toString());
MsgBody r1 = postMsg.getMsg2(test);
log(test.toString());

服務端

       public MsgBody getMsg2(MsgBody b) throws RemoteException {
            log("getMsg2 接收" + b.toString());
            b.msgId=222;
            b.msgContext="change";
            log("getMsg2 修改"+ b.toString());
            return null;
        }

輸出log

01-01 19:16:58.617 30532-30532/com.example.root.aidlclient I/wangcan: client 傳入id = 999 msgContext = test
01-01 19:16:58.618 30263-30281/com.example.root.aidlservice:remote I/wangcan: service getMsg2 接收id = 0 msgContext = null
01-01 19:16:58.619 30263-30281/com.example.root.aidlservice:remote I/wangcan: service getMsg2 修改id = 222 msgContext = change
01-01 19:16:58.622 30532-30532/com.example.root.aidlclient I/wangcan: client id = 222 msgContext = change

結論:
如果使用out tag用於入參
1. service getMsg2 接收id = 0 msgContext = null 說明,服務端不能得到傳入的資料
2. 在服務端修改值(service getMsg2 修改id = 222 msgContext = change),這個值被修改了

服務端能修改客戶端的入引數據,但不能獲取這個引數資料

MsgBody getMsg3(in MsgBody b);

客戶端與服務端程式碼與getMsg2一樣,直接看log

01-01 19:16:58.623 30532-30532/com.example.root.aidlclient I/wangcan: client 傳入id = 999 msgContext = test
01-01 19:16:58.624 30263-30281/com.example.root.aidlservice:remote I/wangcan: service getMsg3 接收id = 999 msgContext = test
01-01 19:16:58.625 30263-30281/com.example.root.aidlservice:remote I/wangcan: service getMsg3 修改id = 333 msgContext = change
01-01 19:16:58.627 30532-30532/com.example.root.aidlclient I/wangcan: client id = 999 msgContext = test

結論:
如果使用in tag用於入參
1. service getMsg3 接收service getMsg3 接收id = 999 msgContext = test 說明,服務端能得到傳入的資料
2. 在服務端修改值(service getMsg2 修改id = 333 msgContext = change),但是服務端的值沒有改變,仍然是(client id = 999 msgContext = test)

服務端不能修改客戶端的入引數據,但能獲取這個引數資料

MsgBody getMsg4(inout MsgBody b);

客戶端與服務端程式碼與上面兩個一樣,直接看log

01-01 19:16:58.628 30532-30532/com.example.root.aidlclient I/wangcan: client 傳入id = 999 msgContext = test
01-01 19:16:58.629 30263-30281/com.example.root.aidlservice:remote I/wangcan: service getMsg4 接收id = 999 msgContext = test
01-01 19:16:58.630 30263-30281/com.example.root.aidlservice:remote I/wangcan: service getMsg4 修改id = 444 msgContext = change
01-01 19:16:58.632 30532-30532/com.example.root.aidlclient I/wangcan: client id = 444 msgContext = change

如果使用inout tag用於入參
1. “接收id = 999 msgContext = test” 說明,服務端能得到傳入的資料
2. 在服務端修改值(service getMsg2 修改id = 333 msgContext = change),與後面客戶端對應的引數的值(client id = 444 msgContext = change)
3. 最後retrun 返回跟getMsg1一樣

服務端能修改客戶端的入引數據,也能獲取這個引數資料

最後的結論

  1. 即使在一個包裡面也要匯入包名
  2. MsgBody getMsg();可以直接返回,不需要什麼tag
  3. MsgBody getMsg2(out MsgBody b); 服務端能修改客戶端的入引數據,但不能獲取這個引數資料
  4. MsgBody getMsg3(in MsgBody b); 服務端不能修改客戶端的入引數據,但能獲取這個引數資料
  5. MsgBody getMsg4(inout MsgBody b);服務端能修改客戶端的入引數據,也能獲取這個引數資料
  6. tag (in out inout)是相對與服務端說的,in就是資料能進入客戶端,out是資料能出服務端,inout是能進能出
  7. 使用out tag序列化的時候一定要有下面的函式和無參建構函式。aidl最後會編譯出對應的java檔案。如果使用的out tag,原始碼中使用這兩個方法。
   public void readFromParcel(Parcel in) {
        msgContext = in.readString();
        msgId = in.readInt();
    }

我的測試程式碼:
https://gitee.com/namedcan/WangCanAIDL或直接git clone [email protected]:namedcan/WangCanAIDL.git