通过前面五篇文章的源码分析及总结,咱们对Weex的总体架构及核心源码都有了清晰的认识。本篇文章主要总结我在Weex SDK源码阅读时以为能够借鉴的细节。java
备注:本文侧重讲Weex SDK源码级别的可借鉴细节,对大方向上的可借鉴点好比动态化+Native思路、一项技术完整的生态等方面能够参考上一篇文章《深刻Weex系列(八)之Weex SDK架构分析》。android
在使用Weex以前咱们都会进行Weex SDK的初始化,对于Weex SDK它的辅助配置类就使用到了建造者模式。git
建造者模式主要解决:一个模块各个部分子对象的构建算法可能变化,可是各个部分子对象相互结合在一块儿的算法确实稳定的。一句话总结就是:模块总体构建过程稳定,可是构建的每一步可能有出入。github
咱们结合Weex的场景来具体分析下:Weex配置模块的构建过程是稳定的(都须要提供一样的能力),可是构建的每一步则可能有出入(每一个配置的能力提供却能够多样)。算法
举例说明:例如Weex须要提供网络请求的基础能力(这个构建过程稳定),可是网络请求能够有不一样的实现方式(具体的构建算法可能变化)。性能优化
InitConfig config = new InitConfig.Builder().
setImgAdapter(new WeexImageAdapter()).
setHttpAdapter(new WeexHttpAdapter).
setSoLoader(new WeexSoLoaderAdapter).
build();
复制代码
好处:调用者无需知道构建模块如何组装,也不会忘记组装某一部分,同时也提供给了开发者定制的能力。bash
So的成功加载对Weex的运行相当重要,毕竟Weex须要V8引擎执行Js与Native的交互,源码中也能够看出So没有加载成功则Weex的各个模块不会执行。微信
而在线上Bug收集中咱们会遇到UnsatisfiedLinkError错误,虽然不是频发性Bug,可是对于Weex而言一旦出现那么Weex就不可能再运行。因而Weex SDK对So加载这块作了优化,咱们看下So加载的代码逻辑:网络
public static boolean initSo(String libName, int version, IWXUserTrackAdapter utAdapter) {
String cpuType = _cpuType();
if (cpuType.equalsIgnoreCase(MIPS) ) {
return false; // mips架构不支持,直接返回
}
boolean InitSuc = false;
if (checkSoIsValid(libName, BuildConfig.ARMEABI_Size) ||checkSoIsValid(libName, BuildConfig.X86_Size)) { // 校验So大小是否正常
/**
* Load library with {@link System#loadLibrary(String)}
*/
try {
// If a library loader adapter exists, use this adapter to load library
// instead of System.loadLibrary.
if (mSoLoader != null) {
mSoLoader.doLoadLibrary(libName);// 自定义SoLoader加载的话本身去加载
} else {
System.loadLibrary(libName);// 默认加载的方式
}
commit(utAdapter, null, null);
InitSuc = true;
} catch (Exception | Error e2) {// So加载失败
if (cpuType.contains(ARMEABI) || cpuType.contains(X86)) {
commit(utAdapter, WXErrorCode.WX_ERR_LOAD_SO.getErrorCode(), WXErrorCode.WX_ERR_LOAD_SO.getErrorMsg() + ":" + e2.getMessage());
}
InitSuc = false;
}
try {
if (!InitSuc) {
// 没有加载成功的话则从文件中加载
//File extracted from apk already exists.
if (isExist(libName, version)) {
boolean res = _loadUnzipSo(libName, version, utAdapter);// 从解压包中加载So
if (res) {
return res;
} else {
//Delete the corrupt so library, and extract it again.
removeSoIfExit(libName, version);// 解压包也加载失败,删除;
}
}
//Fail for loading file from libs, extract so library from so and load it.
if (cpuType.equalsIgnoreCase(MIPS)) {
return false;
} else {
try {
InitSuc = unZipSelectedFiles(libName, version, utAdapter);// 从apk中解压出来So,而后加载;
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
} catch (Exception | Error e) {
InitSuc = false;
e.printStackTrace();
}
}
return InitSuc;
}
复制代码
能够看到Weex中有多项保障去保证So的成功加载,总结下流程图:多线程
各位老司机都知道多线程的好处也知道Android只有主线程才能更新UI,对于Weex来讲它有本身完整的一套工做机制,若是全部任务都在主线程那势必会积压太多任务,致使任务得不到及时执行同时也有卡顿的风险。
Weex SDK也考虑到了这些,分析Weex的机制能够知道任务主要花费在三方面:JSBridge相关、Dom相关、UI相关。因而对这三方面进行了细分,JSBridge相关的操做挪到JSBridge线程执行,Dom相关操做在Dom线程执行,避免了主线程积压太多任务。此处咱们能够想到使用异步线程。同时对于单项的任务例如Dom操做,须要是串行的。若是使用线程池,实际上也发挥不出线程池的威力。
分析到了这里。咱们的需求其实就很明确了:避免异步线程的建立及销毁过程消耗资源,同时支持串行执行。咱们能够设想一种线程能力:有任务的时候则执行,没有任务的时候则等待,是否是完美的符合咱们的需求。
幸运的是Android其实已经为咱们提供了这样的一个类:HandlerThread。你们能够参考我以前的一篇文章《Android性能优化(十一)之正确的异步姿式》。
// 贴出Weex中使用的HandlerThread实例
// JSBridge工做的Thread
mJSThread = new WXThread("WeexJSBridgeThread", this);
mJSHandler = mJSThread.getHandler();
// Dom工做的Thread
mDomThread = new WXThread("WeeXDomThread", new WXDomHandler(this));
mDomHandler = mDomThread.getHandler();
复制代码
总结下Weex的线程模型:
优点:
对于Weex的RunTime,再怎么强大也少不了与Native的交互(方法调用,使用Native的能力),前面的系列文章也详细分析了Module的交互原理。可是有一个细节问题前面没有说到,就是JS与Native交互的方法签名,参数类型只能是String吗?
回到WXBridge这个通讯的桥梁,调用Native的方法都会走到callNative方法,而后走到WxBridgeManager.callNative方法,会发现函数体内有一行:
JSONArray array = JSON.parseArray(tasks);
复制代码
由此能够判定JS传递给Native的参数首先不只仅是普通String字符串,而是Json格式。实际上不论是断点查看或者翻阅WXStreamModule的代码,均可以发现Json的踪迹。
@JSMethod(uiThread = false)
public void fetch(String optionsStr, final JSCallback callback, JSCallback progressCallback){
JSONObject optionsObj = null;
try {
optionsObj = JSON.parseObject(optionsStr);
}catch (JSONException e){
WXLogUtils.e("", e);
}
......
}
复制代码
不过以上发现还不足以解决咱们的疑惑:参数类型只能是String吗?那必须不是!
首先回顾下在Module的注册过程当中会有一步是获取Module中被打上注解的方法而后存在mMethodMap中;而在真正调用方法的地方是NativeInvokeHelper的invoke方法:
public Object invoke(final Object target,final Invoker invoker,JSONArray args) throws Exception {
final Object[] params = prepareArguments(invoker.getParameterTypes(),args);// 解析参数
if (invoker.isRunOnUIThread()) {// 要求在主线程执行则抛到主线程执行;
WXSDKManager.getInstance().postOnUiThread(new Runnable() {
@Override
public void run() {
try {
invoker.invoke(target, params);// 反射调用方法执行
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}, 0);
} else {
return invoker.invoke(target, params);
}
return null;
}
复制代码
咱们再来详细跟踪下解析参数这步:
private Object[] prepareArguments(Type[] paramClazzs, JSONArray args) throws Exception {
Object[] params = new Object[paramClazzs.length];
Object value;
Type paramClazz;
for (int i = 0; i < paramClazzs.length; i++) {
paramClazz = paramClazzs[i];
if(i>=args.size()){
if(!paramClazz.getClass().isPrimitive()) {
params[i] = null;
continue;
}else {
throw new Exception("[prepareArguments] method argument list not match.");
}
}
value = args.get(i);
// JSONObject与JSCallback类型单独处理
if (paramClazz == JSONObject.class) {
params[i] = value;
} else if(JSCallback.class == paramClazz){
if(value instanceof String){
params[i] = new SimpleJSCallback(mInstanceId,(String)value);
}else{
throw new Exception("Parameter type not match.");
}
} else {
// 其它类型的参数
params[i] = WXReflectionUtils.parseArgument(paramClazz,value);
}
}
return params;
}
复制代码
看下其它参数类型的解析:
public static Object parseArgument(Type paramClazz, Object value) {
if (paramClazz == String.class) {
return value instanceof String ? value : JSON.toJSONString(value);
} else if (paramClazz == int.class) {
return value.getClass().isAssignableFrom(int.class) ? value : WXUtils.getInt(value);
} else if (paramClazz == long.class) {
return value.getClass().isAssignableFrom(long.class) ? value : WXUtils.getLong(value);
} else if (paramClazz == double.class) {
return value.getClass().isAssignableFrom(double.class) ? value : WXUtils.getDouble(value);
} else if (paramClazz == float.class) {
return value.getClass().isAssignableFrom(float.class) ? value : WXUtils.getFloat(value);
} else {
return JSON.parseObject(value instanceof String ? (String) value : JSON.toJSONString(value), paramClazz);
}
}
复制代码
跟踪到此处就显而易见:JS与Native的交互参数不只仅支持String。
咱们再来总结下Weex是如何实现不一样方法签名的交互的:
本文主要记录了我在Weex源码阅读过程当中以为不错能够借鉴的细节,限于文章篇幅不能面面俱到。实际上不只Weex的总体思路,Weex SDK的代码也很是优秀,很是建议你们仔细阅读,学习优秀的源码对本身的编码能力会有必定程度的提高!
欢迎持续关注Weex源码分析项目:Weex-Analysis-Project
欢迎关注微信公众号:按期分享Java、Android干货!