前言
首先感谢阿里巴巴团队对移动端跨三端与动态化做出的杰出贡献,阿里在6月初,彻底开放了weex android与ios端内测,最近已使用一周有余,效果果然达到了write once,run everywhere!而且运行效果可以与原声媲美,并且virtual dom的加入更加优化了性能问题,切对native扩展等提供了很好的预留,目前可以采用组件形式与全页形式进行集成,接下来我给大家介绍下weex在android端的工作原理及代码分析。
Weex SDK目录结构
源码分析
初始化操作
BaseApplication 初始化SDK 引擎
WXSDKEngine.init(this, null, null, new ImageAdapter(), null);
@Deprecated
public static void init(Application application, IWXUserTrackAdapter utAdapter, String framework) {
synchronized (mLock) {
if (init) {
return;
}
init = true;
WXEnvironment.sApplication = application;
WXEnvironment.JsFrameworkInit = false;
WXSoInstallMgrSdk.init(application);
WXEnvironment.sSupport = WXSoInstallMgrSdk.initSo(V8_SO_NAME, 1, utAdapter);
if (!WXEnvironment.sSupport) {
return;
}
WXSDKManager.getInstance().initScriptsFramework(framework);
register();
new AsyncTask<Application, Void, Void>() {
@Override
protected Void doInBackground(Application... params) {
try {
Class cls = Class.forName("com.taobao.weex.WXPrettyFish");
Method m = cls.getMethod("init", new Class[]{Application.class});
m.invoke(cls, new Object[]{params[0]});
} catch (Exception e) {
WXLogUtils.d("WXPrettyFish not found!");
}
return null;
}
}.execute(application);
}
}
WXSDKEngine初始化了三件事:
1、初始化so库文件,渲染逻辑、脚本业务框架等都封装在了这里;
2、初始化initScriptsFramework : 初始化脚本框架;
private void invokeInitFramework(Message msg) {
String framework = "";
if (msg.obj != null) {
framework = (String) msg.obj;
}
if (!mInit) {
if (TextUtils.isEmpty(framework)) {
if (WXEnvironment.isApkDebugable()) {
WXLogUtils.d("framework from assets");
}
framework = WXFileUtils.loadFileContent("main.js", WXEnvironment.getApplication());
}
if (TextUtils.isEmpty(framework)) {
mInit = false;
return;
}
try {
long start = System.currentTimeMillis();
if(mWXBridge.initFramework(framework, assembleDefaultOptions())==INIT_FRAMEWORK_OK){
WXEnvironment.sJSLibInitTime = System.currentTimeMillis() - start;
WXLogUtils.renderPerformanceLog("initFramework", WXEnvironment.sJSLibInitTime);
mInit = true;
execRegisterFailTask();
WXEnvironment.JsFrameworkInit = true;
}else{
WXLogUtils.e("[WXBridgeManager] invokeInitFramework ExecuteJavaScript fail");
}
} catch (Throwable e) {
WXLogUtils.e("[WXBridgeManager] invokeInitFramework " + e.getCause());
}
}
}
3、register 操作,初始化weex组件与module;
IndexActivity
private void renderWX() {
Rect outRect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect);
if (instance != null) {
instance.destroy();
instance = null;
}
instance = new WXSDKInstance(this);
instance.registerRenderListener(this);
Map<String, Object> options = new HashMap<>();
options.put("bundleUrl", WEEX_INDEX_URL);
instance.renderByUrl(TAG, WEEX_INDEX_URL, options, null, ScreenUtil.getDisplayWidth(this),
ScreenUtil.getDisplayHeight(this), WXRenderStrategy.APPEND_ASYNC);
}
首先看这个方法,可以在activity oncreate中被调用,可以清楚的看到,注册了渲染监听器,以及传入了当前屏幕的宽高及url,可以猜测weex的适配方案采用的是百分比方案。
WXSDKInstance renderByUrl
public void renderByUrl(final String pageName, final String url, Map<String, Object> options, final String jsonInitData, final int width, final int height, final WXRenderStrategy flag) {
if (options == null) {
options = new HashMap<>();
}
if (!options.containsKey("bundleUrl")) {
options.put("bundleUrl", url);
}
Uri uri=Uri.parse(url);
if(uri!=null && TextUtils.equals(uri.getScheme(),"file")){
render(pageName, WXFileUtils.loadFileContent(assembleFilePath(uri), mContext),options,jsonInitData,width,height,flag);
return;
}
IWXHttpAdapter adapter=WXSDKManager.getInstance().getIWXHttpAdapter();
if (adapter == null) {
adapter = new DefaultWXHttpAdapter();
}
WXRequest wxRequest = new WXRequest();
wxRequest.url = url;
if (wxRequest.paramMap == null) {
wxRequest.paramMap = new HashMap<String, Object>();
}
wxRequest.paramMap.put("user-agent", WXHttpUtil.assembleUserAgent());
adapter.sendRequest(wxRequest, new WXHttpListener(pageName, options, jsonInitData, width, height, flag, System.currentTimeMillis()));
mWXHttpAdapter = adapter;
}
方法内调用了httpadapter进行加载url,并且拼装了user-agent header参数;
DefaultWXHttpAdapter 网络处理
public class DefaultWXHttpAdapter implements IWXHttpAdapter {
@Override
public void sendRequest(final WXRequest request, final OnHttpListener listener) {
if (listener != null) {
listener.onHttpStart();
}
WXHttpManager.getInstance().execute(new Runnable() {
@Override
public void run() {
WXResponse response = new WXResponse();
try {
HttpURLConnection connection = openConnection(request, listener);
int responseCode = connection.getResponseCode();
response.statusCode = String.valueOf(responseCode);
if (responseCode == 200 || responseCode == 202) {
response.originalData = readInputStream(connection.getInputStream(), listener).getBytes();
} else {
response.errorMsg = readInputStream(connection.getErrorStream(), listener);
}
if (listener != null) {
listener.onHttpFinish(response);
}
} catch (IOException e) {
e.printStackTrace();
response.errorCode="-1";
response.errorMsg=e.getMessage();
if(listener!=null){
listener.onHttpFinish(response);
}
}
}
});
}
/**
* Opens an {@link HttpURLConnection} with parameters.
*
* @param request
* @param listener
* @return an open connection
* @throws IOException
*/
private HttpURLConnection openConnection(WXRequest request, OnHttpListener listener) throws IOException {
URL url = new URL(request.url);
HttpURLConnection connection = createConnection(url);
connection.setConnectTimeout(request.timeoutMs);
connection.setReadTimeout(request.timeoutMs);
connection.setUseCaches(false);
connection.setDoInput(true);
if (!TextUtils.isEmpty(request.method)) {
connection.setRequestMethod(request.method);
}
if (request.paramMap != null) {
Set<String> keySets = request.paramMap.keySet();
for (String key : keySets) {
connection.addRequestProperty(key, request.paramMap.get(key).toString());
}
}
if (request.body != null) {
if (listener != null) {
listener.onHttpUploadProgress(0);
}
connection.setDoOutput(true);
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(request.body.getBytes());
out.close();
if (listener != null) {
listener.onHttpUploadProgress(100);
}
}
return connection;
}
private String readInputStream(InputStream inputStream, OnHttpListener listener) {
StringBuilder builder = new StringBuilder();
try {
int fileLen = inputStream.available();
BufferedReader localBufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] data = new char[2048];
int len = -1;
while ((len = localBufferedReader.read(data)) > 0) {
builder.append(data, 0, len);
if (listener != null && fileLen > 0) {
listener.onHttpResponseProgress((builder.length() / fileLen) * 100);
}
}
localBufferedReader.close();
try {
inputStream.close();
} catch (IOException e) {
WXLogUtils.e("DefaultWXHttpAdapter: " + WXLogUtils.getStackTrace(e));
}
} catch (IOException e) {
e.printStackTrace();
}
return builder.toString();
}
/**
* Create an {@link HttpURLConnection} for the specified {@code url}.
*/
protected HttpURLConnection createConnection(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
}
其实就是一个封装的简单的网络请求工具类,把请求下来的json数据回调给WXSDKInstance,不用多说什么~
WXSDKInstance render
public void render(String pageName, String template, Map<String, Object> options, String jsonInitData, int width, int height, WXRenderStrategy flag) {
if (mRendered || TextUtils.isEmpty(template)) {
return;
}
if(options==null){
options=new HashMap<>();
}
if(WXEnvironment.sDynamicMode && !TextUtils.isEmpty(WXEnvironment.sDynamicUrl) && options!=null && options.get("dynamicMode")==null){
options.put("dynamicMode","true");
renderByUrl(pageName,WXEnvironment.sDynamicUrl,options,jsonInitData,width,height,flag);
return;
}
mWXPerformance.pageName = pageName;
mWXPerformance.JSTemplateSize = template.length() / 1024;
mRenderStartTime = System.currentTimeMillis();
mRenderStrategy = flag;
mGodViewWidth = width;
mGodViewHeight = height;
mInstanceId = WXSDKManager.getInstance().generateInstanceId();
WXSDKManager.getInstance().createInstance(this, template, options, jsonInitData);
mRendered = true;
}
进行view的创建初始化~
WXSDKManager createInstance
void createInstance(WXSDKInstance instance, String code, Map<String, Object> options, String jsonInitData) {
mWXRenderManager.createInstance(instance, instance.getInstanceId());
mBridgeManager.createInstance(instance.getInstanceId(), code, options, jsonInitData);
}
1、将json数据与instanceId添加到renderManager中;
private ConcurrentHashMap<String, WXRenderStatement> mRegistries;
public void createInstance(WXSDKInstance instance, String instanceId) {
mRegistries.put(instanceId, new WXRenderStatement(instance, instanceId));
}
2、使用bridge桥接管理器进行UI绘制;
public void createInstance(final String instanceId, final String template,
final Map<String, Object> options, final String data) {
if (!WXEnvironment.sSupport || TextUtils.isEmpty(instanceId)
|| TextUtils.isEmpty(template) || mJSHandler == null) {
WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId);
if (instance != null) {
instance.onRenderError(WXRenderErrorCode.WX_CREATE_INSTANCE_ERROR, "createInstance fail!");
}
return;
}
post(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
invokeCreateInstance(instanceId, template, options, data);
final long totalTime = System.currentTimeMillis() - start;
WXSDKManager.getInstance().postOnUiThread(new Runnable() {
@Override
public void run() {
WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId);
if (instance != null) {
instance.createInstanceFinished(totalTime);
}
}
}, 0);
WXLogUtils.renderPerformanceLog("invokeCreateInstance", totalTime);
}
}, instanceId);
}
private void invokeCreateInstance(String instanceId, String template,
Map<String, Object> options, String data) {
if (mMock) {
mock(instanceId);
} else {
if (!mInit) {
WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId);
if (instance != null) {
instance.onRenderError(WXRenderErrorCode.WX_CREATE_INSTANCE_ERROR, "createInstance "
+ "fail!");
}
String err = "[WXBridgeManager] invokeCreateInstance: framework.js uninitialized.";
WXErrorCode.WX_ERR_INVOKE_NATIVE.appendErrMsg(err);
commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_ERR_INVOKE_NATIVE);
WXLogUtils.e(err);
return;
}
try {
if (WXEnvironment.isApkDebugable()) {
WXLogUtils.d("createInstance >>>> instanceId:" + instanceId
+ ", options:"
+ WXJsonUtils.fromObjectToJSONString(options)
+ ", data:" + data);
}
WXJSObject instanceIdObj = new WXJSObject(WXJSObject.String,
instanceId);
WXJSObject instanceObj = new WXJSObject(WXJSObject.String,
template);
WXJSObject optionsObj = new WXJSObject(WXJSObject.JSON,
options == null ? "{}"
: WXJsonUtils.fromObjectToJSONString(options));
WXJSObject dataObj = new WXJSObject(WXJSObject.JSON,
data == null ? "{}" : data);
WXJSObject[] args = {instanceIdObj, instanceObj, optionsObj,
dataObj};
mWXBridge.execJS(instanceId, null, METHOD_CREATE_INSTANCE, args);
commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_SUCCESS);
} catch (Throwable e) {
WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId);
if (instance != null) {
instance.onRenderError(WXRenderErrorCode.WX_CREATE_INSTANCE_ERROR,
"createInstance failed!");
}
String err = "[WXBridgeManager] invokeCreateInstance " + e.getCause();
WXErrorCode.WX_ERR_INVOKE_NATIVE.appendErrMsg(err);
commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_ERR_INVOKE_NATIVE);
WXLogUtils.e(err);
}
}
}
WXBridge
class WXBridge implements IWXBridge {
private static final String TAG = "WXBridge";
/**
* Init JSFrameWork
*
* @param framework assets/main.js
*/
public native int initFramework(String framework, WXParams params);
/**
* Execute JavaScript function
*
* @param instanceId
* @param namespace default global
* @param function function string name
* @param args WXJSObject array
*/
public native int execJS(String instanceId, String namespace, String function, WXJSObject[] args);
/**
* JavaScript uses this methods to call Android code
*
* @param instanceId
* @param tasks
* @param callback
*/
public void callNative(String instanceId, String tasks, String callback) {
WXBridgeManager.getInstance().callNative(instanceId, tasks, callback);
}
/**
* Report JavaScript error.
*
* @param instanceId
* @param func exception function
* @throws String exception
*/
public void reportJSException(String instanceId, String func, String exception) {
WXBridgeManager.getInstance().reportJSException(instanceId, func, exception);
}
public void setTimeoutNative(String callbackId, String time) {
WXBridgeManager.getInstance().setTimeout(callbackId, time);
}
}
作用: execJS 调用 so库方法执行JS脚本,进行UI渲染;
总结
流程大概就是这样,只是按照渲染流程进行分析,但是目前c++代码并没有开源,只能追到这里,等开源后会将后续分析加入,希望能够对大家有所帮助~ 谢谢!!!