AIDL中的in、out、inout的區別

1、概念說明

定向tag是AIDL中語法的一部分,其中in、out、inout是三個定向tag。 在官網上關於Android定向tag的定義是這樣的:java

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 .android

意思就是全部非基本類型的參數都須要一個定向tag來代表數據是如何走向的,要不是in,out或者inout。基本數據類型默認是in,並且不能是其餘tag。bash

根據上述的聲明,咱們大概猜一下,得出這樣的結論: 定向 tag 表示了在跨進程通訊中數據的流向,其中 in 表示數據只能由客戶端流向服務端, out 表示數據只能由服務端流向客戶端,而 inout 則表示數據能夠在服務端與客戶端之間雙向流通。其中的數據流向是針對在客戶端中的那個傳入方法的對象而言的。app

對於in,服務端將會收到客戶端對象的完整數據,可是客戶端對象不會由於服務端對傳參的修改而發生變更。相似的行爲在Java中的表現是,在Java方法中,對傳進來的參數進行了深複製,傳進來的參數不會受到深複製後的對象的影響。這和in的行爲有點相似。ide

對於out,服務端將會收到客戶端對象,該對象不爲空,可是它裏面的字段爲空,可是在服務端對該對象做任何修改以後客戶端的傳參對象都會同步改動。相似的行爲在Java中的表現是,在Java方法中,對傳進來的參數進行忽略,並new一個新對象,全部的操做都是圍繞着這個新對象進行的,最後將該新對象賦值給傳參對象。測試

對於inout ,服務端將會接收到客戶端傳來對象的完整信息,而且客戶端將會同步服務端對該對象的任何變更。相似的行爲在Java中的表現是,在Java方法中,對傳進來的參數進行修改並返回。ui

2、實驗論證

下面寫一個demo來驗證上面的結論。 aidl文件:(Book.aidl)this

// 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)spa

// 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).net

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項目,生成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;
        }
    }
}
複製代碼

客戶端的代碼比較簡單,主要是三個按鈕,點擊後觸發三個定向tag的不一樣操做,執行完畢後打印傳遞參數Book對象的信息。 接着咱們看一下服務端的代碼:

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;
    }
}
複製代碼

服務端的代碼也挺簡單的,就是初始化一個IBookManager.Stub對象的IBinder接口並返回給客戶端。 如今客戶端分別點擊」測試定向tag in",「測試定向tag out",「測試定向tag inout」按鈕,而後獲得以下的Log信息:

點擊定向tag in。服務端:
origin Book Info=[bookName=Android, bookPrice=30]
add Book with in tag.list=[[bookName=FirstBook, bookPrice=30], [bookName=Android, bookPrice=100]]
點擊定向tag in。客戶端:
Test in tag. book Info: [bookName=Android, bookPrice=30]

點擊定向tag out。服務端: 
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]]
點擊定向tag out。客戶端: 
Test out tag. book Info: [bookName=null, bookPrice=100]

點擊定向tag inout。服務端: 
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]]
點擊定向tag inout。客戶端: 
Test inout tag. book Info: [bookName=Android, bookPrice=100]
複製代碼

從上面信息能夠獲得以下結論: 當咱們點擊「測試定向tag in」按鈕時,服務端接收到了客戶端傳過來的Book對象的完整信息,即[bookName=Android, bookPrice=30] ,執行完畢後客戶端的Book對象的信息沒有改變,依然是[bookName=Android, bookPrice=30]。 當咱們點擊「測試定向tag out」按鈕時,服務端接收到了客戶端傳過來的Book對象,可是對象的字段都是未初始化的,即[bookName=null, bookPrice=0],執行完畢後客戶端的Book對象的信息同步改變了,即和服務端的對象信息進行了同步:[bookName=null, bookPrice=100]。 當咱們點擊「測試定向tag inout」按鈕時,服務端接收到了客戶端傳過來的Book對象的完整信息,同時服務端的對象的改變也會同步到客戶端的對象中。 這個結論的得出和咱們上述的推論是一致的,下面咱們從源碼中看一下這三個方法的邏輯流程,進一步論證咱們的實驗結果。

3、理論支持

首先咱們看一下客戶端的源碼是怎麼處理addBookWithInTagaddBookWithOutTagaddBookWithInOutTag這三個方法的(注意三個方法中註釋的對比):

@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是客戶端到服務端的輸入流數據
		if ((book!=null)) {
			_data.writeInt(1);
			book.writeToParcel(_data, 0);
		}
		else {
			_data.writeInt(0);
		}
        //調用遠程服務端的方法,將數據流傳送過去
	    mRemote.transact(Stub.TRANSACTION_addBookWithInTag, _data, _reply, 0);
        //注意這裏並無對服務端返回的數據流進行讀取
		_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);
        //並不關心客戶端傳遞參數的對象,由於都沒有將對象寫入到_data數據流
        mRemote.transact(Stub.TRANSACTION_addBookWithOutTag, _data, _reply, 0); 
		_reply.readException();
         //從服務端的數據流中讀取數據並解析成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是客戶端到服務端的輸入流數據
		_data.writeInterfaceToken(DESCRIPTOR);
		if ((book!=null)) {
			_data.writeInt(1);
			book.writeToParcel(_data, 0);
		}
		else {
			_data.writeInt(0);
		}
        //調用遠程服務端的方法,將數據流傳送過去	
        mRemote.transact(Stub.TRANSACTION_addBookWithInOutTag, _data, _reply, 0);
		_reply.readException();
        //從服務端的數據流中讀取數據並解析成Book對象返回到傳參對象
		if ((0!=_reply.readInt())) {
			book.readFromParcel(_reply);
		}
	}
	finally {
		_reply.recycle();
		_data.recycle();
	}
}
複製代碼

結論: 定向 tag in 表示數據只能由客戶端流向服務端,服務端將會收到客戶端對象的完整數據,可是客戶端對象不會由於服務端對傳參的修改而發生變更。 out 表示數據只能由服務端流向客戶端,服務端將會收到客戶端對象,該對象不爲空,可是它裏面的字段爲空,可是在服務端對該對象做任何修改以後客戶端的傳參對象都會同步改動。 inout 則表示數據能夠在服務端與客戶端之間雙向流通。其中的數據流向是針對在客戶端中的那個傳入方法的對象而言的。服務端將會接收到客戶端傳來對象的完整信息,而且客戶端將會同步服務端對該對象的任何變更。

4、參考文章:

這篇文章的實際上是對AIDL的in、out、inout這三個定向tag的一個總結,以前一直不瞭解這三個定向tag的意思,知道看到你真的理解AIDL中的in,out,inout麼?這篇文章後,順着做者的思路下來,而且本身作一遍實驗和看一遍源碼,纔對這三個定向tag有一個清晰的瞭解。

相關文章
相關標籤/搜索