AIDL中的in痛黎、out、inout的區(qū)別

一刮吧、概念說明

定向tag是AIDL中語法的一部分湖饱,其中in、out杀捻、inout是三個(gè)定向tag井厌。
在官網(wǎng)上關(guān)于Android定向tag的定義是這樣的:

All non-primitive parameters require a directional tag indicating which way the data goes . Either in , out , or inout . Primitives are in by default , and connot be otherwise .

意思就是所有非基本類型的參數(shù)都需要一個(gè)定向tag來表明數(shù)據(jù)是如何走向的,要不是in致讥,out或者inout仅仆。基本數(shù)據(jù)類型默認(rèn)是in垢袱,而且不能是其他tag墓拜。

根據(jù)上述的聲明,我們大概猜一下请契,得出這樣的結(jié)論:
定向 tag 表示了在跨進(jìn)程通信中數(shù)據(jù)的流向咳榜,其中 in 表示數(shù)據(jù)只能由客戶端流向服務(wù)端, out 表示數(shù)據(jù)只能由服務(wù)端流向客戶端爽锥,而 inout 則表示數(shù)據(jù)可以在服務(wù)端與客戶端之間雙向流通涌韩。其中的數(shù)據(jù)流向是針對在客戶端中的那個(gè)傳入方法的對象而言的。

對于in氯夷,服務(wù)端將會收到客戶端對象的完整數(shù)據(jù)臣樱,但是客戶端對象不會因?yàn)榉?wù)端對傳參的修改而發(fā)生變動。類似的行為在Java中的表現(xiàn)是腮考,在Java方法中雇毫,對傳進(jìn)來的參數(shù)進(jìn)行了深復(fù)制,傳進(jìn)來的參數(shù)不會受到深復(fù)制后的對象的影響踩蔚。這和in的行為有點(diǎn)類似棚放。

對于out,服務(wù)端將會收到客戶端對象寂纪,該對象不為空席吴,但是它里面的字段為空,但是在服務(wù)端對該對象作任何修改之后客戶端的傳參對象都會同步改動捞蛋。類似的行為在Java中的表現(xiàn)是孝冒,在Java方法中,對傳進(jìn)來的參數(shù)進(jìn)行忽略拟杉,并new一個(gè)新對象庄涡,所有的操作都是圍繞著這個(gè)新對象進(jìn)行的,最后將該新對象賦值給傳參對象搬设。

對于inout 穴店,服務(wù)端將會接收到客戶端傳來對象的完整信息,并且客戶端將會同步服務(wù)端對該對象的任何變動拿穴。類似的行為在Java中的表現(xiàn)是泣洞,在Java方法中,對傳進(jìn)來的參數(shù)進(jìn)行修改并返回默色。

二球凰、實(shí)驗(yàn)論證

下面寫一個(gè)demo來驗(yàn)證上面的結(jié)論。
aidl文件:(Book.aidl)

// IBook.aidl
package com.example.runningh.mydemo.binder;
import com.example.runningh.mydemo.binder.Book;

// Declare any non-default types here with import statements

parcelable Book;

aidl文件:(IBookManager.aidl)

// IBookManager.aidl
package com.example.runningh.mydemo.binder;
import com.example.runningh.mydemo.binder.Book;

// Declare any non-default types here with import statements

interface IBookManager {
    List<Book> getBooks();

    void addBookWithInTag(in Book book); //測試定向tag in

    void addBookWithOutTag(out Book book); //測試定向tag out

    void addBookWithInOutTag(inout Book book); //測試定向tag inout
}

Java文件:(Book.java)

package com.example.runningh.mydemo.binder;

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

/**
 * Created by hwldzh on 2018/5/20
 * 類描述:
 */
public class Book implements Parcelable {
    public String bookName;
    public int price;

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(bookName);
        dest.writeInt(price);
    }

    public static Creator<Book> CREATOR =  new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

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

    private Book(Parcel in) {
        readFromParcel(in);
    }

    public Book(String name, int price) {
        this.bookName = name;
        this.price = price;
    }

    public Book() {

    }

    public void readFromParcel(Parcel in) {
        bookName = in.readString();
        price = in.readInt();
    }

    public String toString() {
        return "[bookName=" + bookName + ", bookPrice=" + price + "]";
    }
}

編譯demo項(xiàng)目腿宰,生成IBookManager.java文件呕诉。下面看一下客戶端的代碼:

package com.example.runningh.mydemo.binder;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;

import com.example.runningh.mydemo.R;

/**
 * Created by hwldzh on 2018/5/20
 * 類描述:
 */
public class TestTagActivity extends Activity implements View.OnClickListener {
    public static final String TAG = "ABC";
    private IBookManager bookManager;

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

        findViewById(R.id.test_tag_in).setOnClickListener(this);
        findViewById(R.id.test_tag_out).setOnClickListener(this);
        findViewById(R.id.test_tag_inout).setOnClickListener(this);

        Intent intent = new Intent(this, TestTagService.class);
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                bookManager = IBookManager.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                bookManager = null;
            }
        }, BIND_AUTO_CREATE);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.test_tag_in:
                if (bookManager != null) {
                    try {
                        Book book = new Book("Android", 30);
                        bookManager.addBookWithInTag(book);
                        Log.i(TAG, "Test in tag. book Info: " + book.toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case R.id.test_tag_out:
                if (bookManager != null) {
                    try {
                        Book book = new Book("Android", 30);
                        bookManager.addBookWithOutTag(book);
                        Log.i(TAG, "Test out tag. book Info: " + book.toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case R.id.test_tag_inout:
                if (bookManager != null) {
                    try {
                        Book book = new Book("Android", 30);
                        bookManager.addBookWithInOutTag(book);
                        Log.i(TAG, "Test inout tag. book Info: " + book.toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
            default:
                break;
        }
    }
}

客戶端的代碼比較簡單,主要是三個(gè)按鈕吃度,點(diǎn)擊后觸發(fā)三個(gè)定向tag的不同操作甩挫,執(zhí)行完畢后打印傳遞參數(shù)Book對象的信息。
接著我們看一下服務(wù)端的代碼:

package com.example.runningh.mydemo.binder;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by hwldzh on 2018/5/20
 * 類描述:
 */
public class TestTagService extends Service {
    public static final String TAG = "ABC";
    private IBookManager.Stub bookManager;
    private List<Book> list = new ArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        list.add(new Book("FirstBook", 30));

        bookManager = new IBookManager.Stub() {
            @Override
            public List<Book> getBooks() throws RemoteException {
                return list;
            }

            @Override
            public void addBookWithInTag(Book book) throws RemoteException {
                if (book == null) {
                    Log.i(TAG, "book is null");
                    book = new Book();
                }
                book.price = 100;
                list.add(book);
                Log.i(TAG, "add Book with in tag.list=" + list.toString());
            }

            @Override
            public void addBookWithOutTag(Book book) throws RemoteException {
                if (book == null) {
                    Log.i(TAG, "book is null");
                    book = new Book();
                }
                book.price = 100;
                list.add(book);
                Log.i(TAG, "add Book with out tag.list=" + list.toString());
            }

            @Override
            public void addBookWithInOutTag(Book book) throws RemoteException {
                if (book == null) {
                    Log.i(TAG, "book is null");
                    book = new Book();
                }
                book.price = 100;
                list.add(book);
                Log.i(TAG, "add Book with inout tag.list =" + list.toString());
            }
        };
    }

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

服務(wù)端的代碼也挺簡單的椿每,就是初始化一個(gè)IBookManager.Stub對象的IBinder接口并返回給客戶端伊者。
現(xiàn)在客戶端分別點(diǎn)擊”測試定向tag in",“測試定向tag out"间护,“測試定向tag inout”按鈕删壮,然后得到如下的Log信息:

點(diǎn)擊定向tag in。服務(wù)端:
origin Book Info=[bookName=Android, bookPrice=30]
add Book with in tag.list=[[bookName=FirstBook, bookPrice=30], [bookName=Android, bookPrice=100]]
點(diǎn)擊定向tag in兑牡⊙氲客戶端:
Test in tag. book Info: [bookName=Android, bookPrice=30]

點(diǎn)擊定向tag out。服務(wù)端: 
origin Book Info=[bookName=null, bookPrice=0]
add Book with out tag.list=[[bookName=FirstBook, bookPrice=30], [bookName=Android, bookPrice=100], [bookName=null, bookPrice=100]]
點(diǎn)擊定向tag out均函∫谒洌客戶端: 
Test out tag. book Info: [bookName=null, bookPrice=100]

點(diǎn)擊定向tag inout。服務(wù)端: 
origin Book Info=[bookName=Android, bookPrice=30]
add Book with inout tag.list =[[bookName=FirstBook, bookPrice=30], [bookName=Android, bookPrice=100], [bookName=null, bookPrice=100], [bookName=Android, bookPrice=100]]
點(diǎn)擊定向tag inout≡眩客戶端: 
Test inout tag. book Info: [bookName=Android, bookPrice=100]

從上面信息可以得到如下結(jié)論:
當(dāng)我們點(diǎn)擊“測試定向tag in”按鈕時(shí)檩互,服務(wù)端接收到了客戶端傳過來的Book對象的完整信息,即[bookName=Android, bookPrice=30] 收毫,執(zhí)行完畢后客戶端的Book對象的信息沒有改變攻走,依然是[bookName=Android, bookPrice=30]。
當(dāng)我們點(diǎn)擊“測試定向tag out”按鈕時(shí)此再,服務(wù)端接收到了客戶端傳過來的Book對象昔搂,但是對象的字段都是未初始化的,即[bookName=null, bookPrice=0]输拇,執(zhí)行完畢后客戶端的Book對象的信息同步改變了摘符,即和服務(wù)端的對象信息進(jìn)行了同步:[bookName=null, bookPrice=100]。
當(dāng)我們點(diǎn)擊“測試定向tag inout”按鈕時(shí)策吠,服務(wù)端接收到了客戶端傳過來的Book對象的完整信息逛裤,同時(shí)服務(wù)端的對象的改變也會同步到客戶端的對象中。
這個(gè)結(jié)論的得出和我們上述的推論是一致的猴抹,下面我們從源碼中看一下這三個(gè)方法的邏輯流程带族,進(jìn)一步論證我們的實(shí)驗(yàn)結(jié)果。

三蟀给、理論支持

首先我們看一下客戶端的源碼是怎么處理addBookWithInTag炉菲,addBookWithOutTagaddBookWithInOutTag這三個(gè)方法的(注意三個(gè)方法中注釋的對比):

@Override 
public void addBookWithInTag(com.example.runningh.mydemo.binder.Book book) throws android.os.RemoteException
{
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
         //判斷book對象是否為空坤溃,不為空則將book對象寫入到_data中拍霜,_data是客戶端到服務(wù)端的輸入流數(shù)據(jù)
        if ((book!=null)) {
            _data.writeInt(1);
            book.writeToParcel(_data, 0);
        }
        else {
            _data.writeInt(0);
        }
        //調(diào)用遠(yuǎn)程服務(wù)端的方法,將數(shù)據(jù)流傳送過去
        mRemote.transact(Stub.TRANSACTION_addBookWithInTag, _data, _reply, 0);
        //注意這里并沒有對服務(wù)端返回的數(shù)據(jù)流進(jìn)行讀取
        _reply.readException();
    }
    finally {
        _reply.recycle();
        _data.recycle();
    }
}

@Override 
public void addBookWithOutTag(com.example.runningh.mydemo.binder.Book book) throws android.os.RemoteException
{
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        //并不關(guān)心客戶端傳遞參數(shù)的對象薪介,因?yàn)槎紱]有將對象寫入到_data數(shù)據(jù)流
        mRemote.transact(Stub.TRANSACTION_addBookWithOutTag, _data, _reply, 0); 
        _reply.readException();
         //從服務(wù)端的數(shù)據(jù)流中讀取數(shù)據(jù)并解析成Book對象返回到傳參對象
        if ((0!=_reply.readInt())) {
            book.readFromParcel(_reply);
        }
    }
    finally {
        _reply.recycle();
        _data.recycle();
    }
}

@Override 
public void addBookWithInOutTag(com.example.runningh.mydemo.binder.Book book) throws android.os.RemoteException
{
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
        //判斷book對象是否為空祠饺,不為空則將book對象寫入到_data中,_data是客戶端到服務(wù)端的輸入流數(shù)據(jù)
        _data.writeInterfaceToken(DESCRIPTOR);
        if ((book!=null)) {
            _data.writeInt(1);
            book.writeToParcel(_data, 0);
        }
        else {
            _data.writeInt(0);
        }
        //調(diào)用遠(yuǎn)程服務(wù)端的方法汁政,將數(shù)據(jù)流傳送過去   
        mRemote.transact(Stub.TRANSACTION_addBookWithInOutTag, _data, _reply, 0);
        _reply.readException();
        //從服務(wù)端的數(shù)據(jù)流中讀取數(shù)據(jù)并解析成Book對象返回到傳參對象
        if ((0!=_reply.readInt())) {
            book.readFromParcel(_reply);
        }
    }
    finally {
        _reply.recycle();
        _data.recycle();
    }
}

結(jié)論
定向 tag in 表示數(shù)據(jù)只能由客戶端流向服務(wù)端道偷,服務(wù)端將會收到客戶端對象的完整數(shù)據(jù),但是客戶端對象不會因?yàn)榉?wù)端對傳參的修改而發(fā)生變動记劈。
out 表示數(shù)據(jù)只能由服務(wù)端流向客戶端勺鸦,服務(wù)端將會收到客戶端對象,該對象不為空目木,但是它里面的字段為空换途,但是在服務(wù)端對該對象作任何修改之后客戶端的傳參對象都會同步改動。
inout 則表示數(shù)據(jù)可以在服務(wù)端與客戶端之間雙向流通刽射。其中的數(shù)據(jù)流向是針對在客戶端中的那個(gè)傳入方法的對象而言的军拟。服務(wù)端將會接收到客戶端傳來對象的完整信息,并且客戶端將會同步服務(wù)端對該對象的任何變動誓禁。

四懈息、參考文章:

這篇文章的其實(shí)是對AIDL的in、out摹恰、inout這三個(gè)定向tag的一個(gè)總結(jié)辫继,之前一直不了解這三個(gè)定向tag的意思怒见,知道看到你真的理解AIDL中的in,out姑宽,inout么遣耍?這篇文章后,順著作者的思路下來低千,并且自己做一遍實(shí)驗(yàn)和看一遍源碼,才對這三個(gè)定向tag有一個(gè)清晰的了解馏颂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末示血,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子救拉,更是在濱河造成了極大的恐慌难审,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亿絮,死亡現(xiàn)場離奇詭異告喊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)派昧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門黔姜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蒂萎,你說我怎么就攤上這事秆吵。” “怎么了五慈?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵纳寂,是天一觀的道長。 經(jīng)常有香客問我泻拦,道長毙芜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任争拐,我火速辦了婚禮腋粥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘架曹。我一直安慰自己灯抛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布音瓷。 她就那樣靜靜地躺著对嚼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绳慎。 梳的紋絲不亂的頭發(fā)上纵竖,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天漠烧,我揣著相機(jī)與錄音,去河邊找鬼靡砌。 笑死已脓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的通殃。 我是一名探鬼主播度液,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼画舌!你這毒婦竟也來了堕担?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤曲聂,失蹤者是張志新(化名)和其女友劉穎霹购,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朋腋,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡齐疙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旭咽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贞奋。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖穷绵,靈堂內(nèi)的尸體忽然破棺而出忆矛,到底是詐尸還是另有隱情,我是刑警寧澤请垛,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布催训,位于F島的核電站,受9級特大地震影響宗收,放射性物質(zhì)發(fā)生泄漏漫拭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一混稽、第九天 我趴在偏房一處隱蔽的房頂上張望采驻。 院中可真熱鬧,春花似錦匈勋、人聲如沸礼旅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽痘系。三九已至,卻和暖如春饿自,著一層夾襖步出監(jiān)牢的瞬間汰翠,已是汗流浹背龄坪。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留复唤,地道東北人健田。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像佛纫,于是被迫代替她去往敵國和親妓局。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容