個人博客地址 http://dandanlove.com/
為什么會想寫這篇文章利花,只因為一個error idl.exe E 4928 5836 type_namespace.cpp:130] 'Book' can be an out type, so you must declare it as in, out or inout.
看過上一篇文章Android:IPC之AIDL的學(xué)習(xí)和總結(jié)的同學(xué)都知道這是因為在AIDL文件中使用非常規(guī)類型作為參數(shù)傳遞的時候沒有標記指向tag,那么到底為什么會是這樣子的呢,作為一個好奇寶寶我想好好看看艰亮。
介紹
官網(wǎng)介紹AIDL的時候上有這么一段話
- All non-primitive parameters require a directional tag indicating which way the data goes. Either in, out, or inout (see the example below).
- Primitives are in by default, and cannot be otherwise.
- Caution: You should limit the direction to what is truly needed, because marshalling parameters is expensive.
大概意思是非默認類型的參數(shù)都需要添加指向標簽in,out或inout挣郭。根據(jù)自己的需求去添加垃杖,因為實現(xiàn)是有代價的。
已知結(jié)論
看過我寫的Android:IPC之AIDL的學(xué)習(xí)和總結(jié)的同學(xué)都知道:
- in表示輸入型參數(shù)(Server可以獲取到Client傳遞過去的數(shù)據(jù)丈屹,但是不能對Client端的數(shù)據(jù)進行修改)
- out表示輸出型參數(shù)(Server獲取不到Client傳遞過去的數(shù)據(jù)调俘,但是能對Client端的數(shù)據(jù)進行修改)
- inout表示輸入輸出型參數(shù)(Server可以獲取到Client傳遞過去的數(shù)據(jù)伶棒,但是能對Client端的數(shù)據(jù)進行修改)。
提出問題
下邊我們就研究一個in彩库,out或inout為什么能代表不同的傳輸方式肤无,為什么實現(xiàn)的代價不一樣。
過程驗證
創(chuàng)建Book.aidl文件
package com.tzx.aidldemo.aidl;
parcelable Book;
創(chuàng)建Book.java文件
package com.tzx.aidldemo.aidl;
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book() {
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
//從序列化后的對象中創(chuàng)建原始對象
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
//從序列化后的對象中創(chuàng)建原始對象
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
//指定長度的原始對象數(shù)組
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
//返回當前對象的內(nèi)容描述骇钦。如果含有文件描述符宛渐,返回1,否則返回0眯搭,幾乎所有情況都返回0
@Override
public int describeContents() {
return 0;
}
//將當前對象寫入序列化結(jié)構(gòu)中窥翩,其flags標識有兩種(1|0)。
//為1時標識當前對象需要作為返回值返回鳞仙,不能立即釋放資源寇蚊,幾乎所有情況下都為0.
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
@Override
public String toString() {
return "[bookId=" + bookId + ",bookName='" + bookName + "']";
}
}
創(chuàng)建aidl接口文件IBookManager.aidl文件
package com.tzx.aidlinout.aidl;
import com.tzx.aidlinout.aidl.Book;
interface IBookManager {
Book addInBook(in Book book);
Book addOutBook(out Book book);
Book addInoutBook(inout Book book);
}
創(chuàng)建遠程服務(wù)
//將bookId都改為-1,在bookName后面都添加參數(shù)的tag標記
public class BookManagerService extends Service {
private CopyOnWriteArrayList list = new CopyOnWriteArrayList();
private IBinder mBinder = new IBookManager.Stub(){
@Override
public Book addInBook(Book book) throws RemoteException {
book.bookId = -1;
book.bookName = book.bookName + "-in";
list.add(book);
return book;
}
@Override
public Book addOutBook(Book book) throws RemoteException {
book.bookId = -1;
book.bookName = book.bookName + "-out";
list.add(book);
return book;
}
@Override
public Book addInoutBook(Book book) throws RemoteException {
book.bookId = -1;
book.bookName = book.bookName + "-inout";
list.add(book);
return book;
}
@Override
public List<Book> getBookList() throws RemoteException {
return list;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
在創(chuàng)建上面的文件的過程中棍好,遇到不太清楚的或者編譯出現(xiàn)Error的仗岸,可以參考上一篇文章Android:IPC之AIDL的學(xué)習(xí)和總結(jié)。
具體方法調(diào)用的Activity就不寫全部代碼了借笙,我們看看三種方法的調(diào)用
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.book_in:
try {
int bookId = Integer.parseInt(bookIdET.getText().toString());
String bookName = bookNameET.getText().toString();
if (bookId <= 0 || TextUtils.isEmpty(bookName)) return;
StringBuilder builder = new StringBuilder();
//LogUtils.d("-----------book_in-----------------");
Book book0 = new Book(bookId, bookName);
String source = "source:" + book0.toString();
//LogUtils.d(source);
builder.append(source);
builder.append('\n');
String result = "result:" + bookManager.addInBook(book0).toString();
//LogUtils.d(result);
builder.append(result);
builder.append('\n');
source = "source" + book0.toString();
//LogUtils.d(source);
builder.append(source);
//LogUtils.d("**************book_in****************");
bookinfoTV.setText(builder.toString());
} catch (Exception e) {
e.printStackTrace();
}
break;
case R.id.book_out:
try {
int bookId = Integer.parseInt(bookIdET.getText().toString());
String bookName = bookNameET.getText().toString();
if (bookId <= 0 || TextUtils.isEmpty(bookName)) return;
StringBuilder builder = new StringBuilder();
//LogUtils.d("-----------book_out-----------------");
Book book0 = new Book(bookId, bookName);
String source = "source:" + book0.toString();
//LogUtils.d(source);
builder.append(source);
builder.append('\n');
String result = "result:" + bookManager.addOutBook(book0).toString();
//LogUtils.d(result);
builder.append(result);
builder.append('\n');
source = "source" + book0.toString();
//LogUtils.d(source);
builder.append(source);
//LogUtils.d("**************book_out****************");
bookinfoTV.setText(builder.toString());
} catch (Exception e) {
e.printStackTrace();
}
break;
case R.id.book_inout:
try {
int bookId = Integer.parseInt(bookIdET.getText().toString());
String bookName = bookNameET.getText().toString();
if (bookId <= 0 || TextUtils.isEmpty(bookName)) return;
StringBuilder builder = new StringBuilder();
//LogUtils.d("-----------book_inout-----------------");
Book book0 = new Book(bookId, bookName);
String source = "source:" + book0.toString();
//LogUtils.d(source);
builder.append(source);
builder.append('\n');
String result = "result:" + bookManager.addInoutBook(book0).toString();
//LogUtils.d(result);
builder.append(result);
builder.append('\n');
source = "source" + book0.toString();
//LogUtils.d(source);
builder.append(source);
//LogUtils.d("**************book_inout****************");
bookinfoTV.setText(builder.toString());
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
創(chuàng)建好上面三個文件后扒怖,我們編譯整個項目工程(PS:生成aidl接口實現(xiàn)類)。
運行結(jié)果
[圖片上傳失敗...(image-ed056-1524796700960)]
下邊是與結(jié)果相對應(yīng)的Log輸出
14962-14962/com.tzx.aidlinout D/xxx: -----------book_in-----------------
14962-14962/com.tzx.aidlinout D/xxx: source:[bookId=1212,bookName=C++]
14962-14962/com.tzx.aidlinout D/xxx: result:[bookId=-1,bookName=C++-in]
14962-14962/com.tzx.aidlinout D/xxx: source[bookId=1212,bookName=C++]
14962-14962/com.tzx.aidlinout D/xxx: **************book_in****************
14962-14962/com.tzx.aidlinout D/xxx: -----------book_out-----------------
14962-14962/com.tzx.aidlinout D/xxx: source:[bookId=1212,bookName=C++]
14962-14962/com.tzx.aidlinout D/xxx: result:[bookId=-1,bookName=null-out]
14962-14962/com.tzx.aidlinout D/xxx: source[bookId=-1,bookName=null-out]
14962-14962/com.tzx.aidlinout D/xxx: **************book_out****************
14962-14962/com.tzx.aidlinout D/xxx: -----------book_inout-----------------
14962-14962/com.tzx.aidlinout D/xxx: source:[bookId=1212,bookName=C++]
14962-14962/com.tzx.aidlinout D/xxx: result:[bookId=-1,bookName=C++-inout]
14962-14962/com.tzx.aidlinout D/xxx: source[bookId=-1,bookName=C++-inout]
14962-14962/com.tzx.aidlinout D/xxx: **************book_inout****************
實際結(jié)果與我們已知結(jié)論一致业稼!盗痒!
但問題我們還沒有解決,我們繼續(xù)看代碼低散,其實所有的實現(xiàn)都是在改接口實現(xiàn)類中IBookManager.java
源碼解析
package com.tzx.aidlinout.aidl;
public interface IBookManager extends android.os.IInterface {
public com.tzx.aidlinout.aidl.Book addInBook(
com.tzx.aidlinout.aidl.Book book) throws android.os.RemoteException;
public com.tzx.aidlinout.aidl.Book addOutBook(
com.tzx.aidlinout.aidl.Book book) throws android.os.RemoteException;
public com.tzx.aidlinout.aidl.Book addInoutBook(
com.tzx.aidlinout.aidl.Book book) throws android.os.RemoteException;
public java.util.List<com.tzx.aidlinout.aidl.Book> getBookList()
throws android.os.RemoteException;
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.tzx.aidlinout.aidl.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.tzx.aidlinout.aidl.IBookManager";
static final int TRANSACTION_addInBook = (android.os.IBinder.FIRST_CALL_TRANSACTION +
0);
static final int TRANSACTION_addOutBook = (android.os.IBinder.FIRST_CALL_TRANSACTION +
1);
static final int TRANSACTION_addInoutBook = (android.os.IBinder.FIRST_CALL_TRANSACTION +
2);
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION +
3);
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.tzx.aidlinout.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.tzx.aidlinout.aidl.IBookManager asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) &&
(iin instanceof com.tzx.aidlinout.aidl.IBookManager))) {
return ((com.tzx.aidlinout.aidl.IBookManager) iin);
}
return new com.tzx.aidlinout.aidl.IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
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_addInBook: {
data.enforceInterface(DESCRIPTOR);
//聲明輸入的參數(shù)_arg0的引用
com.tzx.aidlinout.aidl.Book _arg0;
//并根據(jù)輸入的數(shù)據(jù)為其創(chuàng)建對象
if ((0 != data.readInt())) {
_arg0 = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
//獲取調(diào)用this.addInBook方法返回的_result
com.tzx.aidlinout.aidl.Book _result = this.addInBook(_arg0);
reply.writeNoException();
//并向reply中寫入返回值_result
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addOutBook: {
data.enforceInterface(DESCRIPTOR);
//聲明輸入的參數(shù)_arg0的引用
com.tzx.aidlinout.aidl.Book _arg0;
//并為其創(chuàng)建新的對象
_arg0 = new com.tzx.aidlinout.aidl.Book();
//獲取調(diào)用this.addOutBook方法返回的_result
com.tzx.aidlinout.aidl.Book _result = this.addOutBook(_arg0);
reply.writeNoException();
//并向reply中寫入返回值_result
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
//再將參數(shù)_arg0寫入reply中俯邓,至于為什么寫入,我們看看客戶端Proxy中的讀取
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addInoutBook: {
data.enforceInterface(DESCRIPTOR);
//聲明輸入的參數(shù)_arg0的引用
com.tzx.aidlinout.aidl.Book _arg0;
//并根據(jù)輸入的數(shù)據(jù)為其創(chuàng)建對象
if ((0 != data.readInt())) {
_arg0 = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
//獲取調(diào)用this.addInoutBook方法返回的_result
com.tzx.aidlinout.aidl.Book _result = this.addInoutBook(_arg0);
reply.writeNoException();
//并向reply中寫入返回值_result
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
//再將參數(shù)_arg0寫入reply中谦纱,至于為什么寫入看成,我們看看客戶端Proxy中的讀取
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.tzx.aidlinout.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.tzx.aidlinout.aidl.IBookManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public com.tzx.aidlinout.aidl.Book addInBook(
com.tzx.aidlinout.aidl.Book book)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.tzx.aidlinout.aidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//將客戶端調(diào)用時傳入的參數(shù)寫入_data中
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//將_data、_reply序列化對象和Stub.TRANSACTION_addInBook指令傳遞到Server端
mRemote.transact(Stub.TRANSACTION_addInBook, _data, _reply,
0);
_reply.readException();
//讀取Server端返回的序列化_reply中的對象
if ((0 != _reply.readInt())) {
_result = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
//然后直接將_result返回
//我們發(fā)現(xiàn)整個方法調(diào)用期間傳入的對象book只是將數(shù)據(jù)寫入到Server跨嘉,它的值進行并沒有任何修改川慌。
//總結(jié):in類型的參數(shù),它向服務(wù)端傳入數(shù)據(jù)祠乃,但是卻不接受Server返回的值梦重。
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public com.tzx.aidlinout.aidl.Book addOutBook(
com.tzx.aidlinout.aidl.Book book)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.tzx.aidlinout.aidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//將_data、_reply序列化對象和Stub.TRANSACTION_addInBook指令傳遞到Server端
//_data和_reply序列化對象并沒有進行寫入
mRemote.transact(Stub.TRANSACTION_addOutBook, _data,
_reply, 0);
_reply.readException();
//讀取Server端返回的序列化_reply中的對象亮瓷,寫入到_result
if ((0 != _reply.readInt())) {
_result = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
//讀取Server端返回的序列化_reply中的對象琴拧,寫入到傳入的book對象中
if ((0 != _reply.readInt())) {
book.readFromParcel(_reply);
}
//然后直接將_result返回
//我們發(fā)現(xiàn)整個方法調(diào)用期間傳入的對象book并沒有將數(shù)據(jù)寫入到Server,它的值確實是Server返回的嘱支。
//總結(jié):out類型的參數(shù)蚓胸,它并不向服務(wù)端傳入數(shù)據(jù)挣饥,但是卻接受Server返回的值。
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public com.tzx.aidlinout.aidl.Book addInoutBook(
com.tzx.aidlinout.aidl.Book book)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.tzx.aidlinout.aidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//將客戶端調(diào)用時傳入的參數(shù)寫入_data中
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//將_data沛膳、_reply序列化對象和Stub.TRANSACTION_addInoutBook指令傳遞到Server端
mRemote.transact(Stub.TRANSACTION_addInoutBook, _data,
_reply, 0);
_reply.readException();
//讀取Server端返回的序列化_reply中的對象扔枫,寫入到_result
if ((0 != _reply.readInt())) {
_result = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
//讀取Server端返回的序列化_reply中的對象,寫入到傳入的book對象中
if ((0 != _reply.readInt())) {
book.readFromParcel(_reply);
}
//然后直接將_result返回
//我們發(fā)現(xiàn)整個方法調(diào)用期間傳入的對象book將其數(shù)據(jù)寫入到Server锹安,并且它的值被Server返回的數(shù)據(jù)修改短荐。
//總結(jié):inout類型的參數(shù),它既向服務(wù)端傳入數(shù)據(jù)叹哭,也卻接受Server返回的值忍宋。
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public java.util.List<com.tzx.aidlinout.aidl.Book> getBookList()
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.tzx.aidlinout.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data,
_reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.tzx.aidlinout.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
}
看了這么多代碼是不是感覺腦袋大了,沒事接下來一張圖幫你理的清清楚楚的:
經(jīng)過兩篇文章對aidl的講解风罩,我想你已經(jīng)把它理解的透透的了糠排,如果還有什么問題可以給我留言哦~!
想閱讀作者的更多文章泊交,可以查看我 個人博客 和公共號: