2.Aidl相關屬性介紹php
3.實際開發中案例操做java
4.可能出現的問題android
5.部分源碼解析git
在AIDL中,並不是支持全部數據類型,他支持的數據類型以下所示:github
2.2.1 服務端segmentfault
2.2.2 客戶端服務器
3.2.1 服務端app
3.2.2 客戶端ide
3.3.1 建立一個aidl文件【注意:在main路徑下建立】工具
// ICheckAppInfoManager.aidl package cn.ycbjie.ycaudioplayer; import cn.ycbjie.ycaudioplayer.AppInfo; // Declare any non-default types here with import statements interface 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接口
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; } @Override public int describeContents() { return 0; } @Override public 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>() { @Override public AppInfo createFromParcel(Parcel source) { return new AppInfo(source); } @Override public AppInfo[] newArray(int size) { return new AppInfo[size]; } }; @Override public String toString() { return "AppInfo{" + "key='" + key + '\'' + ", value='" + value + '\'' + '}'; } }
3.3.3 在Service子類中實現AIDL中定義的接口方法,並定義生命週期的方法(onCreat、onBind()等)
/** * <pre> * @author yangchong * blog : * time : 2018/05/30 * desc : 用於aidl多進程通訊服務service * revise: * </pre> */ public class AppInfoService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { LogUtils.i("AppInfoService--IBinder:"); return binder; } @Override public void onCreate() { super.onCreate(); LogUtils.i("AppInfoService--onCreate:"); } @Override public 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() { @Override public 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; } @Override public 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; } @Override public 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; } @Override public 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中註冊服務 & 聲明爲遠程服務
<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.1 拷貝服務端的AIDL文件到目錄下
3.4.2 經過Intent指定服務端的服務名稱和所在包,綁定遠程Service
/** * 跨進程綁定服務 */ 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,根據須要調用服務提供的接口方法
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() { @Override public void onItemClick(View view, int position) { } }); } } catch (RemoteException e) { e.printStackTrace(); } }
最後看看經過測試工具[客戶端]跨進程獲取服務端app信息截圖
// 在建立ServiceConnection的匿名類中的onServiceConnected方法中 // 設置死亡代理 messageCenter.asBinder().linkToDeath(deathRecipient, 0); /** * 給binder設置死亡代理 */ private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { if(messageCenter == null){ return; } messageCenter.asBinder().unlinkToDeath(deathRecipient, 0); messageCenter = null; //這裏從新綁定服務 attemptToBindService(); } };
<!--給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.1.2 分析生成的java文件