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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Aidl进程间通信详细介绍

發布時間:2025/3/18 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Aidl进程间通信详细介绍 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄介紹

  • 1.問題答疑
  • 2.Aidl相關屬性介紹

    • 2.1 AIDL所支持的數據類型
    • 2.2 服務端和客戶端
    • 2.3 AIDL的基本概念
  • 3.實際開發中案例操作

    • 3.1 aidl通信業務需求
    • 3.2 操作步驟偽代碼
    • 3.3 服務端操作步驟
    • 3.4 客戶端操作步驟
    • 3.5 測試
  • 4.可能出現的問題

    • 4.1 客戶端在子線程中發起通信訪問問題
    • 4.2 什么情況下會導致遠程調用失敗
    • 4.3 設置aidl的權限,需要通過權限才能調用
  • 5.部分源碼解析

    • 5.1 服務端aidl編譯生成的java文件
    • 5.2 客戶端綁定服務端service原理

關于aidl應用案例

  • https://github.com/yangchong211/YCAudioPlayer

關于鏈接

  • 1.技術博客匯總
  • 2.開源項目匯總
  • 3.生活博客匯總
  • 4.喜馬拉雅音頻匯總
  • 5.其他匯總

1.問題答疑

  • 1.1.0 AIDL所支持的數據類型有哪些?
  • 1.1.1 提供給客戶端連接的service什么時候運行?
  • 1.1.2 Stub類是干什么用的呢?
  • 1.1.3 如何解決遠程調用失敗的問題?

2.Aidl相關屬性介紹

2.1 AIDL所支持的數據類型

  • 在AIDL中,并非支持所有數據類型,他支持的數據類型如下所示:

    • 基本數據類型(int、long、char、boolean、double、float、byte、short)
    • String和CharSequence
    • List:只支持ArrayList,并且里面的每個元素必須被AIDL支持
    • Map: 只支持HashMap, 同樣的,里面的元素都必須被AIDL支持,包括key和value
    • Parcelable:所有實現了Parcelable接口的對象
    • AIDL: 所有的AIDL接口本身也可以在AIDL 文件中使用

2.2 服務端和客戶端

  • 2.2.1 服務端

    • 注意:服務端就是你要連接的進程。服務端給客戶端一個Service,在這個Service中監聽客戶端的連接請求,然后創建一個AIDL接口文件,里面是將要實現的方法,注意這個方法是暴露給客戶端的的。在Service中實現這個AIDL接口即可
  • 2.2.2 客戶端

    • 客戶端首先需要綁定服務端的Service,綁定成功后,將服務端返回的Binder對象轉換成AIDL接口所屬的類型,最后調用AIDL的方法就可以了。

2.3 AIDL的基本概念

  • AIDL:Android Interface Definition Language,即Android接口定義語言;用于讓某個Service與多個應用程序組件之間進行跨進程通信,從而可以實現多個應用程序共享同一個Service的功能。

3.實際開發中案例操作

3.1 aidl通信業務需求

  • aidl多進程通信應用——服務端:某app;客戶端:app調試工具。注意:aidl多進程通信是指兩個獨立app之間的通信……
  • 打開app調試工具,可以通過綁定服務端某app的service,獲取到公司app的信息,比如渠道,版本號,簽名,打包時間,token等屬性
  • 通過app調試工具,可以通過aidl接口中的方法設置屬性,設置成功后,查看某app是否設置屬性成功

3.2 操作步驟偽代碼

  • 3.2.1 服務端

    • 步驟1:新建定義AIDL文件,并聲明該服務需要向客戶端提供的接口
    • 補充,如果aidl中有對象,則需要創建對象,并且實現Parcelable
    • 步驟2:在Service子類中實現AIDL中定義的接口方法,并定義生命周期的方法(onCreat、onBind()、blabla)
    • 步驟3:在AndroidMainfest.xml中注冊服務 & 聲明為遠程服務
  • 3.2.2 客戶端

    • 步驟1:拷貝服務端的AIDL文件到目錄下
    • 步驟2:使用Stub.asInterface接口獲取服務器的Binder,根據需要調用服務提供的接口方法
    • 步驟3:通過Intent指定服務端的服務名稱和所在包,綁定遠程Service

3.3 服務端操作步驟

  • 3.3.1 創建一個aidl文件【注意:在main路徑下創建】

    • 可以看到里面有一個AppInfo,注意這個類需要自己創建,并且手動導包進來。否則編譯時找不到……
// ICheckAppInfoManager.aidl package cn.ycbjie.ycaudioplayer; import cn.ycbjie.ycaudioplayer.AppInfo; // Declare any non-default types here with import statementsinterface ICheckAppInfoManager {//獲取app信息,比如token,版本號,簽名,渠道等信息List<AppInfo> getAppInfo(String sign);boolean setToken(String sign,String token);boolean setChannel(String sign,String channel);boolean setAppAuthorName(String sign,String name);}
  • 3.3.2 創建一個AppInfo類,實現Parcelable接口

    • 這個類就是需要用的實體類,因為是跨進程,所以實現了Parcelable接口,這個是Android官方提供的,它里面主要是靠Parcel來傳遞數據,Parcel內部包裝了可序列化的數據,能夠在Binder中自由傳輸數據。
    • 注意:如果用到了自定義Parcelable對象,就需要創建一個同名的AIDL文件,包名要和實體類包名一致。我之前這個地方沒加,導致出現錯誤!
    • 如圖所示:
import android.os.Parcel; import android.os.Parcelable;public class AppInfo implements Parcelable {private String key;private String value;public AppInfo(String key, String value) {this.key = key;this.value = value;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(this.key);dest.writeString(this.value);}public AppInfo() {}protected AppInfo(Parcel in) {this.key = in.readString();this.value = in.readString();}public static final Creator<AppInfo> CREATOR = new Creator<AppInfo>() {@Overridepublic AppInfo createFromParcel(Parcel source) {return new AppInfo(source);}@Overridepublic AppInfo[] newArray(int size) {return new AppInfo[size];}};@Overridepublic String toString() {return "AppInfo{" +"key='" + key + '\'' +", value='" + value + '\'' +'}';} }
  • 3.3.3 在Service子類中實現AIDL中定義的接口方法,并定義生命周期的方法(onCreat、onBind()等)

    • 重寫的onBinde()方法中返回Binder對象,這個Binder對象指向IAdvertManager.Stub(),這個Stub類并非我們自己創建的,而是AIDL自動生成的。系統會為每個AIDL接口在build/source/aidl下生成一個文件夾,它的名稱跟你命名的AIDL文件夾一樣,里面的類也一樣。
    • 創建binder對象,在這個getAppInfo方法中,可以設置app基本信息,方便后期多進程通信測試
/*** <pre>* @author yangchong* blog :* time : 2018/05/30* desc : 用于aidl多進程通信服務service* revise:* </pre>*/ public class AppInfoService extends Service {@Nullable@Overridepublic IBinder onBind(Intent intent) {LogUtils.i("AppInfoService--IBinder:");return binder;}@Overridepublic void onCreate() {super.onCreate();LogUtils.i("AppInfoService--onCreate:");}@Overridepublic void onDestroy() {super.onDestroy();LogUtils.i("AppInfoService--onDestroy:");}/*** 1.核心,Stub里面的方法運行的binder池中。* 2.Stub類并非我們自己創建的,而是AIDL自動生成的。* 系統會為每個AIDL接口在build/generated/source/aidl下生成一個文件夾,它的名稱跟你命名的AIDL文件夾一樣* 3.Stub類,是一個內部類,他本質上是一個Binder類。當服務端和客戶端位于同一個進程時,方法調用不會走跨進程的transact過程,* 當兩者處于不同晉城市,方法調用走transact過程,這個邏輯由Stub的內部代理類Proxy完成。*/private final IBinder binder = new ICheckAppInfoManager.Stub() {@Overridepublic List<AppInfo> getAppInfo(String sign) throws RemoteException {List<AppInfo> list=new ArrayList<>();String aidlCheckAppInfoSign = AppToolUtils.getAidlCheckAppInfoSign();LogUtils.e("AppInfoService--AppInfoService",aidlCheckAppInfoSign+"-------------"+sign);if(!aidlCheckAppInfoSign.equals(sign)){return list;}list.add(new AppInfo("app版本號(versionName)", BuildConfig.VERSION_NAME));list.add(new AppInfo("app版本名稱(versionCode)", BuildConfig.VERSION_CODE+""));list.add(new AppInfo("打包時間", BuildConfig.BUILD_TIME));list.add(new AppInfo("app包名", getPackageName()));list.add(new AppInfo("app作者", SPUtils.getInstance(Constant.SP_NAME).getString("name","楊充")));list.add(new AppInfo("app渠道", SPUtils.getInstance(Constant.SP_NAME).getString("channel")));list.add(new AppInfo("token", SPUtils.getInstance(Constant.SP_NAME).getString("token")));list.add(new AppInfo("App簽名", AppToolUtils.getSingInfo(getApplicationContext(), getPackageName(), AppToolUtils.SHA1)));return list;}@Overridepublic boolean setToken(String sign, String token) throws RemoteException {if(!AppToolUtils.getAidlCheckAppInfoSign().equals(sign)){return false;}SPUtils.getInstance(Constant.SP_NAME).put("token",token);LogUtils.i("AppInfoService--setToken:"+ token);return true;}@Overridepublic boolean setChannel(String sign, String channel) throws RemoteException {if(!AppToolUtils.getAidlCheckAppInfoSign().equals(sign)){return false;}SPUtils.getInstance(Constant.SP_NAME).put("channel",channel);LogUtils.i("AppInfoService--setChannel:"+ channel);return true;}@Overridepublic boolean setAppAuthorName(String sign, String name) throws RemoteException {if(!AppToolUtils.getAidlCheckAppInfoSign().equals(sign)){return false;}SPUtils.getInstance(Constant.SP_NAME).put("name",name);LogUtils.i("AppInfoService--setAppAuthorName:"+ name);return true;}}; }
  • 3.3.4 在AndroidMainfest.xml中注冊服務 & 聲明為遠程服務

    • 在清單文件注冊即可,需要設置action。這個在客戶端中綁定服務service需要用到!
<service android:name=".service.AppInfoService"android:process=":remote"android:exported="true"><intent-filter><action android:name="cn.ycbjie.ycaudioplayer.service.aidl.AppInfoService"/><category android:name="android.intent.category.DEFAULT"/></intent-filter> </service>

3.4 客戶端操作步驟

  • 3.4.1 拷貝服務端的AIDL文件到目錄下

    • 注意:復制時不要改動任何東西!
    • 如圖所示:
  • 3.4.2 通過Intent指定服務端的服務名稱和所在包,綁定遠程Service

    • 通過Intent指定服務端的服務名稱和所在包,進行Service綁定;
    • 創建ServiceConnection對象
    /*** 跨進程綁定服務*/ private void attemptToBindService() {Intent intent = new Intent();//通過Intent指定服務端的服務名稱和所在包,與遠程Service進行綁定//參數與服務器端的action要一致,即"服務器包名.aidl接口文件名"intent.setAction("cn.ycbjie.ycaudioplayer.service.aidl.AppInfoService");//Android5.0后無法只通過隱式Intent綁定遠程Service//需要通過setPackage()方法指定包名intent.setPackage(packName);//綁定服務,傳入intent和ServiceConnection對象bindService(intent, connection, Context.BIND_AUTO_CREATE); }/*** 創建ServiceConnection的匿名類*/ private ServiceConnection connection = new ServiceConnection() {//重寫onServiceConnected()方法和onServiceDisconnected()方法// 在Activity與Service建立關聯和解除關聯的時候調用@Override public void onServiceDisconnected(ComponentName name) {Log.e(getLocalClassName(), "無法綁定aidlServer的AIDLService服務");mBound = false;}//在Activity與Service建立關聯時調用@Override public void onServiceConnected(ComponentName name, IBinder service) {Log.e(getLocalClassName(), "完成綁定aidlServer的AIDLService服務");//使用IAppInfoManager.Stub.asInterface()方法獲取服務器端返回的IBinder對象//將IBinder對象傳換成了mAIDL_Service接口對象messageCenter = ICheckAppInfoManager.Stub.asInterface(service);mBound = true;if (messageCenter != null) {try {//鏈接成功Toast.makeText(MainActivity.this,"鏈接成功",Toast.LENGTH_SHORT).show();} catch (Exception e) {e.printStackTrace();}}} };
  • 3.4.3 使用Stub.asInterface接口獲取服務器的Binder,根據需要調用服務提供的接口方法

    • 通過步驟3.4.2完成了跨進程綁定服務,接下來通過調用方法獲取到數據。這里可以調用getAppInfo方法獲取到服務端[app]的數據
    private void getAppInfo() {//如果與服務端的連接處于未連接狀態,則嘗試連接if (!mBound) {attemptToBindService();Toast.makeText(this, "當前與服務端處于未連接狀態,正在嘗試重連,請稍后再試",Toast.LENGTH_SHORT).show();return;}if (messageCenter == null) {return;}try {List<AppInfo> info = messageCenter.getAppInfo(Utils.getSign(packName));if(info==null || (info.size()==0)){Toast.makeText(this, "無法獲取數據,可能是簽名錯誤!", Toast.LENGTH_SHORT).show();}else {mRecyclerView.setLayoutManager(new LinearLayoutManager(this));FirstAdapter adapter = new FirstAdapter(info, this);mRecyclerView.setAdapter(adapter);adapter.setOnItemClickListener(new FirstAdapter.OnItemClickListener() {@Overridepublic void onItemClick(View view, int position) {}});}} catch (RemoteException e) {e.printStackTrace();} }

3.5 測試

  • 最后看看通過測試工具[客戶端]跨進程獲取服務端app信息截圖

    • 具體可以通過實際案例操作:后來發現跨進程通信原來挺好玩的……項目地址:https://github.com/yangchong211/YCAudioPlayer
    • 如圖所示:

4.可能出現的問題

4.1 客戶端在子線程中發起通信訪問問題

  • 當客戶端發起遠程請求時,客戶端會掛起,一直等到服務端處理完并返回數據,所以遠程通信是很耗時的,所以不能在子線程發起訪問。由于服務端的Binder方法運行在Binder線程池中,所以應采取同步的方式去實現,因為它已經運行在一個線程中呢。

4.2 什么情況下會導致遠程調用失敗

  • Binder是會意外死亡的。如果服務端的進程由于某種原因異常終止,會導致遠程調用失敗,如果我們不知道Binder連接已經斷裂, 那么客戶端就會受到影響。不用擔心,Android貼心的為我們提供了連個配對的方法linkToDeath和unlinkToDeath,通過linkToDeath我們可以給Binder設置一個死亡代理,當Binder死亡時,我們就會收到通知。
// 在創建ServiceConnection的匿名類中的onServiceConnected方法中 // 設置死亡代理 messageCenter.asBinder().linkToDeath(deathRecipient, 0);/*** 給binder設置死亡代理*/ private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {@Overridepublic void binderDied() {if(messageCenter == null){return;}messageCenter.asBinder().unlinkToDeath(deathRecipient, 0);messageCenter = null;//這里重新綁定服務attemptToBindService();} };

4.3 設置aidl的權限,需要通過權限才能調用

<!--給aidl多進程通信,服務加入權限驗證功能--> <permission android:name="aidl.AppInfoService"android:protectionLevel="normal"/>//在AppInfoService服務中驗證權限 @Nullable @Override public IBinder onBind(Intent intent) {LogUtils.i("AppInfoService--IBinder:");int check = checkCallingOrSelfPermission("aidl.AppInfoService");if(check == PackageManager.PERMISSION_DENIED){return null;}return binder; }

5.部分源碼解析

5.1 服務端aidl編譯生成的java文件

  • 5.1.1 首先找到aidl編譯生成的Java文件
  • 5.1.2 分析生成的java文件

    • 這個ICheckAppInfoManager.java就是系統為我們生成的相應java文件,簡單說下這個類。它聲明了三個方法getAppInfo,setToken和setChannel,分明就是我們AIDL接口中的三個方法。同時他聲明了3個id用來標識這幾個方法,id用于標識在transact過程中客戶端請求的到底是哪個方法。接著就是我們的Stub,可以看到它是一個內部類,他本質上是一個Binder類。當服務端和客戶端位于同一個進程時,方法調用不會走跨進程的transact過程,當兩者處于不同晉城市,方法調用走transact過程,這個邏輯由Stub的內部代理類Proxy完成。
    • 這個Stub對象之所以里面有我們AIDL的接口,正是因為官方替我們做好了,我們只要在這里具體實現就好了。

5.2 客戶端綁定服務端service原理

  • 客戶端也非常簡單,首先我們連接到服務端Service,在連接成功時,也就是onServiceConnected方法里,通過asInterface(service)方法可以將服務端的Binder對象轉換成客戶端所需的AIDL的接口的對象。這種轉換是區分進程的,如果是同一進程,那么此方法返回的就是Stub本身,否則返回的就是系統Stub.proxy對象。拿到接口對象之后,我們就能夠調用相應方法進行自己的處理

參考文章

  • Android 進階7:進程通信之 AIDL 的使用:https://blog.csdn.net/u011240877/article/details/72765136
  • Android中AIDL的使用詳解:https://www.jianshu.com/p/d1fac6ccee98
  • Android Aidl的使用:https://blog.csdn.net/menglong0329/article/details/75127547
  • 安卓中AIDL的使用:https://blog.csdn.net/qq_32006371/article/details/71255764
關于我的博客
  • 我的個人站點:www.yczbj.org,www.ycbjie.cn
  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
  • 簡書:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜馬拉雅聽書:http://www.ximalaya.com/zhubo/71989305/
  • 開源中國:https://my.oschina.net/zbj1618/blog
  • 泡在網上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 郵箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
  • segmentfault頭條:https://segmentfault.com/u/xiangjianyu/articles

總結

以上是生活随笔為你收集整理的Aidl进程间通信详细介绍的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。