日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android ipc 多个客户端,Android IPC之AIDL进阶篇

發(fā)布時間:2023/12/1 Android 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android ipc 多个客户端,Android IPC之AIDL进阶篇 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

在Android IPC之AIDL中我介紹了如何使用AIDL進行多進程通信,不過由于當時個人水平有限,僅僅介紹了最基礎(chǔ)的部分,所以本篇博客主要是在Android IPC之AIDL的基礎(chǔ)上深入介紹下AIDL的進階的幾點理解以及用法。

AIDL接口中的in out inout的含義

在Android IPC之AIDL中稍微提了下,在客戶端與服務端進行復雜數(shù)據(jù)傳遞的時候,需要使用這三個修飾符,表示數(shù)據(jù)的流向,并沒有具體說明,這里通過我的測試給出一個結(jié)果,使用in修飾,客戶端的對象可以"傳遞給"服務端,但是服務端不能修改客戶端的對象,使用out修飾,客戶端的對象不可以"傳遞給"服務端,但是服務端可以修改客戶端的對象,inout則是雙向的,服務端可以拿到客戶端的數(shù)據(jù)也可以修改客戶端的數(shù)據(jù)。"傳遞"之所以有引號,表示客戶端和服務端的對象不是同一個,而是序列化/反序列化的而已。

上面的傳遞的意思是數(shù)據(jù)類型以及值都能傳遞過去,舉例來說,一個Person,初始化為name=a,age=10,

使用in修飾,客戶端為[name=a,age=10]服務端拿到的是[name=a,age=10],但是服務端修改age=11,客戶端還是[name=a,age=10]

使用out修飾,客戶端為[name=a,age=10]服務端拿到的是[name=null,age=0](String的默認類型為空,int的默認類型為0),服務端修改age=11,客戶端變?yōu)閇name=a,age=11]

使用inout修飾符,則服務端可以拿到正確的數(shù)據(jù),對數(shù)據(jù)的修改也會同步到客戶端

oneway的用法

AIDL定義的方法是阻塞的,所以我們需要很注意不要在UI線程中調(diào)用耗時很長的AIDL方法,不然會導致ANR,如果不想客戶端被阻塞可以選擇開啟一個線程去執(zhí)行或者使用oneway修飾被調(diào)用的方法或者接口。這樣當客戶端調(diào)用的時候就不會被阻塞了。

//IRemote.aidl

interface IRemote{

oneway void testOneWay();

}

如果我們多次調(diào)用被oneway修飾的方法,都不會被阻塞,而且遠程方法是順序執(zhí)行的,比如上面的testOneWay()方法被調(diào)用兩次,只有當?shù)谝淮螆?zhí)行完畢,第二次才會繼續(xù)執(zhí)行,但是對于客戶端來說是感知不到的。

服務端感知客戶端是否崩潰

當我們綁定一個遠程服務的時候,如果遠程服務崩潰了,我們可以通過ServiceConnection的onServiceDisconnected感知到,可是如果客戶端崩潰了,服務端怎么感知呢?

對于這種情景,我們可以讓客戶端傳遞一個Binder對象給服務端,然后服務端使用IBinder.linkToDeath監(jiān)聽,當客戶端終止的時候,Binder對象也會被殺死,然后服務端就可以收到客戶端死亡消息了。

binder.linkToDeath(new DeathRecipient() {

@Override

public void binderDied() {

Log.i("IPC", "client died");

}

}, 0);

具體實現(xiàn)如下。

首先定義個AIDL接口

//IRemote.aidl

interface IRemote{

void testClientError(IBinder binder);

}

服務端實現(xiàn)

@Override

public void testClientError(IBinder binder) throws RemoteException {

binder.linkToDeath(new DeathRecipient() {

@Override

public void binderDied() {

Log.i("IPC", "client died");

}

}, 0);

}

客戶端調(diào)用,最后一句int i = 0/0;是為了測試客戶端異常退出

private IBinder mToken = new Binder();

public void testError(View v) {

if (remoteInterface == null) {

return;

}

try {

remoteInterface.testClientError(mToken);

} catch (RemoteException e) {

e.printStackTrace();

}

int i = 0 / 0;

}

擴展:既然通過Binder對象可以感知到對應的進程是否死亡,那么我們也可以換種方式在客戶端獲取服務端的狀態(tài),上面的例子中,我們是主動new了一個Binder對象發(fā)送給服務端,那么怎么獲取到服務端的Binder對象呢?答案就在ServiceConnection的onServiceConnected中,第二個參數(shù)就可以用來監(jiān)聽服務端是否死亡。

服務端調(diào)用客戶端的方法

假設(shè)我們有這樣一個需求,一個客戶連接服務端的時候,服務端需要通知其他所有的客戶端,如果在同一個進程中,我們可以使用回調(diào)的方式,在多進程條件下,我們也可以使用回調(diào),不過客戶端需要實現(xiàn)AIDL接口才行。

具體實現(xiàn)如下

首先定義一個AIDL接口,當連接的時候,服務端調(diào)用所有客戶端的接口,顯示一個Toast

//IClientCallBack.aidl

interface IClientCallBack{

void showToastInClient(String msg);

}

然后添加注冊/解注冊方法

//IRemote.aidl

interface IRemote{

void register(IClientCallBack callback);

void unregister(IClientCallBack callback);

}

客戶端實現(xiàn)AIDL接口

private IClientCallBack mCallBack = new IClientCallBack.Stub() {

@Override

public void showToastInClient(String msg) throws RemoteException {

Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();

}

};

然后接下來就是服務端保存所有的回調(diào)接口到一個List中,剩下的就和普通調(diào)用一樣了。

可是重點來了,如果客戶端注冊了回調(diào),但是沒有解除注冊就意外終止了,那么服務端是無法感知到的,這樣無疑浪費了資源,而且導致程序不穩(wěn)定,所以我們需要在客戶端意外終止的時候移除監(jiān)聽,由于使用的AIDL接口,所以這時我們就可以使用上面的IBinder.linkToDeath方法了。類似如下形式。

@Override

public void register(IClientCallBack callback) throws RemoteException {

callback.asBinder().linkToDeath(new DeathRecipient() {

@Override

public void binderDied() {

// TODO Auto-generated method stub

}

}, 0);

......

}

程序員都是喜歡偷懶的,能簡單就簡單,上面還要我們自己去實現(xiàn)binderDied方法,無疑是降低了開發(fā)效率,好歹谷歌給我們提供了RemoteCallbackList用來為我們保存回調(diào)列表,它會在客戶端異常終止的時候自動移出,免去了我們的人工操作,流程如下。

public class RemoteService extends Service {

//定義RemoteCallbackList對象,保存類型為IClientCallBack

private RemoteCallbackList mList = new RemoteCallbackList();

@Override

public void onDestroy() {

//當RemoteService銷毀的時候,清空RemoteCallbackList列表

mList.kill();

super.onDestroy();

}

private IRemote.Stub mStud = new Stub() {

@Override

public void register(IClientCallBack callback) throws RemoteException {

//保存回調(diào)到RemoteCallbackList中

mList.register(callback);

//開始通知全部客戶端

int i = mList.beginBroadcast();

Log.i("IPC", "size = " + (i - 1));

//遍歷客戶端

while (i > 0) {

i--;

try {

mList.getBroadcastItem(i).showToastInClient("i am from Server");

} catch (RemoteException e) {

e.printStackTrace();

}

}

//結(jié)束通知客戶端

mList.finishBroadcast();

}

@Override

public void unregister(IClientCallBack callback) throws RemoteException {

//將回調(diào)從RemoteCallbackList移除

mList.unregister(callback);

}

};

}

RemoteCallbackList的內(nèi)部實現(xiàn)與我們上面提到的一樣,使用的IBinder.linkToDeath方法。有興趣的可以查看下其源碼。

給服務端加入權(quán)限認證

有時候,我們并不想讓我們的遠程服務隨便的被人綁定,這是就需要使用給我們的服務加入權(quán)限認證才行。一般來說,有兩種方法。

首先我們在AndroidManifest.xml文件中聲明的權(quán)限,如下

然后我們在Service的onBind方法中使用checkCallingOrSelfPermission方法驗證客戶端是否聲明了權(quán)限,如果沒有聲明,則返回null,那么客戶端的onServiceConnected方法將不會被回調(diào),客戶端將綁定失敗。這個方法對于普通的Service同樣適用。

public class MyService extends Service {

private static final String TAG = "MyService";

public MyService() {

}

@Override

public IBinder onBind(Intent intent) {

int permission = checkCallingOrSelfPermission("com.remote.service.PRI");

if (permission == PackageManager.PERMISSION_DENIED) {

Log.i(TAG, "onBind: Error");

return null;

}

Log.i(TAG, "onBind: Success");

return mBinder;

}

}

如果客戶端想綁定我們的服務,那么需要在AndroidManifest.xml文件中聲明這個權(quán)限才行。

對于AIDL來說,我們可以在實現(xiàn)AIDL接口的Stub中,覆寫onTransact,在onTransact方法中進行權(quán)限驗證,如下。

private IRemote.Stub mStud = new IRemote.Stub() {

/**

* 這里可以進行權(quán)限驗證,true,允許綁定,false,不允許綁定

*/

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)

throws RemoteException {

int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");

Log.d("IPC", "onbind check=" + check);

// getCallingPid();

// getCallingUid();

// 只允許包名以org.ipc.demo開頭的app 調(diào)用本服務提供的方法

// 此方法并不能阻止綁定,但是能讓非法調(diào)用者無法使用本服務提供的方法

String[] packagesForUid = getPackageManager().getPackagesForUid(getCallingUid());

if (packagesForUid != null && packagesForUid.length > 0) {

if (packagesForUid[0].startsWith("org.ipc.demo")) {

return super.onTransact(code, data, reply, flags);

}

}

return false;

};

};

在onTransact方法中,我們不僅可以驗證是否聲明了權(quán)限,我們還可以獲取到客戶端的包名,對包名等信息進行限制,但是此方法會回調(diào)客戶端的onServiceConnected方法,但是客戶端無法調(diào)用服務端提供的方法。

參考資料:《Android開發(fā)藝術(shù)探索》第二章、

總結(jié)

以上是生活随笔為你收集整理的android ipc 多个客户端,Android IPC之AIDL进阶篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日韩毛片中文字幕 | 免费av大全 | 91av视频在线播放 | 一级片欧美 | 青青草97| 国产成人+综合亚洲+天堂 | 国产精品av在线播放 | 亚洲国产剧情 | 亚洲精品国产精品国自产观看 | 超碰人人人人人人 | www久久久| 欧美日本亚洲 | av片网站 | 蜜桃视频中文字幕 | av在线伊人 | 中文字幕中文在线 | 日韩一区免费 | av在线成人 | 欧美三级午夜理伦 | 91精品播放 | 激情图片区 | 天天插夜夜爽 | 姑娘第5集高清在线观看 | 久久久久久久久免费视频 | h片在线免费观看 | 77久久| 日韩毛片视频 | 天天天av | 国产精品视频一二区 | 91中文字幕网| 亚洲免费色视频 | 亚洲国产成人自拍 | 国产又粗又黄又爽视频 | 在线亚洲成人 | 久久99精品波多结衣一区 | 日韩精品短片 | www.com黄色| 欧美国产日韩综合 | 激情五月综合 | 日韩国产欧美一区二区三区 | 超碰激情在线 | 欧美激情一区在线 | 成人做爰100 | 不许穿内裤随时挨c调教h苏绵 | 韩国三级中文字幕hd久久精品 | 99久久国产热无码精品免费 | 成人h动漫精品一区二区无码 | 色呦呦国产精品 | 99亚洲天堂 | 午夜寂寞院 | 男女久久久 | 亚洲天堂视频在线观看 | 日韩成人高清视频在线观看 | 懂色av蜜臀av粉嫩av分享吧最新章节 | 亚洲www啪成人一区二区麻豆 | 欧美区在线| 偷自拍 | 人人看人人艹 | 伦理片波多野结衣 | 麻豆视频网页 | 国内精品在线观看视频 | 久久九九精品视频 | 中国少妇高潮 | 久久偷看各类wc女厕嘘嘘偷窃 | 高清一区二区三区 | 午夜电影天堂 | 亚洲视频999 | 精品一区二区精品 | 调教女m荡骚贱淫故事 | 欧美中文字幕在线播放 | 国产a级免费视频 | 午夜18视频在线观看 | 黑人干亚洲女人 | 波多野结衣女同 | 免费观看视频一区二区 | 日本一本一道 | 在线午夜av | 午夜免费播放观看在线视频 | 久久久在线免费观看 | 中文字幕av观看 | 国产成人免费观看视频 | 国产不卡视频在线播放 | 国产床上视频 | 国产亚洲成av人片在线观看桃 | 女同动漫免费观看高清完整版在线观看 | 国产一级做a爱片久久毛片a | 古装做爰无遮挡三级聊斋艳谭 | 中文字幕亚洲在线 | av资源共享| 日本一区欧美 | 国产精品久久久久毛片软件 | 视频一区二区三 | 九热在线视频 | 日韩中文字幕av在线 | 淫妹妹影院| 亚洲激情五月婷婷 | 色午夜视频 | 黄色片视频免费在线观看 | 中文字幕+乱码+中文字幕一区 |