AIDL 定向tag IPC 案例 MD

Markdown版本筆記 個人GitHub首頁 個人博客 個人微信 個人郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

AIDL 定向tag IPC 案例 MDjava


目錄

定向tag

總結

Android官網上在講到AIDL的地方關於定向tag是這樣介紹的:android

All non-primitive parameters require a directional tag indicating which way the data goes. //全部的非基本數據類型的參數都須要一個定向tag來指出數據的流向
Either in , out , or inout. //能夠是 in , out ,或者 inout
Primitives are in by default , and connot be otherwise. //基本數據類型參數的定向tag默認是,而且只能是 ingit

AIDL中的定向 tag 表示了在跨進程通訊中數據的流向,其中 in 表示數據只能由客戶端流向服務端, out 表示數據只能由服務端流向客戶端,而 inout 則表示數據可在服務端與客戶端之間雙向流通github

其中,數據流向是針對在客戶端中的那個傳入方法的對象而言的:微信

  • in 爲定向 tag 的話表現爲服務端將會接收到一個那個對象的完整數據,可是客戶端的那個對象不會由於服務端對傳參的修改而發生變更;
  • out 的話表現爲服務端將會接收到那個對象的參數爲空的對象,可是在服務端對接收到的空對象有任何修改以後客戶端將會同步變更
  • inout 爲定向 tag 的狀況下,服務端將會接收到客戶端傳來對象的完整信息,而且客戶端將會同步服務端對該對象的任何變更。

測試案例

客戶端
服務端ide

AIDL文件夾

public class Book implements Parcelable{}
package com.bqt.aidl;
parcelable Book;
package com.bqt.aidl;
import com.bqt.aidl.Book;

interface BookManager {
    Book addBookIn(in Book book);
    Book addBookOut(out Book book);
    Book addBookInout(inout Book book);
}
sourceSets {
    main {
        java.srcDirs = ['src/main/java', 'src/main/aidl']
    }
}

服務端

服務端的邏輯是,首先接受客戶端鏈接的請求,並把服務端處理好的BookManager.Stub的IBinder接口回傳給客戶端。在BookManager.Stub實現的方法裏面,主要是接收客戶端傳過來的Book對象,並試圖對其進行修改,而後把修改過的對象再傳回去。源碼分析

public class AIDLService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("bqt", "【Service-onBind】");
        return new MyBind();
    }

    private class MyBind extends BookManager.Stub {

        @Override
        public synchronized Book addBookIn(Book book) throws RemoteException {
            modifyBook(book, 100, "Service-In");
            return book;
        }

        @Override
        public synchronized Book addBookOut(Book book) throws RemoteException {
            modifyBook(book, 200, "Service-Out");
            return book;
        }

        @Override
        public synchronized Book addBookInout(Book book) throws RemoteException {
            modifyBook(book, 300, "Service-Inout");
            return book;
        }

        private void modifyBook(Book book, int i, String s) {
            if (book != null) {
                Log.i("bqt", "【Service-接收到的Book】" + book);
                book.setPrice(i);//修改book,觀察客戶端的反饋
                book.setName(s);
                Log.i("bqt", "【Service-返回的Book】" + book);
            }
        }
    }
}
<!-- 聲明權限 -->
<permission
    android:name="com.bqt.permission"
    android:protectionLevel="normal"/>

<!-- 隱式服務 -->
<service
    android:name=".AIDLService"
    android:permission="com.bqt.permission">
    <intent-filter>
        <action android:name="com.bqt.service.aidl"/>
    </intent-filter>
</service>

客戶端

public class MainActivity extends ListActivity {
    private BookManager mBookManager;
    private MyServiceConnection mServiceConnection;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array = {
            "addBookIn",
            "addBookOut",
            "addBookInout"};
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));

        mServiceConnection = new MyServiceConnection();
        Intent intent = new Intent();
        intent.setAction("com.bqt.service.aidl");
        intent.setPackage("com.bqt.aidl2");
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mServiceConnection != null) {
            unbindService(mServiceConnection);
        }
        mBookManager = null;
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        if (mBookManager != null) {
            try {
                Book book = null, returnBook = null;
                switch (position) {
                    case 0:
                        book = new Book("客戶端-In", 10);
                        Log.i("bqt", "【客戶端-傳進去的Book-執行前】" + book);
                        returnBook = mBookManager.addBookIn(book);
                        break;
                    case 1:
                        book = new Book("客戶端-Out", 20);
                        Log.i("bqt", "【客戶端-傳進去的Book-執行前】" + book);
                        returnBook = mBookManager.addBookOut(book);
                        break;
                    case 2:
                        book = new Book("客戶端-Inout", 30);
                        Log.i("bqt", "【客戶端-傳進去的Book-執行前】" + book);
                        returnBook = mBookManager.addBookInout(book);
                        break;
                }

                Log.i("bqt", "【客戶端-returnBook】" + returnBook);
                Log.i("bqt", "【客戶端-傳進去的Book-執行後】" + book);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Toast.makeText(MainActivity.this, "服務已鏈接", Toast.LENGTH_SHORT).show();
            mBookManager = BookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(MainActivity.this, "服務已斷開", Toast.LENGTH_SHORT).show();
            mBookManager = null;
        }
    }
}
<!-- 聲明啓動服務所須要的權限 -->
<uses-permission android:name="com.bqt.permission"/>

演示過程

In測試

【客戶端-傳進去的Book-執行前】{name=客戶端-In,price=10}
【Service-接收到的Book】{name=客戶端-In,price=10}    //收到了客戶端原始對象的值【in】
【Service-返回的Book】{name=Service-In,price=100}
【客戶端-returnBook】{name=Service-In,price=100}
【客戶端-傳進去的Book-執行後】{name=客戶端-In,price=10}   //客戶端原始對象的值沒有改變

Outui

【客戶端-傳進去的Book-執行前】{name=客戶端-Out,price=20}
【Service-接收到的Book】{name=null,price=0}             //沒有收到客戶端原始對象的值
【Service-返回的Book】{name=Service-Out,price=200}
【客戶端-returnBook】{name=Service-Out,price=200}
【客戶端-傳進去的Book-執行後】{name=Service-Out,price=200}   //修改了客戶端原始對象的值【out】

InOutthis

【客戶端-傳進去的Book-執行前】{name=客戶端-Inout,price=30}
【Service-接收到的Book】{name=客戶端-Inout,price=30}    //收到了客戶端原始對象的值【in】
【Service-返回的Book】{name=Service-Inout,price=300}
【客戶端-returnBook】{name=Service-Inout,price=300}
【客戶端-傳進去的Book-執行後】{name=Service-Inout,price=300}   //修改了客戶端原始對象的值【out】

源碼分析

在 AIDL 文件生成的 java 文件中,在進行遠程調用的時候基本的調用順序是:

  • 先從 Proxy 類中調用對應的方法
  • 而後在這些方法中調用 transact() 方法
  • 而後 Stub 中的 onTransact() 方法就會被調用
  • 而後在這個方法裏面再調用具體的業務邏輯的方法
  • 固然,在這幾個方法調用的過程當中,老是會有一些關於數據的寫入讀出的操做,由於這些是跨進程操做,必須將數據序列化傳輸。

BookManager.Stub.Proxy

private static class Proxy implements com.bqt.aidl.BookManager {
    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.bqt.aidl.Book addBookIn(com.bqt.aidl.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain(); //表明從客戶端流向服務端的數據流
        android.os.Parcel _reply = android.os.Parcel.obtain(); //表明從服務端流向客戶端的數據流
        com.bqt.aidl.Book _result; //表明客戶端調用服務端方法後的返回值,返回值沒什麼可研究的
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            if ((book != null)) {
                _data.writeInt(1); //若是book不爲空,則_data寫入int值1
                book.writeToParcel(_data, 0); //將book中的數據寫入_data中(客戶端的數據被完整保留了起來)
            } else {
                _data.writeInt(0); //若是book爲空,則_data寫入int值0
            }
            mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0); //間接調用onTransact方法
            _reply.readException();
            if ((0 != _reply.readInt())) {
                _result = com.bqt.aidl.Book.CREATOR.createFromParcel(_reply);
            } else {
                _result = null;
            }
            //能夠看到,在返回_result以前,沒有修改客戶端傳遞過來的book對象,因此客戶端原始對象不會被改變
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    @Override
    public com.bqt.aidl.Book addBookOut(com.bqt.aidl.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        com.bqt.aidl.Book _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            //能夠看到,這裏沒有將book對象寫入_data流就開始傳輸了,也即客戶端傳過來的數據內容被丟棄了
            mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0);
            _reply.readException();
            if ((0 != _reply.readInt())) {
                _result = com.bqt.aidl.Book.CREATOR.createFromParcel(_reply);
            } else {
                _result = null;
            }
            if ((0 != _reply.readInt())) {
                book.readFromParcel(_reply); //從_reply讀取數據而後寫入book中(客戶端的數據被改變了)
                //能夠看到,在返回_result以前,會修改客戶端傳遞過來的book對象,因此客戶端原始對象會被改變
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    @Override
    public com.bqt.aidl.Book addBookInout(com.bqt.aidl.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        com.bqt.aidl.Book _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            if ((book != null)) {
                _data.writeInt(1);
                book.writeToParcel(_data, 0); //將book中的數據寫入_data中(客戶端的數據被完整保留了起來)
            } else {
                _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_addBookInout, _data, _reply, 0);
            _reply.readException();
            if ((0 != _reply.readInt())) {
                _result = com.bqt.aidl.Book.CREATOR.createFromParcel(_reply);
            } else {
                _result = null;
            }
            if ((0 != _reply.readInt())) {
                book.readFromParcel(_reply);//從_reply讀取數據而後寫入book中(客戶端的數據被改變了)
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}

BookManager.Stub.onTransact

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    //data表明從客戶端流向服務端的數據流,reply表明從服務端流向客戶端的數據流
    switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(DESCRIPTOR);
            return true;
        }
        case TRANSACTION_addBookIn: {
            data.enforceInterface(DESCRIPTOR);
            com.bqt.aidl.Book _arg0; //_arg0表明客戶端輸入的book對象
            if ((0 != data.readInt())) {
                _arg0 = com.bqt.aidl.Book.CREATOR.createFromParcel(data);//獲取客戶端輸入的book對象
            } else {
                _arg0 = null;
            }
            com.bqt.aidl.Book _result = this.addBookIn(_arg0);//這裏纔是真正調用服務端寫好的實現的地方【代理】
            //_result表明客戶端調用服務端方法後的返回值,返回值沒什麼可研究的
            reply.writeNoException();
            if ((_result != null)) {
                reply.writeInt(1);
                _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); //將返回值寫入reply
            } else {
                reply.writeInt(0);
            }
            //執行完方法以後就結束了,對_arg0的修改沒有同步到reply
            return true;
        }
        case TRANSACTION_addBookOut: {
            data.enforceInterface(DESCRIPTOR);
            com.bqt.aidl.Book _arg0;
            _arg0 = new com.bqt.aidl.Book(); //能夠看到,此時沒有從data裏讀取book對象的操做,而是直接new了一個book對象,這就是爲何服務端收不到客戶端傳過來的數據
            com.bqt.aidl.Book _result = this.addBookOut(_arg0);//【代理】
            reply.writeNoException();
            if ((_result != null)) {
                reply.writeInt(1);
                _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); //將返回值寫入reply
            } else {
                reply.writeInt(0);
            }
            //將_arg0寫入reply中
            if ((_arg0 != null)) {
                reply.writeInt(1);
                _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);//同步_arg0
            } else {
                reply.writeInt(0);
            }
            return true;
        }
        case TRANSACTION_addBookInout: {
            data.enforceInterface(DESCRIPTOR);
            com.bqt.aidl.Book _arg0;
            if ((0 != data.readInt())) {
                _arg0 = com.bqt.aidl.Book.CREATOR.createFromParcel(data);//獲取客戶端輸入的book對象
            } else {
                _arg0 = null;
            }
            com.bqt.aidl.Book _result = this.addBookInout(_arg0);//【代理】
            reply.writeNoException();
            if ((_result != null)) {
                reply.writeInt(1);
                _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                reply.writeInt(0);
            }

            if ((_arg0 != null)) {
                reply.writeInt(1);
                _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);//同步_arg0
            } else {
                reply.writeInt(0);
            }
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

2017-11-2

相關文章
相關標籤/搜索