Weex Android SDK源码分析之界面渲染(上)

前言

     首先感谢阿里巴巴团队对移动端跨三端与动态化做出的杰出贡献,阿里在6月初,彻底开放了weex android与ios端内测,最近已使用一周有余,效果果然达到了write once,run everywhere!而且运行效果可以与原声媲美,并且virtual dom的加入更加优化了性能问题,切对native扩展等提供了很好的预留,目前可以采用组件形式与全页形式进行集成,接下来我给大家介绍下weex在android端的工作原理及代码分析。


Weex SDK目录结构



《Weex Android SDK源码分析之界面渲染(上)》

源码分析



《Weex Android SDK源码分析之界面渲染(上)》



《Weex Android 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++代码并没有开源,只能追到这里,等开源后会将后续分析加入,希望能够对大家有所帮助~     谢谢!!!      

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/walid1992/article/details/51705371
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞