Hermes 进程间通信框架源码解读
特色:
使得进程间通信像调用本地函数一样方便简单。
轻而易举在本地进程创建其他进程类的对象,轻而易举在本进程获取其他进程的单例,轻而易举在本进程使用其他进程的工具类。
支持进程间函数回调,调用其他进程函数的时候可以传入回调函数,让其他进程回调本进程的方法。
自带内存优化,并且支持跨进程垃圾回收。
基本使用方式:
IPC的主要目的是调用其他进程的函数,Hermes让你方便地调用其他进程函数,调用语句和本地进程函数调用一模一样。
比如,单例模式经常在Android App中使用。假设有一个app有两个进程,它们共享如下单例:
@ClassId(“Singleton”)
public class Singleton {
private static Singleton sInstance = null;
private volatile String mData;
private Singleton() {
mData = new String();
}
public static synchronized Singleton getInstance() {
if (sInstance == null) {
sInstance = new Singleton();
}
return sInstance;
}
@MethodId(“setData”)
public void setData(String data) {
mData = data;
}
@MethodId(“getData”)
public String getData() {
return mData;
}
}
如果不使用Hermes,单例是无法共享的。
假设单例在进程A中,进程B想访问这个单例。那么你写如下接口:
@ClassId(“Singleton”)
public interface ISingleton {
@MethodId(“setData”)
void setData(String data);
@MethodId(“getData”)
String getData();
}
进程B使用单例的时候,代码如下:
//obtain the instance of Singleton
ISingleton singleton = Hermes.getInstance(ISingleton.class);
//Set a data
singleton.setData(“Hello, Hermes!”);
//Get the data
Log.v(TAG, singleton.getData());
整体原理就是:
1.基于Service, AIDL、binder、反射、注解、进程间垃圾回收、动态代理等
2.请求进程通过bindService获取服务进程的IBinder代理对象,然后请求进程将自己的IBinder对象回传给服务进程,这样,服务进程跟请求进程可以通过这两个代理对象进行互相通信,基于这个基础原理,再经过一系列封装,让请求进程调用服务进程的方法,就像在同个进程调用一样,用户感知不到在进行进程间通信,那他是怎么做到无感知的,原理就是动态代理,请求进程通过Hermes框架提供的接口返回的是一个动态代理对象,在InvokeHandler里面对请求进程调用方法的过程进行拦截。然后将参数通过Binde传给服务进程,服务进程拿到参数进行对应方法的操作,然后将结果通过Binder返回给请求进程。就这样,请求进程就感觉是在调用本地进程的方法一样。具体这个过程是怎么做的,下面一个一个地通过源码进行解答。
框架整体结构图:
请求进程端:
暂时无法在飞书文档外展示此内容
服务进程端:
暂时无法在飞书文档外展示此内容
1.服务进程初始化流程:
初始化的作用其实就是注册需要提供给请求进程调用的一些接口以及方法。
注册的原理就是将类里面的方法以及类名信息都暂存起来,后续请求进程调用到服务进程的时候,可以根据暂存的类名信息以及方法信息进行反射调用。
暂时无法在飞书文档外展示此内容
Hermes.java
register()
public static void register(Class<?> clazz) {
checkInit();
TYPE_CENTER.register(clazz);
}
TypeCenter.java
register()
public void register(Class<?> clazz) {
TypeUtils.validateClass(clazz);
registerClass(clazz);
registerMethod(clazz);
}
TypeCenter.java
registerClass()
private void registerClass(Class<?> clazz) {
ClassId classId = clazz.getAnnotation(ClassId.class);
if (classId == null) {
String className = clazz.getName();
mRawClasses.putIfAbsent(className, clazz);
} else {
String className = classId.value();
mAnnotatedClasses.putIfAbsent(className, clazz);
}
}
TypeCenter.java
registerMethod()
private void registerMethod(Class<?> clazz) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
MethodId methodId = method.getAnnotation(MethodId.class);
if (methodId == null) {
mRawMethods.putIfAbsent(clazz, new ConcurrentHashMap<String, Method>());
ConcurrentHashMap<String, Method> map = mRawMethods.get(clazz);
String key = TypeUtils.getMethodId(method);
map.putIfAbsent(key, method);
} else {
mAnnotatedMethods.putIfAbsent(clazz, new ConcurrentHashMap<String, Method>());
ConcurrentHashMap<String, Method> map = mAnnotatedMethods.get(clazz);
String key = TypeUtils.getMethodId(method);
map.putIfAbsent(key, method);
}
}
}
2.请求进程初始化流程
请求进程初始化的过程其实就是通过bindeService获取服务进程的IBinder代理对象,然后将自己的IBinder对象传给服务进程,这样,请求进程跟服务进程就有了互相通信的能力。
暂时无法在飞书文档外展示此内容
Hermes.java
connect()
public static void connect(Context context) {
connectApp(context, null, HermesService.HermesService0.class);
}
Hermes.java
connectApp()
public static void connectApp(Context context, String packageName, Class<? extends HermesService> service) {
init(context);
CHANNEL.bind(context.getApplicationContext(), packageName, service);
}
Channel.java
bind()
public void bind(Context context, String packageName, Class<? extends HermesService> service) {
HermesServiceConnection connection;
synchronized (this) {
if (getBound(service)) {
return;
}
Boolean binding = mBindings.get(service);
if (binding != null && binding) {
return;
}
mBindings.put(service, true);
connection = new HermesServiceConnection(service);
mHermesServiceConnections.put(service, connection);
}
Intent intent;
if (TextUtils.isEmpty(packageName)) {
intent = new Intent(context, service);
} else {
intent = new Intent();
intent.setClassName(packageName, service.getName());
}
context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
Channel.java
onServiceConnected()
public void onServiceConnected(ComponentName className, IBinder service) {
synchronized (Channel.this) {
mBounds.put(mClass, true);
mBindings.put(mClass, false);
IHermesService hermesService = IHermesService.Stub.asInterface(service);
mHermesServices.put(mClass, hermesService);
try {
hermesService.register(mHermesServiceCallback, Process.myPid());
} catch (RemoteException e) {
e.printStackTrace();
Log.e(TAG, "Remote Exception: Check whether "
"the process you are communicating with is still alive.");
return;
}
}
if (mListener != null) {
mListener.onHermesConnected(mClass);
}
}
HermesService.java
register()
@Override
public void register(IHermesServiceCallback callback, int pid) throws RemoteException {
mCallbacks.put(pid, callback);
}
3.请求进程发起通信流程
1)请求的过程其实就是通过动态代理Hook住请求进程的调用方法,然后将方法参数以及方法名信息传给服务进程,服务进程在暂存的集合里面找到对应的方法进行反射调用,将结果通过binder返回。
2)请求进程在调用服务进程方法的时候,会先通过Binder预先发一次消息给服务进程,将时间戳作为key传给服务进程,服务进程拿到这个时间戳作为key映射到对应的对象,后续调用这个对象的方法的时候,就通过这个key值找到服务进程对应的对象,从而达到间接引用的目的,进而可以调用对应的方法。
3)如果传入的参数是个自定义对象,比如回调,那么服务端也会在找到对应方法的时候,通过动态代理,Hook住这个回调对象里面的方法,当这个回调对象里面的方法被调用的时候,通过一开始请求进程传过去的IBinder代理对象,将方法参数回传给请求进程,请求进程通过服务进程带过来的参数,找到对应的回调对象,然后进行反射调用。
暂时无法在飞书文档外展示此内容
1.新建对象的方式调用服务进程的方法
ILoadingTask.java
start()
/**
* Created by Xiaofei on 16/4/25.
*/
@ClassId("LoadingTask")
public interface ILoadingTask {
@MethodId("start")
void start(LoadingCallback loadingCallback);
}
DemoActivity.java
start()
ILoadingTask loadingTask = Hermes.newInstanceInService(HermesService.HermesService1.class, ILoadingTask.class, "pic.png");
loadingTask.start(new LoadingCallback() {
@Override
public void callback(int progress) {
progressBar.setProgress(progress);
}
});
这里会预先发一次请求,传入时间戳给服务进程,作为key值引用要调用的那个对象
Hermes.java
newInstanceInService()
public static <T> T newInstanceInService(Class<? extends HermesService> service, Class<T> clazz, Object... parameters) {
TypeUtils.validateServiceInterface(clazz);
checkBound(service);
ObjectWrapper object = new ObjectWrapper(clazz, ObjectWrapper.TYPE_OBJECT_TO_NEW);
Sender sender = SenderDesignator.getPostOffice(service, SenderDesignator.TYPE_NEW_INSTANCE, object);
try {
Reply reply = sender.send(null, parameters);
if (reply != null && !reply.success()) {
Log.e(TAG, "Error occurs during creating instance. Error code: " reply.getErrorCode());
Log.e(TAG, "Error message: " reply.getMessage());
return null;
}
} catch (HermesException e) {
e.printStackTrace();
return null;
}
object.setType(ObjectWrapper.TYPE_OBJECT);
return getProxy(service, object);
}
将参数都封装包打包发送给服务进程
Sender.java
send()
public synchronized final Reply send(Method method, Object[] parameters) throws HermesException {
mTimeStamp = TimeStampGenerator.getTimeStamp();
if (parameters == null) {
parameters = new Object[0];
}
ParameterWrapper[] parameterWrappers = getParameterWrappers(method, parameters);
MethodWrapper methodWrapper = getMethodWrapper(method, parameterWrappers);
registerClass(method);
setParameterWrappers(parameterWrappers);
Mail mail = new Mail(mTimeStamp, mObject, methodWrapper, mParameters);
mMethod = methodWrapper;
return CHANNEL.send(mService, mail);
}
拿到IBinder代理对象进行进程间通信
Channel.java
send()
public Reply send(Class<? extends HermesService> service, Mail mail) {
IHermesService hermesService = mHermesServices.get(service);
try {
if (hermesService == null) {
return new Reply(ErrorCodes.SERVICE_UNAVAILABLE,
"Service Unavailable: Check whether you have connected Hermes.");
}
return hermesService.send(mail);
} catch (RemoteException e) {
return new Reply(ErrorCodes.REMOTE_EXCEPTION, "Remote Exception: Check whether "
"the process you are communicating with is still alive.");
}
}
进入服务进程的send()设置请求进程回传过来的IBinder代理对象到Receiver对象里面去,后面,如果客户端调用的方法参数有回调,那就用这个代理对象回传给请求进程。
HermesServer.java
send()
public Reply send(Mail mail) {
try {
Receiver receiver = ReceiverDesignator.getReceiver(mail.getObject());
int pid = mail.getPid();
IHermesServiceCallback callback = mCallbacks.get(pid);
if (callback != null) {
receiver.setHermesServiceCallback(callback);
}
return receiver.action(mail.getTimeStamp(), mail.getMethod(), mail.getParameters());
} catch (HermesException e) {
e.printStackTrace();
return new Reply(e.getErrorCode(), e.getErrorMessage());
}
}
将请求进程回传过来的参数设置进去,方便后续寻找到到目标方法进行调用
HermesService.java
action()
public final Reply action(long methodInvocationTimeStamp, MethodWrapper methodWrapper, ParameterWrapper[] parameterWrappers) throws HermesException{
setMethod(methodWrapper, parameterWrappers);
setParameters(methodInvocationTimeStamp, parameterWrappers);
Object result = invokeMethod();
if (result == null) {
return null;
} else {
return new Reply(new ParameterWrapper(result));
}
}
预发的请求主要是在服务进程新建一个目标对象,然后用时间戳作为key值进行引用。后续请求进程就通过key值直接跨进程操作这个服务进程的对象。
Receiver.java
invokeMethod()
protected Object invokeMethod() throws HermesException {
Exception exception;
try {
Object object;
Object[] parameters = getParameters();
if (parameters == null) {
object = mConstructor.newInstance();
} else {
object = mConstructor.newInstance(parameters);
}
OBJECT_CENTER.putObject(getObjectTimeStamp(), object);
return null;
} catch (InstantiationException e) {
exception = e;
} catch (IllegalAccessException e) {
exception = e;
} catch (InvocationTargetException e) {
exception = e;
}
exception.printStackTrace();
throw new HermesException(ErrorCodes.METHOD_INVOCATION_EXCEPTION,
"Error occurs when invoking constructor to create an instance of "
mObjectClass.getName(), exception);
}
回到请求进程端,可以看到预先发完一次请求以后,这里返回一个动态代理对象。
Hermes.java
getProxy()
private static <T> T getProxy(Class<? extends HermesService> service, ObjectWrapper object) {
Class<?> clazz = object.getObjectClass();
T proxy = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz},
new HermesInvocationHandler(service, object));
HERMES_GC.register(service, proxy, object.getTimeStamp());
return proxy;
}
进入到这个InvocationHandler里面,可以看到,当我们请求进程只要发起方法的调用,这里都会被拦截,然后进行跨进程通信,将方法调用的参数都传给服务进程。
HermesInvocationHandler.java
invoke()
public Object invoke(Object proxy, Method method, Object[] objects) {
try {
Reply reply = mSender.send(method, objects);
if (reply == null) {
return null;
}
if (reply.success()) {
return reply.getResult();
} else {
Log.e(TAG, "Error occurs. Error " reply.getErrorCode() ": " reply.getMessage());
return null;
}
} catch (HermesException e) {
e.printStackTrace();
Log.e(TAG, "Error occurs. Error " e.getErrorCode() ": " e.getErrorMessage());
return null;
}
}
封装方法请求参数传给服务进程。
Sender.java
send()
public synchronized final Reply send(Method method, Object[] parameters) throws HermesException {
mTimeStamp = TimeStampGenerator.getTimeStamp();
if (parameters == null) {
parameters = new Object[0];
}
ParameterWrapper[] parameterWrappers = getParameterWrappers(method, parameters);
MethodWrapper methodWrapper = getMethodWrapper(method, parameterWrappers);
registerClass(method);
setParameterWrappers(parameterWrappers);
Mail mail = new Mail(mTimeStamp, mObject, methodWrapper, mParameters);
mMethod = methodWrapper;
return CHANNEL.send(mService, mail);
}
这里真正的拿到IBinder代理对象进行跨进程通信。
Channel.java
send()
public Reply send(Class<? extends HermesService> service, Mail mail) {
IHermesService hermesService = mHermesServices.get(service);
try {
if (hermesService == null) {
return new Reply(ErrorCodes.SERVICE_UNAVAILABLE,
"Service Unavailable: Check whether you have connected Hermes.");
}
return hermesService.send(mail);
} catch (RemoteException e) {
return new Reply(ErrorCodes.REMOTE_EXCEPTION, "Remote Exception: Check whether "
"the process you are communicating with is still alive.");
}
}
进入服务进程的send()设置请求进程回传过来的IBinder代理对象到Receiver对象里面去,后面,如果客户端调用的方法参数有回调,那就用这个代理对象回传给请求进程
HermesService.java
send()
public Reply send(Mail mail) {
try {
Receiver receiver = ReceiverDesignator.getReceiver(mail.getObject());
int pid = mail.getPid();
IHermesServiceCallback callback = mCallbacks.get(pid);
if (callback != null) {
receiver.setHermesServiceCallback(callback);
}
return receiver.action(mail.getTimeStamp(), mail.getMethod(), mail.getParameters());
} catch (HermesException e) {
e.printStackTrace();
return new Reply(e.getErrorCode(), e.getErrorMessage());
}
}
调用用send()方法,然后将请求进程传过来的参数设置到receiver对象里面去,跟刚才流程是一致的
HermesService.java
action()
public final Reply action(long methodInvocationTimeStamp, MethodWrapper methodWrapper, ParameterWrapper[] parameterWrappers) throws HermesException{
setMethod(methodWrapper, parameterWrappers);
setParameters(methodInvocationTimeStamp, parameterWrappers);
Object result = invokeMethod();
if (result == null) {
return null;
} else {
return new Reply(new ParameterWrapper(result));
}
}
这里主要解析请求进程回传过来的方法参数,然后这里有一个点,判断到假设方法有接口类型的形参的,会动态代理那个接口。
Receiver.java
setParameters()
private void setParameters(long methodInvocationTimeStamp, ParameterWrapper[] parameterWrappers) throws HermesException {
if (parameterWrappers == null) {
mParameters = null;
} else {
int length = parameterWrappers.length;
mParameters = new Object[length];
for (int i = 0; i < length; i) {
ParameterWrapper parameterWrapper = parameterWrappers[i];
if (parameterWrapper == null) {
mParameters[i] = null;
} else {
Class<?> clazz = TYPE_CENTER.getClassType(parameterWrapper);
if (clazz != null && clazz.isInterface()) {
registerCallbackReturnTypes(clazz); //****
mParameters[i] = getProxy(clazz, i, methodInvocationTimeStamp);
HERMES_CALLBACK_GC.register(mCallback, mParameters[i], methodInvocationTimeStamp, i);
} else if (clazz != null && Context.class.isAssignableFrom(clazz)) {
mParameters[i] = Hermes.getContext();
} else {
String data = parameterWrapper.getData();
if (data == null) {
mParameters[i] = null;
} else {
mParameters[i] = CodeUtils.decode(data, clazz);
}
}
}
}
}
}
这里可以看到给形参类型是接口类的设置了动态代理,作用就是,当执行到这个接口里面的方法的时候,服务进程会将参数封装起来,通过刚才设置的IBinder代理对象,将参数回传给请求进程,然后在请求进程找到对应的回调对象,然后执行对应的方法
Receiver.java
getProxy()
private Object getProxy(Class<?> clazz, int index, long methodInvocationTimeStamp) {
return Proxy.newProxyInstance(
clazz.getClassLoader(),
new Class<?>[]{clazz},
new HermesCallbackInvocationHandler(methodInvocationTimeStamp, index, mCallback));
}
回到刚才的流程,这里直接反射调用对应的方法。
Receiver.java
invokeMethod()
protected Object invokeMethod() throws HermesException {
Exception exception;
try {
return mMethod.invoke(mObject, getParameters());
} catch (IllegalAccessException e) {
exception = e;
} catch (InvocationTargetException e) {
exception = e;
}
exception.printStackTrace();
throw new HermesException(ErrorCodes.METHOD_INVOCATION_EXCEPTION,
"Error occurs when invoking method " mMethod " on " mObject, exception);
}
反射调用完毕以后将结果封装返回到请求进程
action()
public final Reply action(long methodInvocationTimeStamp, MethodWrapper methodWrapper, ParameterWrapper[] parameterWrappers) throws HermesException{
setMethod(methodWrapper, parameterWrappers);
setParameters(methodInvocationTimeStamp, parameterWrappers);
Object result = invokeMethod();
if (result == null) {
return null;
} else {
return new Reply(new ParameterWrapper(result));
}
}
回到请求进程的invokehandler里面,将结果进行返回,整个调用流程就结束了。
invoke()
public Object invoke(Object proxy, Method method, Object[] objects) {
try {
Reply reply = mSender.send(method, objects);
if (reply == null) {
return null;
}
if (reply.success()) {
return reply.getResult();
} else {
Log.e(TAG, "Error occurs. Error " reply.getErrorCode() ": " reply.getMessage());
return null;
}
} catch (HermesException e) {
e.printStackTrace();
Log.e(TAG, "Error occurs. Error " e.getErrorCode() ": " e.getErrorMessage());
return null;
}
}
假设调用的方法存在回调对象的,我们进去回调代理对象的InvokeHandler里面去看看,主要做的事情就是封装参数,通过客户端传过来的IBinder代理对象,将参数回传给请求进程。
HermesCallbackInvocationHandler.java
invoke()
public Object invoke(Object proxy, Method method, Object[] objects) {
try {
MethodWrapper methodWrapper = new MethodWrapper(method);
ParameterWrapper[] parameterWrappers = TypeUtils.objectToWrapper(objects);
CallbackMail callbackMail = new CallbackMail(mTimeStamp, mIndex, methodWrapper, parameterWrappers);
Reply reply = mCallback.callback(callbackMail);
if (reply == null) {
return null;
}
if (reply.success()) {
/**
* Note that the returned type should be registered in the remote process.
*/
return reply.getResult();
} else {
Log.e(TAG, "Error occurs: " reply.getMessage());
return null;
}
} catch (HermesException e) {
Log.e(TAG, "Error occurs but does not crash the app.", e);
} catch (RemoteException e) {
Log.e(TAG, "Error occurs but does not crash the app.", e);
}
return null;
}
回到请求进程的callback,我们可以看到回去回去找到对应的方法进行反射调用。
Channel.java
callback()
public Reply callback(CallbackMail mail) {
final Pair<Boolean, Object> pair = CALLBACK_MANAGER.getCallback(mail.getTimeStamp(), mail.getIndex());
if (pair == null) {
return null;
}
final Object callback = pair.second;
if (callback == null) {
return new Reply(ErrorCodes.CALLBACK_NOT_ALIVE, "");
}
boolean uiThread = pair.first;
try {
// TODO Currently, the callback should not be annotated!
final Method method = TYPE_CENTER.getMethod(callback.getClass(), mail.getMethod());
final Object[] parameters = getParameters(mail.getParameters());
Object result = null;
Exception exception = null;
if (uiThread) {
boolean isMainThread = Looper.getMainLooper() == Looper.myLooper();
if (isMainThread) {
try {
result = method.invoke(callback, parameters);
} catch (IllegalAccessException e) {
exception = e;
} catch (InvocationTargetException e) {
exception = e;
}
} else {
mUiHandler.post(new Runnable() {
@Override
public void run() {
try {
method.invoke(callback, parameters);
} catch (Exception e) {
e.printStackTrace();
}
}
});
return null;
}
} else {
try {
result = method.invoke(callback, parameters);
} catch (IllegalAccessException e) {
exception = e;
} catch (InvocationTargetException e) {
exception = e;
}
}
if (exception != null) {
exception.printStackTrace();
throw new HermesException(ErrorCodes.METHOD_INVOCATION_EXCEPTION,
"Error occurs when invoking method " method " on " callback, exception);
}
if (result == null) {
return null;
}
return new Reply(new ParameterWrapper(result));
} catch (HermesException e) {
e.printStackTrace();
return new Reply(e.getErrorCode(), e.getErrorMessage());
}
}
2调用的是服务进程已有对象:
通过获取单例对象的getInstance()方法得到这个已有对象的引用,通过请求进程传过来的时间戳对这个对象进程关联。
InstanceGettingReceiver.java
setMethod()
@Override
public void setMethod(MethodWrapper methodWrapper, ParameterWrapper[] parameterWrappers)
throws HermesException {
int length = parameterWrappers.length;
Class<?>[] parameterTypes = new Class<?>[length];
for (int i = 0; i < length; i) {
parameterTypes[i] = TYPE_CENTER.getClassType(parameterWrappers[i]);
}
String methodName = methodWrapper.getName();
Method method = TypeUtils.getMethodForGettingInstance(mObjectClass, methodName, parameterTypes);
if (!Modifier.isStatic(method.getModifiers())) {
throw new HermesException(ErrorCodes.METHOD_GET_INSTANCE_NOT_STATIC,
"Method " method.getName() " of class " mObjectClass.getName() " is not static. "
"Only the static method can be invoked to get an instance.");
}
TypeUtils.validateAccessible(method);
mMethod = method;
}
TypeUtils.java
getMethodForGettingInstance()
public static Method getMethodForGettingInstance(Class<?> clazz, String methodName, Class<?>[] parameterTypes)
throws HermesException {
Method[] methods = clazz.getMethods();
Method result = null;
for (Method method : methods) {
String tmpName = method.getName();
if (methodName.equals("") && (tmpName.equals("getInstance") || method.isAnnotationPresent(GetInstance.class))
|| !methodName.equals("") && tmpName.equals(methodName)) {
if (classAssignable(method.getParameterTypes(), parameterTypes)) {
if (result == null) {
result = method;
} else {
throw new HermesException(ErrorCodes.TOO_MANY_MATCHING_METHODS_FOR_GETTING_INSTANCE,
"When getting instance, there are more than one method named "
methodName " of the class " clazz.getName()
" matching the parameters!");
}
}
}
}
if (result != null) {
if (result.getReturnType() != clazz) {
throw new HermesException(ErrorCodes.GETTING_INSTANCE_RETURN_TYPE_ERROR,
"When getting instance, the method named " methodName " of the class " clazz.getName()
" matches the parameter types but not the return type. The return type is "
result.getReturnType().getName() " but the required type is "
clazz.getName() ".");
}
return result;
}
throw new HermesException(ErrorCodes.GETTING_INSTANCE_METHOD_NOT_FOUND,
"When getting instance, the method named " methodName " of the class "
clazz.getName() " is not found. The class must have a method for getting instance.");
}
3.调用静态方法
UtilityReceiver.java
setMethod()
@Override
protected void setMethod(MethodWrapper methodWrapper, ParameterWrapper[] parameterWrappers)
throws HermesException {
Method method = TYPE_CENTER.getMethod(mClass, methodWrapper);
if (!Modifier.isStatic(method.getModifiers())) {
throw new HermesException(ErrorCodes.ACCESS_DENIED,
"Only static methods can be invoked on the utility class " mClass.getName()
". Please modify the method: " mMethod);
}
TypeUtils.validateAccessible(method);
mMethod = method;
}
@Override
protected Object invokeMethod() throws HermesException {
Exception exception;
try {
return mMethod.invoke(null, getParameters());
} catch (IllegalAccessException e) {
exception = e;
} catch (InvocationTargetException e) {
exception = e;
}
exception.printStackTrace();
throw new HermesException(ErrorCodes.METHOD_INVOCATION_EXCEPTION,
"Error occurs when invoking method " mMethod ".", exception);
}
4.垃圾回收流程
如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。
虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
1.回收服务进程引用对象过程
这个过程主要是回收服务进程通过时间戳引用的对象
暂时无法在飞书文档外展示此内容
在开始发起进程通信的时候,就会在HermesGC这个类里面注册类的相关信息。
Hermes.java
getProxy()
private static <T> T getProxy(Class<? extends HermesService> service, ObjectWrapper object) {
Class<?> clazz = object.getObjectClass();
T proxy = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz},
new HermesInvocationHandler(service, object));
HERMES_GC.register(service, proxy, object.getTimeStamp());
return proxy;
}
这里通过引用队列进行对象的绑定。
HermesGc.java
register()
public void register(Class<? extends HermesService> service, Object object, Long timeStamp) {
gc();
mTimeStamps.put(new PhantomReference<Object>(object, mReferenceQueue), timeStamp);
mServices.put(timeStamp, service);
}
去遍历引用队列是否存在对象,存的话,找出映射的信息,回传给服务进程,进行对象的删除回收。
HermesGc.java
gc()
private void gc() {
synchronized (mReferenceQueue) {
PhantomReference<Object> reference;
Long timeStamp;
HashMap<Class<? extends HermesService>, ArrayList<Long>> timeStamps
= new HashMap<Class<? extends HermesService>, ArrayList<Long>>();
while ((reference = (PhantomReference<Object>) mReferenceQueue.poll()) != null) {
//After a long time, the program can reach here.
timeStamp = mTimeStamps.remove(reference);
if (timeStamp != null) {
Class<? extends HermesService> clazz = mServices.remove(timeStamp);
if (clazz != null) {
ArrayList<Long> tmp = timeStamps.get(clazz);
if (tmp == null) {
tmp = new ArrayList<Long>();
timeStamps.put(clazz, tmp);
}
tmp.add(timeStamp);
}
}
}
Set<Map.Entry<Class<? extends HermesService>, ArrayList<Long>>> set = timeStamps.entrySet();
for (Map.Entry<Class<? extends HermesService>, ArrayList<Long>> entry : set) {
ArrayList<Long> values = entry.getValue();
if (!values.isEmpty()) {
CHANNEL.gc(entry.getKey(), values);
}
}
}
}
这里进行跨进程通信。
Channel.java
gc()
public void gc(Class<? extends HermesService> service, List<Long> timeStamps) {
IHermesService hermesService = mHermesServices.get(service);
if (hermesService == null) {
Log.e(TAG, "Service Unavailable: Check whether you have disconnected the service before a process dies.");
} else {
try {
hermesService.gc(timeStamps);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
在这个地方真正的删除所引用的对象。
HermesService.java
gc()
@Override
public void gc(List<Long> timeStamps) throws RemoteException {
OBJECT_CENTER.deleteObjects(timeStamps);
}
ObjectCenter.java
deleteObjects()
public void deleteObjects(List<Long> timeStamps) {
for (Long timeStamp : timeStamps) {
if (mObjects.remove(timeStamp) == null) {
Log.e(TAG, "An error occurs in the GC.");
}
}
}
2.回收callback流程
这个过程主要回收请求进程暂存的那些回调对象。
暂时无法在飞书文档外展示此内容
请求进程端,每次获取方法参数的时候,都会去判断里面形参是不是个接口,如果是个接口,那就添加到CallbackManager里面
private final ParameterWrapper[] getParameterWrappers(Method method, Object[] parameters) throws HermesException {
int length = parameters.length;
ParameterWrapper[] parameterWrappers = new ParameterWrapper[length];
if (method != null) {
Class<?>[] classes = method.getParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < length; i) {
if (classes[i].isInterface()) {
Object parameter = parameters[i];
if (parameter != null) {
parameterWrappers[i] = new ParameterWrapper(classes[i], null);
} else {
parameterWrappers[i] = new ParameterWrapper(null);
}
if (parameterAnnotations[i] != null && parameter != null) {
CALLBACK_MANAGER.addCallback(
mTimeStamp, i, parameter,
TypeUtils.arrayContainsAnnotation(parameterAnnotations[i], WeakRef.class),
!TypeUtils.arrayContainsAnnotation(parameterAnnotations[i], Background.class));
}
} else if (Context.class.isAssignableFrom(classes[i])) {
parameterWrappers[i] = new ParameterWrapper(TypeUtils.getContextClass(classes[i]), null);
} else {
parameterWrappers[i] = new ParameterWrapper(parameters[i]);
}
}
} else {
for (int i = 0; i < length; i) {
parameterWrappers[i] = new ParameterWrapper(parameters[i]);
}
}
return parameterWrappers;
}
服务进程端:
Receiver.java
action()
public final Reply action(long methodInvocationTimeStamp, MethodWrapper methodWrapper, ParameterWrapper[] parameterWrappers) throws HermesException{
setMethod(methodWrapper, parameterWrappers);
setParameters(methodInvocationTimeStamp, parameterWrappers);
Object result = invokeMethod();
if (result == null) {
return null;
} else {
return new Reply(new ParameterWrapper(result));
}
}
服务进程在设置请求进程发过来的参数的时候,判断是否存在接口类型的形参,如果存在,那么就进行垃圾回收注册。
Receiver.java
setParameters()
private void setParameters(long methodInvocationTimeStamp, ParameterWrapper[] parameterWrappers) throws HermesException {
if (parameterWrappers == null) {
mParameters = null;
} else {
int length = parameterWrappers.length;
mParameters = new Object[length];
for (int i = 0; i < length; i) {
ParameterWrapper parameterWrapper = parameterWrappers[i];
if (parameterWrapper == null) {
mParameters[i] = null;
} else {
Class<?> clazz = TYPE_CENTER.getClassType(parameterWrapper);
if (clazz != null && clazz.isInterface()) {
registerCallbackReturnTypes(clazz); //****
mParameters[i] = getProxy(clazz, i, methodInvocationTimeStamp);
HERMES_CALLBACK_GC.register(mCallback, mParameters[i], methodInvocationTimeStamp, i);
} else if (clazz != null && Context.class.isAssignableFrom(clazz)) {
mParameters[i] = Hermes.getContext();
} else {
String data = parameterWrapper.getData();
if (data == null) {
mParameters[i] = null;
} else {
mParameters[i] = CodeUtils.decode(data, clazz);
}
}
}
}
}
}
也是同样通过引用队列绑定对象。
HermesCallbackGc.java
register()
public void register(IHermesServiceCallback callback, Object object, long timeStamp, int index) {
gc();
mTimeStamps.put(new PhantomReference<Object>(object, mReferenceQueue), Triple.create(callback, timeStamp, index));
}
同样也是去遍历引用队列是否存在对象,存的话,找出映射的信息,回传给服务进程,进行对象的删除回收。
HermesCallbackGc.java
gc()
private void gc() {
synchronized (mReferenceQueue) {
PhantomReference<Object> reference;
Triple<IHermesServiceCallback, Long, Integer> triple;
HashMap<IHermesServiceCallback, Pair<ArrayList<Long>, ArrayList<Integer>>> timeStamps
= new HashMap<IHermesServiceCallback, Pair<ArrayList<Long>, ArrayList<Integer>>>();
while ((reference = (PhantomReference<Object>) mReferenceQueue.poll()) != null) {
triple = mTimeStamps.remove(reference);
if (triple != null) {
Pair<ArrayList<Long>, ArrayList<Integer>> tmp = timeStamps.get(triple.first);
if (tmp == null) {
tmp = new Pair<ArrayList<Long>, ArrayList<Integer>>(new ArrayList<Long>(), new ArrayList<Integer>());
timeStamps.put(triple.first, tmp);
}
tmp.first.add(triple.second);
tmp.second.add(triple.third);
}
}
Set<Map.Entry<IHermesServiceCallback, Pair<ArrayList<Long>, ArrayList<Integer>>>> set = timeStamps.entrySet();
for (Map.Entry<IHermesServiceCallback, Pair<ArrayList<Long>, ArrayList<Integer>>> entry : set) {
Pair<ArrayList<Long>, ArrayList<Integer>> values = entry.getValue();
if (!values.first.isEmpty()) {
try {
entry.getKey().gc(values.first, values.second);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
最终这里回到请求进程的gc方法里面,对没用的callaback对象进行删除
Channel.java
gc()
@Override
public void gc(List<Long> timeStamps, List<Integer> indexes) throws RemoteException {
int size = timeStamps.size();
for (int i = 0; i < size; i) {
CALLBACK_MANAGER.removeCallback(timeStamps.get(i), indexes.get(i));
}
}
CallBackManager.java
removeCallback()
public void removeCallback(long timeStamp, int index) {
long key = getKey(timeStamp, index);
if (mCallbackWrappers.remove(key) == null) {
Log.e(TAG, "An error occurs in the callback GC.");
}
}
5.注解的作用:
1)如果两个进程属于两个不同的app(分别叫App A和App B),App A想访问App B的一个类,并且App A的接口和App B的对应类有相同的包名和类名,那么就没有必要在类和接口上加@ClassId注解。但是要注意使用ProGuard后类名和包名仍要保持一致。
2)如果接口和类里面对应的方法的名字相同,那么也没有必要在方法上加上@MethodId注解,同样注意ProGuard的使用后接口内的方法名字必须仍然和类内的对应方法名字相同。
TyperCenter.java
registerClass()
private void registerClass(Class<?> clazz) {
ClassId classId = clazz.getAnnotation(ClassId.class);
if (classId == null) {
String className = clazz.getName();
mRawClasses.putIfAbsent(className, clazz);
} else {
String className = classId.value();
mAnnotatedClasses.putIfAbsent(className, clazz);
}
}
private void registerMethod(Class<?> clazz) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
MethodId methodId = method.getAnnotation(MethodId.class);
if (methodId == null) {
mRawMethods.putIfAbsent(clazz, new ConcurrentHashMap<String, Method>());
ConcurrentHashMap<String, Method> map = mRawMethods.get(clazz);
String key = TypeUtils.getMethodId(method);
map.putIfAbsent(key, method);
} else {
mAnnotatedMethods.putIfAbsent(clazz, new ConcurrentHashMap<String, Method>());
ConcurrentHashMap<String, Method> map = mAnnotatedMethods.get(clazz);
String key = TypeUtils.getMethodId(method);
map.putIfAbsent(key, method);
}
}
}
6.引用文章:
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfkgeeg
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24