国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

AIDL使用和Binder機制詳解

2019-11-06 09:36:48
字體:
來源:轉載
供稿:網友

一、AIDL的使用

1.AIDL的簡介

AIDL (Android Interface Definition Language) 是一種接口定義語言,用于生成可以在Android設備上兩個進程之間進行進程間通信(interPRocess communication, ipC)的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數,來完成進程間通信。

簡言之,AIDL能夠實現進程間通信,其內部是通過Binder機制來實現的,后面會具體介紹,現在先介紹AIDL的使用。

2.AIDL的具體使用

AIDL的實現一共分為三部分,一部分是客戶端,調用遠程服務。一部分是服務端,提供服務。最后一部分,也是最關鍵的是AIDL接口,用來傳遞的參數,提供進程間通信。

先在服務端創建AIDL部分代碼。AIDL文件

通過如下方式新建一個AIDL文件

默認生成格式

interface IBookManager {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,            double aDouble, String aString);}默認如下格式,由于本例要操作Book類,實現兩個方法,添加書本和返回書本列表。

定義一個Book類,實現Parcelable接口。

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;    }    public int getBookId() {        return bookId;    }    public void setBookId(int bookId) {        this.bookId = bookId;    }    public String getBookName() {        return bookName;    }    public void setBookName(String bookName) {        this.bookName = bookName;    }    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeInt(this.bookId);        dest.writeString(this.bookName);    }    protected Book(Parcel in) {        this.bookId = in.readInt();        this.bookName = in.readString();    }    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {        @Override        public Book createFromParcel(Parcel source) {            return new Book(source);        }        @Override        public Book[] newArray(int size) {            return new Book[size];        }    };}

由于AIDL只支持數據類型:基本類型(int,long,char,boolean等),String,CharSequence,List,Map,其他類型必須使用import導入,即使它們可能在同一個包里,比如上面的Book。

最終IBookManager.aidl 的實現

// Declare any non-default types here with import statementsimport com.lvr.aidldemo.Book;interface IBookManager {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,            double aDouble, String aString);    void addBook(in Book book);    List<Book> getBookList();}

注意:如果自定義的Parcelable對象,必須創建一個和它同名的AIDL文件,并在其中聲明它為parcelable類型。

Book.aidl

// Book.aidlpackage com.lvr.aidldemo;parcelable Book;

以上就是AIDL部分的實現,一共三個文件。

然后Make Project ,SDK為自動為我們生成對應的Binder類。

在如下路徑下:

其中該接口中有個重要的內部類Stub ,繼承了Binder 類,同時實現了IBookManager接口。這個內部類是接下來的關鍵內容。

public static abstract class Stub extends android.os.Binder implements com.lvr.aidldemo.IBookManager{}

服務端服務端首先要創建一個Service用來監聽客戶端的連接請求。然后在Service中實現Stub 類,并定義接口中方法的具體實現。

//實現了AIDL的抽象函數    private IBookManager.Stub mbinder = new IBookManager.Stub() {        @Override        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {            //什么也不做        }        @Override        public void addBook(Book book) throws RemoteException {            //添加書本            if(!mBookList.contains(book)){                mBookList.add(book);            }        }        @Override        public List<Book> getBookList() throws RemoteException {            return mBookList;        }    };

當客戶端連接服務端,服務端就會調用如下方法:

public IBinder onBind(Intent intent) {        return mbinder;    }就會把Stub實現對象返回給客戶端,該對象是個Binder對象,可以實現進程間通信。本例就不真實模擬兩個應用之間的通信,而是讓Service另外開啟一個進程來模擬進程間通信。

 <service            android:name=".MyService"            android:process=":remote">            <intent-filter>                <category android:name="android.intent.category.DEFAULT"/>                <action android:name="com.lvr.aidldemo.MyService"/>            </intent-filter>        </service>

android:process=":remote"設置為另一個進程。<action android:name="com.lvr.aidldemo.MyService"/>

是為了能讓其他apk隱式bindService。

通過隱式調用的方式來連接service,需要把category設為default,這是因為,隱式調用的時候,intent中的category默認會被設置為default。

客戶端

首先將服務端工程中的aidl文件夾下的內容整個拷貝到客戶端工程的對應位置下,由于本例的使用在一個應用中,就不需要拷貝了,其他情況一定不要忘記這一步。

客戶端需要做的事情比較簡單,首先需要綁定服務端的Service。

     Intent intentService = new Intent();                intentService.setAction("com.lvr.aidldemo.MyService");                intentService.setPackage(getPackageName());                intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                MyClient.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);                Toast.makeText(getapplicationContext(),"綁定了服務",Toast.LENGTH_SHORT).show();

將服務端返回的Binder對象轉換成AIDL接口所屬的類型,接著就可以調用AIDL中的方法了。

if(mIBookManager!=null){ try { mIBookManager.addBook(new Book(18,"新添加的書")); Toast.makeText(getApplicationContext(),mIBookManager.getBookList().size()+"",Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } }

二、Binder的工作原理

Binder機制的運行主要包括三個部分:注冊服務、獲取服務和使用服務。其中注冊服務和獲取服務的流程涉及C的內容,由于個人能力有限,就不予介紹了。

本篇文章主要介紹使用服務時,Binder機制的工作原理。

1.Binder對象的獲取

Binder是實現跨進程通信的基礎,那么Binder對象在服務端和客戶端是共享的,是同一個Binder對象。在客戶端通過Binder對象獲取實現了IInterface接口的對象來調用遠程服務,然后通過Binder來實現參數傳遞。

那么如何維護實現了IInterface接口的對象和獲取Binder對象呢?

服務端獲取Binder對象并保存IInterface接口對象Binder中兩個關鍵方法:

public class Binder implement IBinder{        void attachInterface(IInterface plus, String descriptor)        IInterface queryLocalInterface(Stringdescriptor) //從IBinder中繼承而來      ..........................}

Binder具有被跨進程傳輸的能力是因為它實現了IBinder接口。系統會為每個實現了該接口的對象提供跨進程傳輸,這是系統給我們的一個很大的福利。

Binder具有的完成特定任務的能力是通過它的IInterface的對象獲得的,我們可以簡單理解attachInterface方法會將(descriptor,plus)作為(key,value)對存入Binder對象中的一個Map<String,IInterface>對象中,Binder對象可通過attachInterface方法持有一個IInterface對象(即plus)的引用,并依靠它獲得完成特定任務的能力。queryLocalInterface方法可以認為是根據key值(即參數 descriptor)查找相應的IInterface對象。

在服務端進程,通過實現private IBookManager.Stub mbinder = new IBookManager.Stub() {}抽象類,獲得Binder對象。并保存了IInterface對象。

public Stub(){this.attachInterface(this, DESCRIPTOR);}

客戶端獲取Binder對象并獲取IInterface接口對象

通過bindService獲得Binder對象

MyClient.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);

然后通過Binder對象獲得IInterface對象。

private ServiceConnection mServiceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder binder) {            //通過服務端onBind方法返回的binder對象得到IBookManager的實例,得到實例就可以調用它的方法了            mIBookManager = IBookManager.Stub.asInterface(binder);        }        @Override        public void onServiceDisconnected(ComponentName name) {            mIBookManager = null;        }    };

其中asInterface(binder)方法如下:

public static com.lvr.aidldemo.IBookManager asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.lvr.aidldemo.IBookManager))) {return ((com.lvr.aidldemo.IBookManager)iin);}return new com.lvr.aidldemo.IBookManager.Stub.Proxy(obj);}先通過queryLocalInterface(DESCRIPTOR);查找到對應的IInterface對象,然后判斷對象的類型,如果是同一個進程調用則返回IBookManager對象,由于是跨進程調用則返回Proxy對象,即Binder類的代理對象。

2.調用服務端方法
獲得了Binder類的代理對象,并且通過代理對象獲得了IInterface對象,那么就可以調用接口的具體實現方法了,來實現調用服務端方法的目的。

以addBook方法為例,調用該方法后,客戶端線程掛起,等待喚醒:

@Override public void addBook(com.lvr.aidldemo.Book book) throws android.os.RemoteException{..........//第一個參數:識別調用哪一個方法的ID//第二個參數:Book的序列化傳入數據//第三個參數:調用方法后返回的數據//最后一個不用管mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);_reply.readException();}..........}

省略部分主要完成對添加的Book對象進行序列化工作,然后調用transact方法。

Proxy對象中的transact調用發生后,會引起系統的注意,系統意識到Proxy對象想找它的真身Binder對象(系統其實一直存著Binder和Proxy的對應關系)。于是系統將這個請求中的數據轉發給Binder對象,Binder對象將會在onTransact中收到Proxy對象傳來的數據,于是它從data中取出客戶端進程傳來的數據,又根據第一個參數確定想讓它執行添加書本操作,于是它就執行了響應操作,并把結果寫回reply。代碼概略如下:

case TRANSACTION_addBook:{data.enforceInterface(DESCRIPTOR);com.lvr.aidldemo.Book _arg0;if ((0!=data.readInt())) {_arg0 = com.lvr.aidldemo.Book.CREATOR.createFromParcel(data);}else {_arg0 = null;}//這里調用服務端實現的addBook方法this.addBook(_arg0);reply.writeNoException();return true;}

然后在transact方法獲得_reply并返回結果,本例中的addList方法沒有返回值。

客戶端線程被喚醒。因此調用服務端方法時,應開啟子線程,防止UI線程堵塞,導致ANR。

關于上述步驟可以簡單用如下方式理解:BpBinder(客戶端)對象和BBinder(服務端)對象,它們都從IBinder類中派生而來,BpBinder(客戶端)對象是BBinder(服務端)對象的代理對象,關系圖如下:

client端:BpBinder.transact()來發送事務請求;server端:BBinder.onTransact()會接收到相應事務。

這樣就完成了跨進程間的通信。以上內容就是當調用遠程方法時,Binder機制所實現的運行過程。通過上面的介紹,就可以理解AIDL的使用,并知道在使用AIDL時,Binder所起到的作用。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 嘉善县| 乐平市| 海伦市| 德阳市| 固阳县| 托里县| 遵义市| 佳木斯市| 蓬莱市| 安龙县| 县级市| 体育| 太仆寺旗| 长沙市| 棋牌| 彭山县| 平湖市| 天峨县| 绥棱县| 奉新县| 巴林右旗| 连州市| 江达县| 芦溪县| 即墨市| 江阴市| 石门县| 大竹县| 贺兰县| 镇原县| 察雅县| 泰安市| 桦甸市| 吉木萨尔县| 金乡县| 施甸县| 屯昌县| 巴塘县| 岳阳县| 深圳市| 团风县|