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

Posted 王永迪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Weex Android SDK源码分析之界面渲染(上)相关的知识,希望对你有一定的参考价值。

前言

     首先感谢阿里巴巴团队对移动端跨三端与动态化做出的杰出贡献,阿里在6月初,彻底开放了weex androidios端内测,最近已使用一周有余,效果果然达到了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++代码并没有开源,只能追到这里,等开源后会将后续分析加入,希望能够对大家有所帮助~     谢谢!!!

以上是关于Weex Android SDK源码分析之界面渲染(上)的主要内容,如果未能解决你的问题,请参考以下文章

Weex Android SDK源码分析之Module(navigator)

Weex Android SDK源码分析之Module(modal)

Weex Android SDK源码分析之Module(webview)

Weex Android SDK源码分析之Module(animation)

Weex Android SDK源码分析之Module(animation)

深入Weex系列Weex渲染流程分析