一、CC 的基本使用方式
1、在模块A中定义Component
/**
* @author billy.qi
*/
public class ComponentA implements IComponent {
@Override
public String getName() {
//组件的名称,调用此组件的方式:
// CC.obtainBuilder("ComponentA")...build().callAsync()
return "demo.ComponentA";
}
/**
* 组件被调用时的入口
* 要确保每个逻辑分支都会调用到CC.sendCCResult,
* 包括try-catch,if-else,switch-case-default,startActivity
* @param cc 组件调用对象,可从此对象中获取相关信息
* @return true:将异步调用CC.sendCCResult(...),用于异步实现相关功能,例如:文件加载、网络请求等
* false:会同步调用CC.sendCCResult(...),即在onCall方法return之前调用,否则将被视为不合法的实现
*/
@Override
public boolean onCall(CC cc) {
String actionName = cc.getActionName();
switch (actionName) {
case "showActivityA":
openActivity(cc);
break;
case "getLifecycleFragment":
//demo for provide fragment object to other component
getLifecycleFragment(cc);
break;
case "lifecycleFragment.addText":
lifecycleFragmentDoubleText(cc);
break;
case "getInfo":
getInfo(cc);
break;
default:
//这个逻辑分支上没有调用CC.sendCCResult(...),是一种错误的示例
//并且方法的返回值为false,代表不会异步调用CC.sendCCResult(...)
//在LocalCCInterceptor中将会返回错误码为-10的CCResult
break;
}
return false;
}
private void lifecycleFragmentDoubleText(CC cc) {
LifecycleFragment lifecycleFragment = cc.getParamItem("fragment");
if (lifecycleFragment != null) {
String text = cc.getParamItem("text", "");
lifecycleFragment.addText(text);
CC.sendCCResult(cc.getCallId(), CCResult.success());
} else {
CC.sendCCResult(cc.getCallId(), CCResult.error("no fragment params"));
}
}
private void getLifecycleFragment(CC cc) {
CC.sendCCResult(cc.getCallId(), CCResult.successWithNoKey(new LifecycleFragment()));
}
private void getInfo(CC cc) {
String userName = "billy";
CC.sendCCResult(cc.getCallId(), CCResult.success("userName", userName));
}
private void openActivity(CC cc) {
CCUtil.navigateTo(cc, ActivityA.class);
CC.sendCCResult(cc.getCallId(), CCResult.success());
}
}
2、在主工程中调用A模块中的Component
public static final String COMPONENT_NAME_A = "demo.ComponentA";
IComponentCallback printResultCallback = new IComponentCallback() {
@Override
public void onResult(CC cc, CCResult result) {
showResult(cc, result);
}
};
CC.obtainBuilder(COMPONENT_NAME_A)
.setActionName("showActivityA")
.build().callAsyncCallbackOnMainThread(printResultCallback);
二、CC的调用流程
1、CC 是一次组件接口调用的载体类(data)
承载接口调用的各种参数,调用结果。
也可以理解为是一个装饰器类。
public class CC {
private static final String TAG = "ComponentCaller";
private static final String VERBOSE_TAG = "ComponentCaller_VERBOSE";
public static final String CC_NULL_KEY = "CC_NULL_KEY";
/**
* 默认超时时间为2秒
*/
private static final long DEFAULT_TIMEOUT = 2000;
static boolean DEBUG = false;
static boolean VERBOSE_LOG = false;
/**
* 是否响应跨app的组件调用
* 为了方便开发调试,默认设置为允许响应跨app组件调用
* 为了安全,app上线时可以将此值设置为false,避免被恶意调用
*/
private static boolean REMOTE_CC_ENABLED = false;
private volatile CCResult result;
private final byte[] wait4resultLock = new byte[0];
private static Application application;
WeakReference<Activity> cancelOnDestroyActivity;
WeakReference<Fragment> cancelOnDestroyFragment;
private volatile boolean waiting;
static {
Application app = CCUtil.initApplication();
if (app != null) {
init(app);
}
}
/**
* 初始化方法
* 仅初始化CC,不初始化组件和拦截器
* 在Application.onCreate(...)中调用
* @param app Application
*/
public static synchronized void init(Application app) {
init(app, false, false);
}
/**
* 初始化方法
* 同时初始化组件和全局拦截器
* @param app Application
* @param initComponents 如果设置为true则同时初始化组件
* @param initGlobalInterceptors 如果设置为true则同时初始化全局拦截器
*/
public static synchronized void init(Application app, boolean initComponents, boolean initGlobalInterceptors) {
if (application == null && app != null) {
application = app;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
application.registerActivityLifecycleCallbacks(new CCMonitor.ActivityMonitor());
}
}
if (initComponents) {
ComponentManager.init();
}
if (initGlobalInterceptors) {
GlobalCCInterceptorManager.init();
}
}
private WeakReference<Context> context;
/**
* 组件名称
*/
private String componentName;
/**
* 组件中某个功能的名称,用以区别同一个组件中不同功能的调用
*/
private String actionName;
private final Map<String, Object> params = new HashMap<>();
/**
* 回调对象
*/
private IComponentCallback callback;
/**
* 是否异步执行
*/
private boolean async;
private final List<ICCInterceptor> interceptors = new ArrayList<>();
private boolean callbackOnMainThread;
/**
* 调用超时时间,默认值(同步调用:2000, 异步调用:0)
*/
private long timeout = -1;
long timeoutAt;
private final AtomicBoolean finished = new AtomicBoolean(false);
private String callId;
private volatile boolean canceled = false;
private volatile boolean timeoutStatus = false;
private boolean withoutGlobalInterceptor = false;
private CC(String componentName) {
this.componentName = componentName;
}
/**
* 创建CC对象的Builder<br>
* <b>此对象会被CC框架复用,请勿在程序中保存</b>
* @param componentName 要调用的组件名称
* @return 创建CC对象的Builder
*/
public static Builder obtainBuilder(String componentName) {
return BUILDER_POOL.get(componentName);
}
/**
* 获取当前app的Application对象
* @return application对象
*/
public static Application getApplication() {
return application;
}
@Override
public String toString() {
JSONObject json = new JSONObject();
put(json, "callId", callId);
put(json, "context", getContext());
put(json, "componentName", componentName);
put(json, "actionName", actionName);
put(json, "timeout", timeout);
put(json, "withoutGlobalInterceptor", withoutGlobalInterceptor);
put(json, "callbackOnMainThread", callbackOnMainThread);
put(json, "params", CCUtil.convertToJson(params));
put(json, "interceptors", interceptors);
put(json, "callback", getCallback());
return json.toString();
}
public Context getContext() {
if (context != null) {
Context context = this.context.get();
if (context != null) {
return context;
}
}
return application;
}
void forwardTo(String componentName) {
this.componentName = componentName;
}
public String getActionName() {
return actionName;
}
/**
* get all params
* @return all params as map
*/
public Map<String, Object> getParams() {
return params;
}
/**
* 获取通过 {@link Builder#setParamWithNoKey(Object)} 设置的参数
* @param defaultValue 默认值
* @param <T> 泛型,返回值的类型
* @return 未设置key(使用默认key)的参数
*/
public <T> T getParamItemWithNoKey(T defaultValue) {
return getParamItem(CC_NULL_KEY, defaultValue);
}
/**
* 获取通过 {@link Builder#setParamWithNoKey(Object)} 设置的参数
* @param <T> 泛型,返回值的类型
* @return 未设置key(使用默认key)的参数
*/
public <T> T getParamItemWithNoKey() {
return getParamItem(CC_NULL_KEY);
}
/**
* get param(auto class casted) by key
* @param key key for param
* @param defaultValue default value if not found or class cast error
* @param <T> class to cast for param
* @return class casted param
*/
public <T> T getParamItem(String key, T defaultValue) {
T item = getParamItem(key);
if (item == null) {
return defaultValue;
}
return item;
}
/**
* get param(auto class casted) by key
* @param key key for param
* @param <T> class to cast for param
* @return class casted param
*/
public <T> T getParamItem(String key) {
try {
return (T) params.get(key);
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
boolean isAsync() {
return async;
}
boolean isCallbackOnMainThread() {
return callbackOnMainThread;
}
long getTimeout() {
return timeout;
}
public String getCallId() {
return callId;
}
boolean isCanceled() {
return canceled;
}
/**
* 判断是否需要中止运行,本次调用被手动取消或已超时。
* 组件在处理耗时操作时,要根据此状态进行判断,以免进行无效操作
* @return <code>true</code>:需要中止继续执行;false:可以继续运行
*/
public boolean isStopped() {
return canceled || timeoutStatus;
}
boolean isTimeout() {
return timeoutStatus;
}
boolean isWithoutGlobalInterceptor() {
return withoutGlobalInterceptor;
}
CCResult getResult() {
return result;
}
void setResult(CCResult result) {
finished.set(true);
this.result = result;
}
void setResult4Waiting(CCResult result) {
try {
synchronized (wait4resultLock) {
if (VERBOSE_LOG) {
verboseLog(callId, "setResult" + (waiting ? "4Waiting" : "")
+ ". CCResult:" + result);
}
setResult(result);
if (waiting) {
waiting = false;
wait4resultLock.notifyAll();
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
void wait4Result() {
//等待调用CC.sendCCResult(callId, result)
synchronized (wait4resultLock) {
if (!isFinished()) {
try {
verboseLog(callId, "start waiting for CC.sendCCResult(...)");
waiting = true;
wait4resultLock.wait();
verboseLog(callId, "end waiting for CC.sendCCResult(...)");
} catch (InterruptedException ignored) {
}
}
}
}
IComponentCallback getCallback() {
return callback;
}
/**
* 在onDestroy后,自动cancel
*/
void cancelOnDestroy(Object reason) {
if (!isFinished()) {
if (VERBOSE_LOG) {
verboseLog(callId, "call cancel on " + reason + " destroyed");
}
cancel();
}
}
void addCancelOnFragmentDestroyIfSet() {
if (cancelOnDestroyFragment == null) {
return;
}
Fragment fragment = cancelOnDestroyFragment.get();
if (fragment == null) {
return;
}
FragmentManager manager = fragment.getFragmentManager();
if (manager != null) {
manager.registerFragmentLifecycleCallbacks(
new CCMonitor.FragmentMonitor(this)
, false);
}
}
public String getComponentName() {
return componentName;
}
List<ICCInterceptor> getInterceptors() {
return interceptors;
}
/**
* 异步调用,且不需要回调
* @return callId,可用于取消调用的任务
*/
public String callAsync() {
return callAsync(null);
}
/**
* 异步调用,在异步线程执行回调
* @param callback 回调函数
* @return callId 用于取消
*/
public String callAsync(IComponentCallback callback) {
this.callbackOnMainThread = false;
return processCallAsync(callback);
}
/**
* 异步调用,在主线程执行回调
* @param callback 回调函数
* @return callId 用于取消
*/
public String callAsyncCallbackOnMainThread(IComponentCallback callback) {
this.callbackOnMainThread = true;
return processCallAsync(callback);
}
private String processCallAsync(IComponentCallback callback) {
if (callback != null) {
this.callback = callback;
}
this.async = true;
//调用方未设置超时时间,默认为无超时时间
if (timeout < 0) {
timeout = 0;
}
setTimeoutAt();
this.callId = nextCallId();
this.canceled = false;
this.timeoutStatus = false;
if (VERBOSE_LOG) {
verboseLog(callId, "start to callAsync:" + this);
}
ComponentManager.call(this);
return callId;
}
/**
* 同步调用
* @return CCResult
*/
public CCResult call() {
this.callback = null;
this.async = false;
boolean mainThreadCallWithNoTimeout = timeout == 0 && Looper.getMainLooper() == Looper.myLooper();
//主线程下的同步调用必须设置超时时间,默认为2秒
if (mainThreadCallWithNoTimeout || timeout < 0) {
timeout = DEFAULT_TIMEOUT;
}
setTimeoutAt();
this.callId = nextCallId();
this.canceled = false;
this.timeoutStatus = false;
//加上开关判断,防止开关关闭的情况下也执行this.toString()方法
if (VERBOSE_LOG) {
verboseLog(callId, "start to call:" + this);
}
return ComponentManager.call(this);
}
/**
* 在任意位置回调结果
* 组件的onCall方法被调用后,<b>必须确保所有分支均会调用</b>到此方法将组件调用结果回调给调用方
* @param callId 回调对象的调用id
* @param ccResult 回调的结果
*/
public static void sendCCResult(String callId, CCResult ccResult) {
if (VERBOSE_LOG) {
verboseLog(callId, "CCResult received by CC.sendCCResult(...).CCResult:" + ccResult);
}
CC cc = CCMonitor.getById(callId);
if (cc != null) {
if (cc.markFinished()) {
if (ccResult == null) {
ccResult = CCResult.defaultNullResult();
logError("CC.sendCCResult called, But ccResult is null, set it to CCResult.defaultNullResult(). "
+ "ComponentName=" + cc.getComponentName());
}
cc.setResult4Waiting(ccResult);
} else {
logError("CC.sendCCResult called, But ccResult is null. "
+ "ComponentName=" + cc.getComponentName());
}
} else {
log("CCResult received, but cannot found callId:" + callId);
}
}
/**
* 在任意位置回调结果
* @param callId 回调对象的调用id
* @param result 回调的结果
* @deprecated use {@link #sendCCResult(String, CCResult)}
*/
@Deprecated
public static void invokeCallback(String callId, CCResult result) {
sendCCResult(callId, result);
}
/**
* 获取当前app内是否含有指定的组件
* @param componentName 组件名称
* @return true:有, false:没有
*/
public static boolean hasComponent(String componentName) {
return ComponentManager.hasComponent(componentName);
}
/**
* 开关跨app调用组件支持,默认为关闭状态
* 1. 某个componentName当前app中不存在时,是否尝试调用其它app的此组件
* 2. 接收到跨app调用时,是否执行本次调用
* 3. 建议仅在开发阶段调试时打开,正式发布时关闭
* @param enable 开关(true:会执行; false:不会)
*/
public static void enableRemoteCC(boolean enable) {
REMOTE_CC_ENABLED = enable;
if (enable && application != null) {
RemoteCCInterceptor.getInstance().enableRemoteCC();
}
}
public static boolean isRemoteCCEnabled() {
return REMOTE_CC_ENABLED;
}
/**
* 当前进程是否以包名在运行(当前进程是否为主进程)
*/
public static boolean isMainProcess(){
return CCUtil.isMainProcess();
}
public static boolean isDebugMode() {
return DEBUG;
}
}
其核心方法 call(),其实是代理到了
ComponentManager.call(this)中。
/**
* 同步调用
* @return CCResult
*/
public CCResult call() {
this.callback = null;
this.async = false;
boolean mainThreadCallWithNoTimeout = timeout == 0 && Looper.getMainLooper() == Looper.myLooper();
//主线程下的同步调用必须设置超时时间,默认为2秒
if (mainThreadCallWithNoTimeout || timeout < 0) {
timeout = DEFAULT_TIMEOUT;
}
setTimeoutAt();
this.callId = nextCallId();
this.canceled = false;
this.timeoutStatus = false;
//加上开关判断,防止开关关闭的情况下也执行this.toString()方法
if (VERBOSE_LOG) {
verboseLog(callId, "start to call:" + this);
}
return ComponentManager.call(this);
}
2、ComponentManager 类是CC 中的总线类,或者说仓库类。维护着CC 中的路由信息
<组件名字符串,Component组件对象>
private static final ConcurrentHashMap<String, IComponent> COMPONENTS = new ConcurrentHashMap<>();
组件映射关系的收集是通过gradle插件 cc-register 来完成的。
https://github.com/luckybilly/AutoRegister
scanInterface : (必须)字符串,接口名(完整类名),所有直接实现此接口的类将会被收集
codeInsertToClassName : (必须)字符串,类名(完整类名),通过编译时生成代码的方式将收集到的类注册到此类的codeInsertToMethodName方法中
codeInsertToMethodName: 字符串,方法名,注册代码将插入到此方法中。若未指定,则默认为static块,(方法名为:)
registerMethodName : (必须)字符串,方法名,静态方法,方法的参数为 scanInterface
scanSuperClasses : 字符串或字符串数组,类名(完整类名),所有直接继承此类的子类将会被收集
include : 数组,需要扫描的类(正则表达式,包分隔符用/代替,如: com/billy/android/.*),默认为所有的类
exclude : 数组,不需要扫描的类(正则表达式,包分隔符用/代替,如: com/billy/android/.*),
自动收集原理是:
- gradle插件包含了一个叫Transform的API,这个API允许第三方插件在class文件转为为dex文件前操作编译好的class文件。
- 在Tansform中 收集相关类,编译期间修改字节码,加入到管理类中。
/**
* 组件调用管理类
* @author billy.qi
* @since 17/6/28 20:14
*/
class ComponentManager {
/** 当前进程中的组件集合 */
private static final ConcurrentHashMap<String, IComponent> COMPONENTS = new ConcurrentHashMap<>();
/**
* 组件名称对应的进程名称集合
* 当前进程为主进程:包含当前app内的所有静态组件和动态组件的(名称 - 进程名)的映射表
* 当前进程为子进程:包含当前app内的所有静态组件和当前进程内注册的动态组件的(名称 - 进程名)的映射表
*/
private static final ConcurrentHashMap<String, String> COMPONENT_PROCESS_NAMES = new ConcurrentHashMap<>();
private static final String SUB_PROCESS_SEPARATOR = ":";
static {
registerComponent(new DynamicComponentOption());
//加载类时自动调用初始化:注册所有组件
//通过auto-register插件生成组件注册代码
//生成的代码如下:
//registerComponent(new ComponentA());
//registerComponent(new ComponentAA());
}
/**
* 提前初始化所有全局拦截器
*/
static void init(){
//调用此方法时,虚拟机会加载ComponentManager类
//会自动执行static块中的组件自动注册,调用组件类的无参构造方法
//如果不提前调用此方法,static块中的代码将在第一次进行组件调用时(cc.callXxx())执行
}
}
核心调用方法call(),交给了一个拦截器调用链。
/**
* 组件调用统一入口
* @param cc 组件调用指令
* @return 组件调用结果(同步调用的返回值)
*/
static CCResult call(CC cc) {
String callId = cc.getCallId();
Chain chain = new Chain(cc);
if (!cc.isWithoutGlobalInterceptor()) {
chain.addInterceptors(INTERCEPTORS);
}
chain.addInterceptors(cc.getInterceptors());
// 有效性校验放在自定义拦截器之后执行,优先执行自定义拦截器,让其可以拦截到所有组件调用
// 执行实际调用的拦截器在校验有效性结束后再添加
chain.addInterceptor(ValidateInterceptor.getInstance());
ChainProcessor processor = new ChainProcessor(chain);
//异步调用,放到线程池中运行
if (cc.isAsync()) {
if (CC.VERBOSE_LOG) {
CC.verboseLog(callId, "put into thread pool");
}
CC_THREAD_POOL.submit(processor);
//异步调用时此方法返回null,CCResult通过callback回调
return null;
} else {
//同步调用,直接执行
CCResult ccResult;
try {
ccResult = processor.call();
} catch (Exception e) {
ccResult = CCResult.defaultExceptionResult(e);
}
if (CC.VERBOSE_LOG) {
CC.verboseLog(callId, "cc finished.CCResult:" + ccResult);
}
//同步调用的返回结果,永不为null,默认为CCResult.defaultNullResult()
return ccResult;
}
}
Chain 实现了一个拦截器调用链:
/**
* 组件调用链,用于管理拦截器的运行顺序
* @author billy.qi
*/
public class Chain {
private final List<ICCInterceptor> interceptors = new ArrayList<>();
private final CC cc;
private int index;
Chain(CC cc) {
this.cc = cc;
this.index = 0;
}
void addInterceptors(Collection<? extends ICCInterceptor> interceptors) {
if (interceptors != null && !interceptors.isEmpty()) {
this.interceptors.addAll(interceptors);
}
}
void addInterceptor(ICCInterceptor interceptor) {
if (interceptor != null) {
this.interceptors.add(interceptor);
}
}
public CCResult proceed() {
if (index >= interceptors.size()) {
return CCResult.defaultNullResult();
}
ICCInterceptor interceptor = interceptors.get(index++);
//处理异常情况:如果为拦截器为null,则执行下一个
if (interceptor == null) {
return proceed();
}
String name = interceptor.getClass().getName();
String callId = cc.getCallId();
CCResult result;
if (cc.isFinished()) {
//timeout, cancel, CC.sendCCResult(callId, ccResult), cc.setResult, etc...
result = cc.getResult();
} else {
if (CC.VERBOSE_LOG) {
CC.verboseLog(callId, "start interceptor:" + name + ", cc:" + cc);
}
try {
result = interceptor.intercept(this);
} catch(Throwable e) {
//防止拦截器抛出异常
result = CCResult.defaultExceptionResult(e);
}
if (CC.VERBOSE_LOG) {
CC.verboseLog(callId, "end interceptor:" + name + ".CCResult:" + result);
}
}
//拦截器理论上不应该返回null,但为了防止意外(自定义拦截器返回null,此处保持CCResult不为null
//消灭NPE
if (result == null) {
result = CCResult.defaultNullResult();
}
cc.setResult(result);
return result;
}
public CC getCC() {
return cc;
}
}
image.png
三、CC 库有点可以值得我们仔细研究和学习。
- gradle编程 Transform +ASM 动态修改字节码的技术。自动生成类,或者自动修改类没完成插桩操作。
- 拦截器链(责任链)的应用
- 通过ContentProvider来实现远程调用。值得研究一下原理。
网友评论