2.2.1 服務端php
2.2.2 客戶端java
3.2.1 服務端android
3.2.2 客戶端git
// 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);
}
複製代碼
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 + '\'' + '}'; } } 複製代碼
/**
* <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;
}
};
}
複製代碼
<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文件到目錄下github
3.4.2 經過Intent指定服務端的服務名稱和所在包,綁定遠程Servicesegmentfault
/**
* 跨進程綁定服務
*/
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,根據須要調用服務提供的接口方法bash
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();
}
}
複製代碼
// 在建立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.1 首先找到aidl編譯生成的Java文件 服務器
5.1.2 分析生成的java文件markdown