一刮吧、概念說明
定向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
炉菲,addBookWithOutTag
,addBookWithInOutTag
這三個(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è)清晰的了解馏颂。