一.背景
最近学习了下Weex Android的渲染逻辑,这里做个总结,加深印象。
总体上说 Weex Android的渲染代码分为三个部分:
- bridge。 运行于 js bridge thread,主要负责与native层进行数据交互。
- dom。运行于 dom thread,主要负责解析渲染命令(JSON)生成控件,解析属性,生成控件树结构等。
- render。运行于 render thread (也就是UI thread),主要负责控件的渲染逻辑。
ok,话不多说,直接看源码分析。(源码基于Weex release 0.16版本)
二.源码分析
一般来说,Weex渲染的使用方式都是从
WXSDKInstance的Render方法开始,那么我们就从这里开始分析:
1.render方法最终都会调用到,renderInternal( String pageName,String template, Map<String, Object> options, String jsonInitData,WXRenderStrategy flag)
private void renderInternal(String pageName,
String template,
Map<String, Object> options,
String jsonInitData,
WXRenderStrategy flag){
if (mRendered || TextUtils.isEmpty(template)) { //如果渲染过,或者模板为空,直接返回。
return;
}
WXLogUtils.d("WXSDKInstance", "Start render page: " + pageName);
if (WXTracing.isAvailable()) { //性能采集相关,可以不关注。
WXTracing.TraceEvent traceEvent = WXTracing.newEvent("executeBundleJS", mInstanceId, -1);
traceEvent.traceId = mExecJSTraceId;
traceEvent.iid = mInstanceId;
traceEvent.tname = "JSThread";
traceEvent.ph = "B";
traceEvent.submit();
mRenderStartNanos = System.nanoTime();
}
ensureRenderArchor(); //确保RenderContainer存在,没有的话,会新建。
Map<String, Object> renderOptions = options;
if (renderOptions == null) {
renderOptions = new HashMap<>();
}
if (WXEnvironment.sDynamicMode && !TextUtils.isEmpty(WXEnvironment.sDynamicUrl) && renderOptions.get("dynamicMode") == null) {
renderOptions.put("dynamicMode", "true");
renderByUrl(pageName, WXEnvironment.sDynamicUrl, renderOptions, jsonInitData, flag);
return;
}
mWXPerformance.pageName = pageName;
mWXPerformance.JSTemplateSize = template.length() / 1024f;
mRenderStartTime = System.currentTimeMillis();
mRenderStrategy = flag;
WXSDKManager.getInstance().setCrashInfo(WXEnvironment.WEEX_CURRENT_KEY,pageName);
WXSDKManager.getInstance().createInstance(this, template, renderOptions, jsonInitData);
mRendered = true;
if (TextUtils.isEmpty(mBundleUrl)) {
mBundleUrl = pageName;
}
}
其中 ensureRenderArchor() 方法主要是确保RenderContainer存在,RenderContainer是Weex渲染后的View的Parent容器,继承于FrameLayout。
ensureRenderArchor方法如下:
private void ensureRenderArchor(){
if(mRenderContainer == null){
if (getContext() != null) {
mRenderContainer = new RenderContainer(getContext());
mRenderContainer.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mRenderContainer.setBackgroundColor(Color.TRANSPARENT);
mRenderContainer.setSDKInstance(this);
mRenderContainer.addOnLayoutChangeListener(this);
}
}
}
renderInternal 方法 最终会 调用到 WXSDKManager的createInstance方法:
2. WXSDKManager.createInstance()
void createInstance(WXSDKInstance instance, String code, Map<String, Object> options, String jsonInitData) {
mWXRenderManager.registerInstance(instance); //
mBridgeManager.createInstance(instance.getInstanceId(), code, options, jsonInitData);
if (mLifeCycleCallbacks != null) {
for (InstanceLifeCycleCallbacks callbacks : mLifeCycleCallbacks) { //Weex instance相关生命周期的回调。
callbacks.onInstanceCreated(instance.getInstanceId());
}
}
}
其中mWXRenderManager是渲染相关的管理器,它的registerInstance方法如下:
public void registerInstance(WXSDKInstance instance) {
mRegistries.put(instance.getInstanceId(), new RenderActionContextImpl(instance));
}
会生成一个RenderActionContextImpl,并且把它和WXSDKInstance绑定在一起。
之后调用了BridgeManager的createInstance方法,继续往下看。
3. BridgeManager.createInstance()
/**
* Create instance.
*/
public void createInstance(final String instanceId, final String template,
final Map<String, Object> options, final String data) {
final WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId); //实际上是从上述RenderActionContextImpl里面获取到的。
if (instance == null) {
WXLogUtils.e("WXBridgeManager", "createInstance failed, SDKInstance is not exist");
return;
}
if (TextUtils.isEmpty(instanceId)
|| TextUtils.isEmpty(template) || mJSHandler == null) {
instance.onRenderError(WXRenderErrorCode.WX_CREATE_INSTANCE_ERROR, "createInstance fail!"); //渲染出错回调
return;
}
if (!isJSFrameworkInit() && reInitCount == 1 && !WXEnvironment.sDebugServerConnectable) { //没有初始化成功,并且不是连接到Debug模式下
instance.onRenderError(WXRenderErrorCode.WX_CREATE_INSTANCE_ERROR, "createInstance fail!");
post(new Runnable() { // post 到 js thread
@Override
public void run() {
initFramework("");//初始化Framework,Framework实际上是main.js文件
}
}, instanceId);
return;
}
WXModuleManager.createDomModule(instance); //注册WXDomModule,提供部分DOM相关的方法实现。
post(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
invokeCreateInstance(instance, template, options, data);
final long totalTime = System.currentTimeMillis() - start;
WXSDKManager.getInstance().postOnUiThread(new Runnable() {
@Override
public void run() {
instance.createInstanceFinished(totalTime);
}
}, 0);
}
}, instanceId);
}
一般来说js framework会在Weex初始化的时候进行初始化,但是如果js framework没有初始化成功,那么会触发方法:initFramework方法,该方法如下:
private void initFramework(String framework) {
if (!isJSFrameworkInit()) { //没有初始化过,方法内部通过mInit变量判断。
if (TextUtils.isEmpty(framework)) {
// if (WXEnvironment.isApkDebugable()) {
WXLogUtils.d("weex JS framework from assets");
// }
framework = WXFileUtils.loadAsset("main.js", WXEnvironment.getApplication()); //asset目录下的main.js
}
if (TextUtils.isEmpty(framework)) {
mInit = false;
commitJSFrameworkAlarmMonitor(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_ERR_JS_FRAMEWORK, "JS Framework is empty!");
return;
}
try {
if (WXSDKManager.getInstance().getWXStatisticsListener() != null) {
WXSDKManager.getInstance().getWXStatisticsListener().onJsFrameworkStart();
}
long start = System.currentTimeMillis();
String crashFile = "";
try {
crashFile = WXEnvironment.getApplication().getApplicationContext().getCacheDir().getPath();
} catch (Exception e) {
e.printStackTrace();
}
boolean pieSupport = true;
try {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
pieSupport = false;
}
} catch (Exception e) {
e.prin