我们都知道,由于不同进程间的内存是不可见的,所以,这就给在于不同进程间的对象的访问带来了麻烦。列如:在A进程中有一个User单例对象,在A进程中,获取该对象getInstance(),都是同一个对象。但是,如果在B进程中调用getInstance(),则此时的对象将是一个新的对象了。我们先来看下面一张图:
点击查看完整项目
进程间通信.png
如上图所示,我们现在如果要实现跨进程通信,需要解决以下几个问题:
1、由于B进程中可能没有User对象,如何得到一个B进程中没有的User对象?
2、如何保证B进程中获取的对象User,是跟A进程的User对象是同一个对象?
3、由于B进程中可能没有User对象,如何在B进程调用A进程中的User中的方法?
其实,进程间通信本质是通过Service,aidl协议实现的。如果有不清楚的请先查看下资料,这里就不详述了。
对于以上问题,下面将一一解答。
一、如何得到一个B进程中没有的User对象?
1、在这里,我是通过一个接口IUserInfo来规范的。也就是说,在客户端A进程中的UserInfo对象,必须要实现这个规范的接口IUserInfo。虽然A进程中的IUserInfo与B进程中的IUserInfo也不是同一个对象,但是,他们都有相同的方法。
2、然后,在接口IUserInfo上第一个一个注解ClassId,该注解的值必须是客户端UserInfo对象的全路径。因为客户端B进程,最终需要根据这个路径,在A进程中找到UserInfo对象并返回。
二、如何保证B进程中获取的对象User,是跟A进程的User对象是同一个对象?
这里实现的方式是——动态代理。
1、由于客户端(SecondActivity)没有需要操作的对象(如:UserInfo,在服务端),所以,从服务端获取的对象只能是一个代理的对象,不能是具体的对象。
2、调用对象中的方法时,由于该对象在服务端是没有的,所以,执行该对象的方法,是通过用动态代理实现。
三、如何在B进程调用A进程中的User中的方法?
这个过程比较复杂。后面通过代码相结合的方式阐述。
为了更好的理解,特意画了价值一千万的图来帮助理解:
四、代码阐述
下面就开始开车了,晕车的童鞋注意了哈~~
1、服务端B进程,需要提前注册访问类的信息。
/**
* 1、注册类
* 2、注册方法
* @param clzz
*/
public void register(Class clzz) {
// 1、注册类
registerClass(clzz);
// 2、注册方法
registerMethod(clzz);
}
// 缓存事件event的class
private void registerClass(Class clzz) {
String name = clzz.getName();
/**
* (1)如果是新的记录,那么会向map中添加该键值对,并返回null。
* (2)如果已经存在,那么不会覆盖已有的值,直接返回已经存在的值。
*/
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信息,则新实例化一个value,并存入mAnnotatedMethodCache中,
* 这样mAnnotatedMethodCache.get(clzz)永远不会为空
* 2、如果mAnnotatedMethodCache中已经缓存clzz信息,则不会重新覆盖以前存的值
*/
mAnnotatedMethodCache.putIfAbsent(clzz,new ConcurrentHashMap());
// 永远不会为空
ConcurrentHashMap 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());
}
这里,将被访问的对象预先缓存,分别将类Class信息和方法Metod信息缓存到两张Map表中。目的是,便于以后客户端B进程调用方法时,服务端A进程直接从这个缓存表中获取。
2、客户端b进程
(1) 连接服务CrossService
CrossService:A进程与B 进程之间 的进程通信就是在这个service中进行的。
/**
* 缓存 远程服务CoreService
*/
private ConcurrentHashMap,CoreService> mCoreServiceCache;
/**
* 缓存service 的connection
*/
private ConcurrentHashMap, CoreServiceConnection> mCoreServiceConnectionCache;
/**
* @param packageName 远程服务的packageName
* @param serviceClass 远程服务的 class
*/
public void bind(Context context, String packageName, Class 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);
}
// 接受远端的binder 对象 进程B就可以了通过Binder对象去操作 服务端的 方法
private class CoreServiceConnection implements ServiceConnection {
Class serviceClz = null;
public CoreServiceConnection(Class 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单例对象
public T getInstance(Class clzz, Object... parameters) {
sendRequest(CrossService.class,clzz,null,parameters);
return invokeProxy(CrossService.class,clzz);
}
1)发送请求给服务端A进程
private Response sendRequest(Class coreServiceClass, Class 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) {
// 方法全类名 方法名 统一 传 方法名+参数名 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进程,响应客户端B进程的请求
这个过程,是通过CrossService中的onBind获取到了。其本质是binder机制。
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: //新实例化一个对象(单例)
responseMake = new InstanceResponseMake();
break;
}
return responseMake.makeResponse(request);
}
};
3)服务端A进程中 ,通过反射完成方法的调用,并最后将调用的结果返回给客户端
注意:最后返回的response,需要转换成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());
// 查找被调用方法的参数
findInvokeMethodParameters(requestBean);
// 查找被调用的方法
mMethod = findInvokeMethod(requestBean);
// 反射调用已经被查找到的方法
Object result = invokeMethod();
// 被调用的方法有返回值
if (result != null) {
ResponseBean responseBean = new ResponseBean(result);
String resultJson = mGson.toJson(responseBean);
response = new Response(resultJson);
}
return response;
}
3)最终,客户端B进程收到了A进程返回的结果response.
此时,其实A进程返回的结果不是直接返回的,而是中间还经过代理对象处理后,再返回的。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 发送请求给服务端(MainActivity),然后服务端执行需要被代理的方法。实现跨进程通信
*/
Response response = Cross.getDefault().sendObjectRequest(mCoreServiceClass, mClzz, method, args);
Log.e(TAG,"invokeMethod===>" + method.getName());
if (response != null && !TextUtils.isEmpty(response.getResponseData())) {
// 若有返回数据,则需要还原,再返回
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进程调用A进程中的方法
由于客户端(B进程)中没有需要操作的对象(如:UserInfo,在服务端),所以,从服务端获取的对象只能是一个代理的对象,不能是具体的对象。
同时,调用获取的对象中的方法,由于该对象在服务端是没有的,所以,执行该对象的方法,通过用动态代理实现。在代理中,去执行该方法,然后进行B进程
与A进程间通信。若有返回值,则需要还原返回值,最后通过代理返回
总之,总体的过程还是比较简单的。不清楚的可以多看看图。最后再把图贴出来,便于大家理解。
点击查看完整项目