亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

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

IPC機制<二>AIDL

2019-11-07 23:55:26
字體:
來源:轉載
供稿:網友

上次講到ipC機制的幾種通信方式,但是沒有講完,還剩下幾種方式沒有講,今天繼續。

在上次講到Service的時候,說過AIDL,但是那種只是非常簡單的方式,今天將會詳細的講述AIDL。

一、AIDL 中的數據類型

aidl支持的數據類型:

基本數據類型(int,long,char,boolean,double等)String和CharSequenceList:只支持ArrayList,里面的每個元素都必須能夠被AIDL支持。Map:只支持HashMap,里面的每個元素都必須能夠被AIDL支持Parcelable:所有實現了Parcelable接口的對象。AIDL:所有的AIDL接口本身也可以在AIDL中使用。

注意:

自定義的Parcelable對象和AIDL對象必須要顯示import進來,不管他們是否在同一個包中。

package cn.demo.zx_aidl_learn;import cn.demo.zx_aidl_learn.domain.Book;interface IService { List<Book>getBookList(); void addBook(in Book b);}

如果AIDL文件中用到了自定義的Parcelable對象,那么必須新建一個和它同名的AIDL文件,并在其中聲明為parcelable類型。

在AIDL中處理基本數據類型,其他類型的參數必須標上方向:in,out或者inout。in表示輸入型參數,out表示輸出型參數,inout表示輸入輸出型參數。AIDL接口中只支持方法不支持聲明靜態常量。AIDL的包結構在服務端和客戶端要保持一致,否則運行會出錯,這是因為客戶端需要反序列化服務端中和AIDL接口相關的所有類,如果類的完整路徑不一樣的話,就無法成功反序列化。

案例:

服務端:

public class BookManagerService extends Service { /** * 使用CopyOnWriteArrayList的原因: * 因為CopyOnWriteArrayList支持并發讀/寫,AIDL方法是在服務端的Binder的線程池中執行的, * 因此當多個客戶端同時連接的時候,會存在多個線程同時訪問的情形,所以我們要在AIDL方法中 * 處理線程同步,而我們這里直接使用CopyOnWriteArrayList來進行自動的線程同步。 * * AIDL中能夠使用的List只有ArrayList,但是這里為什么還使用它呢? * 因為AIDL中所支持的是抽象的List,而List只是一個接口,因此雖然服務端返回的是CopyOnWriteArrayList, * 但是在Binder中會按照List的規范去訪問數據并最終返回一個新的ArrayList傳遞給客戶端。還有就是類似的是 * ConcurrentHashMap. */ CopyOnWriteArrayList<Book>mBookList = new CopyOnWriteArrayList<>(); public BookManagerService() { } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onCreate() { super.onCreate(); mBookList.add(new Book(1,"android")); mBookList.add(new Book(2,"ios學習")); } PRivate Binder mBinder = new IService.Stub(){ @Override public List<Book> getBookList() throws RemoteException { return mBookList; } @Override public void addBook(Book b) throws RemoteException { mBookList.add(b); } };}

客戶端:

public class MainActivity extends AppCompatActivity { private IService mService = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this,BookManagerService.class); startService(intent); bindService(intent,conn,BIND_AUTO_CREATE); } public void findBook(View v){ /** * 注意:服務端的方法有可能是耗時的方法,那么下面的調用就有可能早能ANR異常,需要注意。 */ try { List<Book> bookList = mService.getBookList(); if(bookList!=null){ for(Book b : bookList){ System.out.println("書籍::"+b.toString()); } } } catch (RemoteException e) { e.printStackTrace(); } } private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } };}

清單文件AndroidMenifest:

<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/A上述案例只是講述客戶端從服務端獲取圖書信息,如果是在服務端每添加一本書籍就向客戶端發送消息,那么怎么實現呢?

這里就會用到觀察者模式。

二、AIDL中的觀察者模式

創建一個aidl接口,每個用戶都需要實現這個接口并且向圖書館申請新書的提醒功能。

新創建的aidl文件:

import cn.demo.zx_aidl_learn.domain.Book;interface IOnNewBookArrivedListener { void onNewBookArrived(in Book newBook);}import cn.demo.zx_aidl_learn.domain.Book;import cn.demo.zx_aidl_learn.IOnNewBookArrivedListener;interface IService { List<Book>getBookList(); void addBook(in Book b); void registerListener(in IOnNewBookArrivedListener listener); void unregisterListener(in IOnNewBookArrivedListener listener);}

在MainActivity中創建一個IOnNewBookArrivedListener對象,然后通過注冊的方式將IOnNewBookArrivedListener對象傳遞給服務端:

private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IService.Stub.asInterface(service); try { /** * 注冊對新書的監控 */ mService.registerListener(listener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { }};private IOnNewBookArrivedListener listener = new IOnNewBookArrivedListener.Stub() { @Override public void onNewBookArrived(Book newBook) throws RemoteException { System.out.println("新到的圖書::"+newBook.toString()); mHandler.obtainMessage(0,newBook).sendToTarget(); }};

服務端通過獲取傳入的IOnNewBookArrivedListener對象來調用客戶端中的方法來通知客戶端:

/** * 發送新書已到的消息到客戶端 * 怎樣通知呢? * 通過客戶端在主持監聽的時候傳入的IOnNewBookArrivedListener對象來通知客戶端 * @param newBook */private void sendNewBookArrivedToClient(Book newBook){ mBookList.add(newBook); for(int i=0;i<mListenerList.size();i++){ IOnNewBookArrivedListener listener = mListenerList.get(i); try { listener.onNewBookArrived(newBook); } catch (RemoteException e) { e.printStackTrace(); } }}

詳細功能實現請查看源碼

三、取消注冊功能

當我們實現了注冊功能之后,怎樣取消注冊功能呢?

@Overrideprotected void onDestroy() { super.onDestroy(); if(mService!=null || mService.asBinder().isBinderAlive()){ try { mService.unregisterListener(listener); } catch (RemoteException e) { e.printStackTrace(); } } unbindService(conn);}

我們通過上述方法無法取消注冊。這是為什么呢?

這是因為在客戶端傳入的IOnNewBookArrivedListener對象是同一個對象,但是在服務端卻又變成了另外的一個對象了。因為對象無法跨進程通信,服務端接收的IOnNewBookArrivedListener對象是通過序列化之后得到對象,所以無法取消注冊,這種情況怎么處理呢?

這里我們就會用到RemoteCallbackList類,它是系統專門提供的用于刪除跨進程listener的接口,它是一個泛型,支持管理任意的AIDL接口,因為它繼承了IInterface接口,aidl接口都繼承IInterface。

它的工作原理非常簡單,就是在它的內部有一個專門用于保存所有的AIDL回調的Map集合:

ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();

其中Callback中封裝了真正的遠程listener,當客戶端注冊listener的時候,它會把這個listener的信息存入mCallback中,其中key和value分別通過下面的方式獲得:

private final class Callback implements IBinder.DeathRecipient。。。。public boolean register(E callback, Object cookie) { synchronized (mCallbacks) { if (mKilled) { return false; } IBinder binder = callback.asBinder(); try { Callback cb = new Callback(callback, cookie); binder.linkToDeath(cb, 0); mCallbacks.put(binder, cb); return true; } catch (RemoteException e) { return false; } }}

到這里就應該知道,雖然在客戶端傳入的同一對象在服務端會生成了不同的對象,但是它的底層的Binder對象卻沒有變化,利用這個特性就可以實現上面的功能了。當客戶端取消注冊的時候,我們會便利所有的listener接口,找到與客戶端傳入的listener具有相同Binder對象的服務端listener,然后把它刪除即可。

服務端的代碼:

RemoteCallbackList<IOnNewBookArrivedListener>mListenerList = new RemoteCallbackList<>();@Override public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {// if(!mListenerList.contains(listener)){// mListenerList.add(listener);// } mListenerList.register(listener); } @Override public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {// if(mListenerList.contains(listener)){// mListenerList.remove(listener);// } mListenerList.unregister(listener); }/** * 發送新書已到的消息到客戶端 * 怎樣通知呢? * 通過客戶端在主持監聽的時候傳入的IOnNewBookArrivedListener對象來通知客戶端 * @param newBook */private void sendNewBookArrivedToClient(Book newBook){ mBookList.add(newBook);// for(int i=0;i<mListenerList.size();i++){ // IOnNewBookArrivedListener listener = mListenerList.get(i); // try { // listener.onNewBookArrived(newBook); // } catch (RemoteException e) { // e.printStackTrace(); // } // } int N = mListenerList.beginBroadcast(); for(int i=0;i<N;i++){ IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i); try { listener.onNewBookArrived(newBook); } catch (RemoteException e) { e.printStackTrace(); } } mListenerList.finishBroadcast();}

四、設置死亡代理

關于死亡代理,以前講過,這里在說明一下,當客戶端在與服務端進行跨進程通信的時候,服務端因為異常而被銷毀,但是服務端卻不知道,那么怎樣解決這個問題呢,這里就講到了重新連接服務的兩種方法,第一種就是死亡代理:

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { mService.asBinder().unlinkToDeath(mDeathRecipient,0); mService = null; //重新連接 bindService(intent,conn,BIND_AUTO_CREATE); }};private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IService.Stub.asInterface(service); try { service.linkToDeath(mDeathRecipient,0); } catch (RemoteException e) { e.printStackTrace(); } try { /** * 注冊對新書的監控 */ mService.registerListener(listener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { }};

還有一種方法就是在onServiceDisconnected中重新連接綁定服務。

五、權限驗證

我們不想讓所有的客戶端訪問我們的服務端,我們希望具有某些權限的客戶端才能訪問,怎樣實現?

第一種方法,在onBind中進行驗證。驗證不通過就返回null,造成無法連接服務端。這種驗證很多,這里介紹其中一種permission權限驗證,在服務端定義一個權限。

AndroidMenifest:

<permission android:name="cn.demo.zx_aidl_learn.YANZHENG"/>

服務端:

@Overridepublic IBinder onBind(Intent intent) { int check = checkCallingOrSelfPermission("cn.demo.zx_aidl_learn.YANZHENG"); if(check== PackageManager.PERMISSION_DENIED){ return null; } return mBinder;}

如果想要訪問這個服務端的話,那么客戶端就需要設置權限:

<uses-permission android:name="cn.demo.zx_aidl_learn.YANZHENG"/>

另一種方法,這種方法就是在服務端的onTransact方法中進行權限驗證,如果驗證失敗返回false。驗證的方法也是很多,可以通過permission驗證,也可以通過包名驗證。permission驗證的方法與上述onBind方法中差不多,唯一的區別就是返回false。包名驗證就通過getCallinngUidgetCallingPid獲取客戶端的uid和pid從而獲取包名,進行驗證。

String[] packageNames = getPackageManager().getPacagesForUid(getCallingUid());if(!packageNames[0].startsWith("cn.demo")){ return false;}

注意

當客戶端訪問服務端的方法時候,客戶端會等待跨進程通信,如果服務端中的方法是一個耗時的方法的話,那么客戶端就需要在子線程中去訪問,如果在UI線程中,容易造成ANR異常。服務端訪問客戶端的方法也是一樣的。

源碼下載:

https://github.com/zhangxun1900/my_ipc_learn/


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
成人午夜激情免费视频| 亚洲国产精品视频在线观看| 亚洲国产欧美久久| 成人黄色av播放免费| 中文字幕亚洲欧美日韩2019| 欧美在线一区二区三区四| 日韩av电影在线免费播放| 黑人巨大精品欧美一区二区一视频| 国产精品夫妻激情| 茄子视频成人在线| 亚洲欧美国产制服动漫| 久久天天躁狠狠躁夜夜躁2014| 国产aaa精品| 在线激情影院一区| 国产欧美一区二区白浆黑人| 欧美自拍大量在线观看| 色综合久久悠悠| 国产精品一区久久久| www亚洲精品| 亚洲综合色激情五月| 中文字幕日韩欧美在线| 日韩中文字幕在线观看| 这里只有精品丝袜| 国产精品xxx视频| 国产成人精品a视频一区www| 亚洲第一页自拍| 国产精品无av码在线观看| 亚洲人成电影在线观看天堂色| 国产精品日韩在线| 国产亚洲精品久久久久动| 一本一本久久a久久精品综合小说| 亚洲女人初尝黑人巨大| 欧美综合第一页| 国模视频一区二区| 国产在线不卡精品| 国产精品成人播放| 蜜臀久久99精品久久久无需会员| 欧美极品少妇全裸体| 久久久爽爽爽美女图片| 国内精品久久久久影院 日本资源| 亚洲全黄一级网站| 精品国产美女在线| 91久久久在线| 色综合影院在线| 久久久在线观看| 91九色国产视频| 久久亚洲国产成人| 国产91对白在线播放| 91精品国产91久久久久福利| 国产精品av免费在线观看| 隔壁老王国产在线精品| 日韩中文字幕视频在线| 最新的欧美黄色| 成人a视频在线观看| 国产成人亚洲综合青青| 国产精品免费一区二区三区都可以| 欧美性猛交xxxxx免费看| 欧美成人黄色小视频| 97**国产露脸精品国产| 久久久久久午夜| 成人网欧美在线视频| 日韩中文理论片| 久久久久久亚洲精品| 国产又爽又黄的激情精品视频| 精品露脸国产偷人在视频| 国产精品欧美一区二区| 欧日韩在线观看| 欧美性xxxxx极品| 久久久久久久久久久国产| 欧美视频在线视频| 亚洲人成欧美中文字幕| 亚洲一区二区三区在线免费观看| 日韩在线中文字幕| 亚洲第一二三四五区| 国产人妖伪娘一区91| 欧美成人黑人xx视频免费观看| 国产亚洲欧洲在线| 亚洲美女av黄| 国产视频一区在线| 日韩中文字幕免费| 亚洲第一天堂av| yellow中文字幕久久| 欧美成人在线免费| 91高清视频在线免费观看| 欧美日本在线视频中文字字幕| 中文字幕亚洲一区| 亚洲一区亚洲二区亚洲三区| 色噜噜狠狠狠综合曰曰曰88av| 亚洲人成电影在线播放| 亚洲欧美另类人妖| xx视频.9999.com| 国模视频一区二区三区| 欧美疯狂性受xxxxx另类| 国产精品久久久999| 亚洲午夜未满十八勿入免费观看全集| 欧美大片第1页| 久久99久久亚洲国产| 久久久久久12| 国产欧美韩国高清| 久久久久久尹人网香蕉| 久久久久久久国产精品| 久久久久久久久久久免费精品| 欧美国产精品va在线观看| 国产精品福利在线观看网址| 亚洲福利精品在线| 国产精品人成电影在线观看| 成人av.网址在线网站| 九九久久久久99精品| 亚洲天堂男人天堂女人天堂| 久久在线观看视频| 国产一区二中文字幕在线看| 亚洲精品欧美日韩| 久久久久久久久综合| 成人免费xxxxx在线观看| 精品视频一区在线视频| 77777亚洲午夜久久多人| 国产成人综合亚洲| 日韩极品精品视频免费观看| 韩国国内大量揄拍精品视频| 97久久精品人搡人人玩| 欧美在线观看视频| 亚洲黄色www| 影音先锋欧美精品| 亚洲电影免费观看高清完整版在线| 亚洲高清久久久久久| 久久夜色精品国产亚洲aⅴ| 成人国产亚洲精品a区天堂华泰| 国产精品91视频| 久久久久国色av免费观看性色| 精品欧美国产一区二区三区| 久久久女女女女999久久| 国产精品久久久久久av福利| 97在线看免费观看视频在线观看| 91在线视频精品| 久久天天躁狠狠躁老女人| 久久99热精品| 国产欧美精品久久久| 亚洲色图偷窥自拍| 国产成人精品免高潮在线观看| 尤物99国产成人精品视频| 高清日韩电视剧大全免费播放在线观看| 国产精品va在线| 2018日韩中文字幕| 91极品女神在线| 欧美成人精品不卡视频在线观看| 亚洲色图国产精品| 国产精品va在线播放我和闺蜜| 久久免费观看视频| 精品久久久久久久久久久| 亚洲va久久久噜噜噜久久天堂| 欧美一级大片在线免费观看| 国产精品爽黄69天堂a| 国产精品视频精品| 亚洲精品第一国产综合精品| 一区二区成人av| 欧洲中文字幕国产精品| 久久福利网址导航| 91香蕉嫩草神马影院在线观看| 久久久国产在线视频| 国产日韩欧美在线播放| xxxxx成人.com| 精品久久香蕉国产线看观看亚洲| 国产精品一二区| 欧美xxxx18性欧美|