我們都知道,由于不同進(jìn)程間的內(nèi)存是不可見的禁偎,所以腿堤,這就給在于不同進(jìn)程間的對(duì)象的訪問帶來了麻煩。列如:在A進(jìn)程中有一個(gè)User單例對(duì)象如暖,在A進(jìn)程中笆檀,獲取該對(duì)象getInstance(),都是同一個(gè)對(duì)象盒至。但是酗洒,如果在B進(jìn)程中調(diào)用getInstance(),則此時(shí)的對(duì)象將是一個(gè)新的對(duì)象了枷遂。我們先來看下面一張圖:
點(diǎn)擊查看完整項(xiàng)目
進(jìn)程間通信.png
如上圖所示樱衷,我們現(xiàn)在如果要實(shí)現(xiàn)跨進(jìn)程通信,需要解決以下幾個(gè)問題:
1酒唉、由于B進(jìn)程中可能沒有User對(duì)象矩桂,如何得到一個(gè)B進(jìn)程中沒有的User對(duì)象?
2痪伦、如何保證B進(jìn)程中獲取的對(duì)象User侄榴,是跟A進(jìn)程的User對(duì)象是同一個(gè)對(duì)象?
3网沾、由于B進(jìn)程中可能沒有User對(duì)象,如何在B進(jìn)程調(diào)用A進(jìn)程中的User中的方法癞蚕?
其實(shí),進(jìn)程間通信本質(zhì)是通過Service辉哥,aidl協(xié)議實(shí)現(xiàn)的桦山。如果有不清楚的請(qǐng)先查看下資料,這里就不詳述了醋旦。
對(duì)于以上問題恒水,下面將一一解答。
一饲齐、如何得到一個(gè)B進(jìn)程中沒有的User對(duì)象寇窑?
1、在這里箩张,我是通過一個(gè)接口IUserInfo來規(guī)范的甩骏。也就是說,在客戶端A進(jìn)程中的UserInfo對(duì)象先慷,必須要實(shí)現(xiàn)這個(gè)規(guī)范的接口IUserInfo饮笛。雖然A進(jìn)程中的IUserInfo與B進(jìn)程中的IUserInfo也不是同一個(gè)對(duì)象,但是论熙,他們都有相同的方法福青。
2、然后脓诡,在接口IUserInfo上第一個(gè)一個(gè)注解ClassId无午,該注解的值必須是客戶端UserInfo對(duì)象的全路徑。因?yàn)榭蛻舳薆進(jìn)程祝谚,最終需要根據(jù)這個(gè)路徑宪迟,在A進(jìn)程中找到UserInfo對(duì)象并返回。
二交惯、如何保證B進(jìn)程中獲取的對(duì)象User次泽,是跟A進(jìn)程的User對(duì)象是同一個(gè)對(duì)象?
這里實(shí)現(xiàn)的方式是——?jiǎng)討B(tài)代理席爽。
1意荤、由于客戶端(SecondActivity)沒有需要操作的對(duì)象(如:UserInfo,在服務(wù)端),所以,從服務(wù)端獲取的對(duì)象只能是一個(gè)代理的對(duì)象只锻,不能是具體的對(duì)象玖像。
2、調(diào)用對(duì)象中的方法時(shí)齐饮,由于該對(duì)象在服務(wù)端是沒有的捐寥,所以,執(zhí)行該對(duì)象的方法沈矿,是通過用動(dòng)態(tài)代理實(shí)現(xiàn)上真。
三、如何在B進(jìn)程調(diào)用A進(jìn)程中的User中的方法羹膳?
這個(gè)過程比較復(fù)雜睡互。后面通過代碼相結(jié)合的方式闡述。
為了更好的理解陵像,特意畫了價(jià)值一千萬的圖來幫助理解:
四就珠、代碼闡述
下面就開始開車了,暈車的童鞋注意了哈~~
1醒颖、服務(wù)端B進(jìn)程妻怎,需要提前注冊(cè)訪問類的信息。
/**
* 1泞歉、注冊(cè)類
* 2逼侦、注冊(cè)方法
* @param clzz
*/
public void register(Class<?> clzz) {
// 1匿辩、注冊(cè)類
registerClass(clzz);
// 2、注冊(cè)方法
registerMethod(clzz);
}
// 緩存事件event的class
private void registerClass(Class<?> clzz) {
String name = clzz.getName();
/**
* (1)如果是新的記錄榛丢,那么會(huì)向map中添加該鍵值對(duì)铲球,并返回null。
* (2)如果已經(jīng)存在晰赞,那么不會(huì)覆蓋已有的值稼病,直接返回已經(jīng)存在的值。
*/
mAnnotatedClassCache.putIfAbsent(clzz,name);
Log.e(TAG,"mAnnotatedClassCache====>" + mAnnotatedClassCache + " ,size=="+mAnnotatedClassCache.size());
}
// 緩存事件event的中所有的方法
private void registerMethod(Class<?> clzz) {
Method[] methods = clzz.getMethods();
/**
* 這樣做的目的:
* 1掖鱼、如果mAnnotatedMethodCache中沒有緩存clzz信息然走,則新實(shí)例化一個(gè)value,并存入mAnnotatedMethodCache中,
* 這樣mAnnotatedMethodCache.get(clzz)永遠(yuǎn)不會(huì)為空
* 2戏挡、如果mAnnotatedMethodCache中已經(jīng)緩存clzz信息芍瑞,則不會(huì)重新覆蓋以前存的值
*/
mAnnotatedMethodCache.putIfAbsent(clzz,new ConcurrentHashMap<String,Method>());
// 永遠(yuǎn)不會(huì)為空
ConcurrentHashMap<String, Method> valueMap = mAnnotatedMethodCache.get(clzz);
for (Method method : methods) {
String key = TypeUtils.getMethodId(method);
valueMap.put(key,method);
}
Log.e(TAG,"mAnnotatedMethodCache====>" + mAnnotatedMethodCache + " ,size=="+mAnnotatedMethodCache.size());
}
這里,將被訪問的對(duì)象預(yù)先緩存增拥,分別將類Class信息和方法Metod信息緩存到兩張Map表中啄巧。目的是,便于以后客戶端B進(jìn)程調(diào)用方法時(shí)掌栅,服務(wù)端A進(jìn)程直接從這個(gè)緩存表中獲取秩仆。
2、客戶端b進(jìn)程
(1) 連接服務(wù)CrossService
CrossService:A進(jìn)程與B 進(jìn)程之間 的進(jìn)程通信就是在這個(gè)service中進(jìn)行的猾封。
/**
* 緩存 遠(yuǎn)程服務(wù)CoreService
*/
private ConcurrentHashMap<Class<? extends CrossService>,CoreService> mCoreServiceCache;
/**
* 緩存service 的connection
*/
private ConcurrentHashMap<Class<? extends CrossService>, CoreServiceConnection> mCoreServiceConnectionCache;
/**
* @param packageName 遠(yuǎn)程服務(wù)的packageName
* @param serviceClass 遠(yuǎn)程服務(wù)的 class
*/
public void bind(Context context, String packageName, Class<? extends CrossService> serviceClass) {
CoreServiceConnection coreServiceConnection ;
if (mCoreServiceConnectionCache.containsKey(serviceClass)){
coreServiceConnection = mCoreServiceConnectionCache.get(serviceClass);
}else {
coreServiceConnection = new CoreServiceConnection(serviceClass);
mCoreServiceConnectionCache.put(serviceClass,coreServiceConnection);
}
Intent intent;
if (!TextUtils.isEmpty(packageName)){
intent = new Intent();
intent.setClassName(packageName,serviceClass.getName());
}else {
intent = new Intent(context,serviceClass);
}
context.bindService(intent,coreServiceConnection, Service.BIND_AUTO_CREATE);
}
// 接受遠(yuǎn)端的binder 對(duì)象 進(jìn)程B就可以了通過Binder對(duì)象去操作 服務(wù)端的 方法
private class CoreServiceConnection implements ServiceConnection {
Class<? extends CrossService> serviceClz = null;
public CoreServiceConnection(Class<? extends CrossService> serviceClass) {
serviceClz = serviceClass;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
CoreService coreService = CoreService.Stub.asInterface(service);
if (serviceClz != null && coreService != null){
mCoreServiceCache.put(serviceClz,coreService);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
if (mCoreServiceCache.containsKey(serviceClz)) {
mCoreServiceCache.remove(serviceClz);
}
if (mCoreServiceConnectionCache.containsKey(serviceClz)) {
mCoreServiceConnectionCache.remove(serviceClz);
}
}
}
(2) 獲取UserInfo單例對(duì)象
public <T> T getInstance(Class<T> clzz, Object... parameters) {
sendRequest(CrossService.class,clzz,null,parameters);
return invokeProxy(CrossService.class,clzz);
}
1)發(fā)送請(qǐng)求給服務(wù)端A進(jìn)程
private <T> Response sendRequest(Class<? extends CrossService> coreServiceClass, Class<T> clzz, Method method, Object[] parameters) {
RequestBean requestBean = new RequestBean();
if (clzz.getAnnotation(ClassId.class) == null) {
requestBean.setClassName(clzz.getName());
requestBean.setResultClassName(clzz.getName());
}else {
requestBean.setClassName(clzz.getAnnotation(ClassId.class).value());
requestBean.setResultClassName(clzz.getAnnotation(ClassId.class).value());
}
if (method != null) {
// 方法全類名 方法名 統(tǒng)一 傳 方法名+參數(shù)名 getInstance(java.lang.String)
requestBean.setMethodName(TypeUtils.getMethodId(method));
}
if (parameters != null && parameters.length > 0) {
RequestParameter[] newParameters = new RequestParameter[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Object parameter = parameters[i];
String parameterName = parameter.getClass().getName();
String parameterValue = mGson.toJson(parameter);
RequestParameter requestParameter = new RequestParameter(parameterName, parameterValue);
newParameters[i] = requestParameter;
}
requestBean.setParameters(newParameters);
}
Request request = new Request(mGson.toJson(requestBean), TYPE_GET);
if (request != null) {
return mServiceConnectionManager.request(coreServiceClass,request);
}
return null;
}
2)A進(jìn)程澄耍,響應(yīng)客戶端B進(jìn)程的請(qǐng)求
這個(gè)過程,是通過CrossService中的onBind獲取到了晌缘。其本質(zhì)是binder機(jī)制齐莲。
CrossService:
private CoreService.Stub mBinder = new CoreService.Stub(){
@Override
public Response send(Request request) throws RemoteException {
IResponseMake responseMake = null;
switch (request.getRequestType()){
case Cross.TYPE_NEW:
responseMake = new ObjectResponseMake();
break;
case Cross.TYPE_GET: //新實(shí)例化一個(gè)對(duì)象(單例)
responseMake = new InstanceResponseMake();
break;
}
return responseMake.makeResponse(request);
}
};
3)服務(wù)端A進(jìn)程中 ,通過反射完成方法的調(diào)用磷箕,并最后將調(diào)用的結(jié)果返回給客戶端
注意:最后返回的response选酗,需要轉(zhuǎn)換成json串的形式)
public Response makeResponse(Request request){
Response response = null;
String requestData = request.getRequestData();
RequestBean requestBean = mGson.fromJson(requestData, RequestBean.class);
mResultClass = mTypeCenter.getClassType(requestBean.getClassName());
// 查找被調(diào)用方法的參數(shù)
findInvokeMethodParameters(requestBean);
// 查找被調(diào)用的方法
mMethod = findInvokeMethod(requestBean);
// 反射調(diào)用已經(jīng)被查找到的方法
Object result = invokeMethod();
// 被調(diào)用的方法有返回值
if (result != null) {
ResponseBean responseBean = new ResponseBean(result);
String resultJson = mGson.toJson(responseBean);
response = new Response(resultJson);
}
return response;
}
3)最終,客戶端B進(jìn)程收到了A進(jìn)程返回的結(jié)果response.
此時(shí)岳枷,其實(shí)A進(jìn)程返回的結(jié)果不是直接返回的芒填,而是中間還經(jīng)過代理對(duì)象處理后,再返回的空繁。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 發(fā)送請(qǐng)求給服務(wù)端(MainActivity)殿衰,然后服務(wù)端執(zhí)行需要被代理的方法。實(shí)現(xiàn)跨進(jìn)程通信
*/
Response response = Cross.getDefault().sendObjectRequest(mCoreServiceClass, mClzz, method, args);
Log.e(TAG,"invokeMethod===>" + method.getName());
if (response != null && !TextUtils.isEmpty(response.getResponseData())) {
// 若有返回?cái)?shù)據(jù)盛泡,則需要還原闷祥,再返回
ResponseBean responseBean = mGson.fromJson(response.getResponseData(), ResponseBean.class);
if (responseBean != null) {
Log.e(TAG,"responseBean===>" + responseBean.toString());
}
if (responseBean.getResultData() != null){
Object resultData = responseBean.getResultData();
String resultStr = mGson.toJson(resultData);
Class<?> returnType = method.getReturnType();
Object realReturnObj = mGson.fromJson(resultStr, returnType);
return realReturnObj;
}
}
return null;
}
(4) B進(jìn)程調(diào)用A進(jìn)程中的方法
由于客戶端(B進(jìn)程)中沒有需要操作的對(duì)象(如:UserInfo,在服務(wù)端),所以,從服務(wù)端獲取的對(duì)象只能是一個(gè)代理的對(duì)象傲诵,不能是具體的對(duì)象凯砍。
同時(shí)箱硕,調(diào)用獲取的對(duì)象中的方法,由于該對(duì)象在服務(wù)端是沒有的果覆,所以颅痊,執(zhí)行該對(duì)象的方法,通過用動(dòng)態(tài)代理實(shí)現(xiàn)局待。在代理中,去執(zhí)行該方法菱属,然后進(jìn)行B進(jìn)程
與A進(jìn)程間通信钳榨。若有返回值,則需要還原返回值纽门,最后通過代理返回
總之薛耻,總體的過程還是比較簡(jiǎn)單的。不清楚的可以多看看圖赏陵。最后再把圖貼出來饼齿,便于大家理解。
點(diǎn)擊查看完整項(xiàng)目