(自以为)优雅的跨进程单例的实现思路
作者:网络转载 发布时间:[ 2016/8/5 17:10:22 ] 推荐标签:测试用例
3.3 实例-Service
ServiceA.class是A进程提供单例给其他进程的服务的类,每个进程都需要有一个(这样别的进程才能绑定过来)。所以在这个例子,会有ServiceB.class,ServiceC.class,这几个类的实现都是一样的,因此这里他们其实只是简单的继承了一个基类BaseService,并没有做其他改动,需要派生出来的原因是需要在AndroidManifest.xml里为不同进程指定一个Service。
代码如下:
public class BaseService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new InstanceTransferImp();
}
}
public class ServiceA extends BaseService {}
public class ServiceB extends BaseService {}
public class ServiceC extends BaseService {}
3.4 实例-InstanceReceiver
InstanceReceiver.class是一个ServiceConnection的实现,这里把接收到的Binder对象转为一个InstanceTransfer,也是封装的一个AIDL对象,这个对象的作用是把我们的单例传输过来。
代码:
@Override
public void onServiceConnected(ComponentName name, IBinder service){
Log.i(TAG, "[onServiceConnected]" + name);
try {
/** 调用这句会将单例(代理)实例传递过来了 */
InstanceTransfer.Stub.asInterface(service).transfer();
} catch (Exception e) {
Log.e(TAG, "[onServiceConnected][exception when transfer instance]" + name, e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
/** 意外断开绑定的情况,这里可以重写成发起重连 */
Log.e(TAG, "[onServiceDisconnected][exception when service disconnected]" + name);
}
InstanceTransfer的定义:
interface InstanceTransfer {
InstanceCarrier transfer();
}
3.5 InstanceCarrier
这里冒出了一个InstanceCarrier,这个InstanceCarrier实际上是我们定义的一个Parcelable类,这个类干的事情,是前面提到的:统一序列化(Stub)和反序列化(Proxy)单例对象。
代码大概是这样的:
private static final String TAG = "InstanceCarrier";
private static final int PROCESS_A = 1;
private static final int PROCESS_B = 2;
private static final int PROCESS_C = 3;
/**
* 在这里把单例转成IBinder传输到其他进程
* @param dest
* @param flags
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
if (ProcessUtils.isProcessA()) {
dest.writeInt(PROCESS_A);
dest.writeStrongInterface(SingletonAImp.getInstance());
Log.i(TAG, String.format(
"[write][PROCESS_A][processCode=%s]", PROCESS_A));
}else if (ProcessUtils.isProcessB()) {
dest.writeInt(PROCESS_B);
dest.writeStrongInterface(SingletonBImp.getInstance());
Log.i(TAG, String.format(
"[write][PROCESS_B][processCode=%s]", PROCESS_B));
}else if (ProcessUtils.isProcessC()) {
dest.writeInt(PROCESS_C);
dest.writeStrongInterface(SingletonCImp.getInstance());
Log.i(TAG, String.format(
"[write][PROCESS_C][processCode=%s]", PROCESS_C));
}
}
/**
* 在这里把跨进程传递过来的IBinder赋值给对应的实例
* @param in
*/
protected InstanceCarrier(Parcel in) {
int processCode = in.readInt();
switch (processCode) {
case PROCESS_A:
SingletonAImp.INSTANCE =
SingletonA.Stub.asInterface(in.readStrongBinder());
Log.i(TAG, String.format(
"[read][PROCESS_A][processCode=%s]", processCode));
break;
case PROCESS_B:
SingletonBImp.INSTANCE =
SingletonB.Stub.asInterface(in.readStrongBinder());
Log.i(TAG, String.format(
"[read][PROCESS_B][processCode=%s]", processCode));
break;
case PROCESS_C:
SingletonCImp.INSTANCE =
SingletonC.Stub.asInterface(in.readStrongBinder());
Log.i(TAG, String.format(
"[read][PROCESS_C][processCode=%s]", processCode));
break;
default:
Log.w(TAG, String.format(
"[unknown][processCode=%s]", processCode));
}
}
public InstanceCarrier() {}
@Override
public int describeContents() {
return 0;
}
public static final Creator<InstanceCarrier> CREATOR = new Creator<InstanceCarrier>() {
@Override
public InstanceCarrier createFromParcel(Parcel in) {
return new InstanceCarrier(in);
}
@Override
public InstanceCarrier[] newArray(int size) {
return new InstanceCarrier[size];
}
};
这么一套下来,整个实现机制搞好。
后续添加新的单例,只需要:
定义单例的AIDL
实现单例
在InstanceCarrier里添加序列化和反序列化的两行代码
如果添加了进程,需要在那个进程添加一个BaseService的派生类
如果是新增接口的话,也简单的修改下AIDL文件,然后实现新的接口。
三、存在的问题或不足
单例内使用到的数据类型,必须支持AIDL(Android IPC通讯的要求),对于简单的数据,可以使用系统的Bundle对象
实现的调用方法的时候,需要考虑到执行的线程可能不是调用的线程(跨进程调用的情况下是在Binder线程),因为调用是同步的,对返回结果没有影响,但对于需要在主线程执行的逻辑来说,需要主动异步放到主线程去。
线程安全:这个是编写单例的时候需要注意的问题,因为任何一个线程都能够访问到这个单例,使用这个方式支持跨进程可能会放大这个问题。
Android的IPC通讯机制本身的限制:Android的IPC通讯共享1M的内存,因此需要避免传输大量的数据,同时,处理逻辑也不宜很耗时(否则消费数据不及时,消费者处理能力低于生产者的生产力,迟早会耗光1M的内存)。、
AIDL不支持方法重载(弱弱的...)
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南