点我达骑手Weex接入过程详述

yangyenan 2年前
   <p>一、概述</p>    <p>截止11月底点我达骑手APP已完整的接入了13个weex页面,效果还不错,Weex化推进也比较顺利。现特分享一下Android端点我达骑手APP接入步骤及相关核心逻辑,以此作为记录,同时也希望能给后来者带来一些帮助。</p>    <p>二、集成Weex</p>    <p>Android 集成有两种方式:</p>    <p>- 源码依赖:能够快速使用WEEX最新功能,可以根据自己项目的特性进行相关改进。</p>    <p>- SDK依赖:WEEX 会在jcenter 定期发布稳定版本。</p>    <p>由于业务关系点我达骑手APP接入了菜鸟SDK,而菜鸟SDK通过SDK依赖的方式在其中引入了Weex。</p>    <p>1、新建Weex module</p>    <p>新建一个Android Library类型的module,起名Weex。</p>    <p>2、添加build.gradle依赖</p>    <p>在Weex module下的build.gradle文件添加Weex依赖。</p>    <pre>  <code class="language-java">compile 'com.taobao.android:weex_sdk:0.11.0'  compile 'com.taobao.android:weex_inspector:0.12.1'</code></pre>    <p>3、实现Weex接口协议</p>    <p>Weex提供了扩展机制,可以根据自己的业务进行定制自己的功能。比如需要加载图片,则需要实现实现Weex的图片接口协议。常见的module、component及adapter实现,官方示例中都已经给出完整的案例。</p>    <pre>  <code class="language-java">public class ImageAdapter implements IWXImgLoaderAdapter {    @Override    public void setImage(String url, ImageView view, WXImageQuality quality,                         WXImageStrategy strategy) {      //实现你自己的图片下载,否则图片无法显示。    }  }</code></pre>    <p>4、初始化</p>    <p>新建DWeexManager管理类,由此类统一管理Weex初始化、配置文件加载及路由控制等相关配置。initWeex()是DWeexManager的入口,初始化WXSDK引擎、加载JS配置文件列表及注册扩展Module等。以下是initWeex()方法的相关逻辑:</p>    <pre>  <code class="language-java">public void initWeex(Application application) {          mContext = application.getApplicationContext();          weexPath = mContext.getFilesDir().getPath();          appVersion = PhoneUtils.getAppVersion(mContext);          InitConfig config=new InitConfig.Builder().setImgAdapter(new ImageAdapter())                      .setHttpAdapter(new OkHttpAdapter()).build();          WXSDKEngine.initialize(application, config);          mConfigList = WXJsonUtils.getList(ConfigManager.getWeexConfig(mContext),                         WeexConfig.class);          try {              WXSDKEngine.registerModule("DEvent", WXEventModule.class);              WXSDKEngine.registerModule("DModal", ModalModule.class);              WXSDKEngine.registerModule("DCommon", WXCommonModule.class);              WXSDKEngine.registerModule("DRouter", WXRouterModule.class);          } catch (WXException e) {              e.printStackTrace();          }      }</code></pre>    <p>在Application的onCreate方法中调用进行初始化。</p>    <pre>  <code class="language-java">public class DwdApplication extends Application {      @Override    public void onCreate() {      super.onCreate();      DWeexManager.getInstance().initWeex(this);    }  }</code></pre>    <p>5、开始渲染</p>    <p>新建一个统一的渲染页面WeexPageActivity,在onCreate中初始化WXSDKInstance,并加载js文件。实现接口IWXRenderListener,重写onRenderSuccess、onException等回调方法,对加载过程进行控制,具体实现可以参考官方demo中AbsWeexActivity。所有的Weex页面统一跳转到此activity,通过Intent传入以下四个参数:</p>    <ul>     <li>mUrl:js链接,线上及本地均可</li>     <li>mH5:降级到h5的链接</li>     <li>mBundleUrl:需要传入JS页面的参数BUNDLE_URL</li>     <li>pageName:JS页面对应Native页面名字,从配置文件中获取,降级到Native使用</li>    </ul>    <p>以下为渲染的部分代码,renderURL方法为加载URL的关键方法,由于本地JS文件与在线JS加载方法不一致,此处需要根据URL判断是否本地JS文件,options为需要传入JS页面的参数,格式类似Http的get请求。</p>    <pre>  <code class="language-java">protected void createWeexInstance() {            destoryWeexInstance();          mInstance = new WXSDKInstance(this);          mInstance.registerRenderListener(this);      }    protected void destoryWeexInstance() {            if (mInstance != null) {              mInstance.registerRenderListener(null);              mInstance.destroy();              mInstance = null;          }      }     protected void renderURL(String url, String bundleUrl, String jsonInitData) {            Map<String, Object> options = new HashMap<>();          options.put(WXSDKInstance.BUNDLE_URL, bundleUrl);          if (isLocalPage()) {              Uri uri = Uri.parse(url);              if (uri != null) {                  mInstance.render(getPageName(),                                   WXFileUtils.loadFileOrAsset(assembleFilePath(uri),                                   this), options, jsonInitData,                                   PhoneUtils.getScreenWidth(this),                                   PhoneUtils.getScreenHeight(this),                                  WXRenderStrategy.APPEND_ASYNC);              }          } else {              mInstance.renderByUrl(getPageName(), url, options, jsonInitData,                                     PhoneUtils.getScreenWidth(this),                                    PhoneUtils.getScreenHeight(this),                                     WXRenderStrategy.APPEND_ASYNC);          }      }</code></pre>    <p>渲染成功回调,在此方法中可以进行完成后的相关操作。如取消渲染计时器及隐藏加载进度条。</p>    <pre>  <code class="language-java">@Override  public void onRenderSuccess(WXSDKInstance instance, int width, int height) {            renderFinish = true;          if (timer != null) {              timer.cancel();          }          mProgressBar.setVisibility(View.GONE);      }</code></pre>    <p>6、降级</p>    <p>渲染失败或者加载时间超时,降级到Native或者h5页面。</p>    <pre>  <code class="language-java">@Override  public void onException(WXSDKInstance instance, String errCode, String msg) {            renderFinish = true;          if (timer != null) {              timer.cancel();          }          downgrade(errCode, msg);      }    private void downgrade(String errCode, String msg) {            if (mProgressBar != null) {              mProgressBar.setVisibility(View.GONE);          }          boolean isClosePage = true;          if (!TextUtils.isEmpty(mH5)) {              isClosePage = WeexNavHelper.openWebview(this, mBundleUrl, getIntent());          } else {               errorOnpenNative = true;              isClosePage = WeexNavHelper.openNative(this, mBundleUrl, pageName,                                                       getIntent());          if (isClosePage) {            finish();           }      }</code></pre>    <h3>三、高阶知识</h3>    <p>1、路由控制</p>    <p>路由主要由配置文件预先设定好的规则进行控制,配置文件由服务端下发,实现动态更新。由于Weex刚接入,为了降低风险,增加了全局开关以及单个页面开关,开关关闭时就直接访问Native页面。路由规则如下图: <img src="https://simg.open-open.com/show/8d778947cc54d4c0910b49be19b72e4e.png"> 配置文件:</p>    <pre>  <code class="language-java">{      "weex_switch": 1,      "version": "1.0.0",      "interfaceVersion":"v1",      "support_city_list":["1"],      "js_list": [          {              "activityName": "com.dwd.setting.MoreActivity",              "jsDownloadUrl": "http://prodwbbucket.com/weex/js/SettingView.js",              "h5Url": "",              "pageName": "setting/SettingView.js",              "jsSwitch": 1,              "version": "1.0"          }      ]  }</code></pre>    <p>外层为全局参数:</p>    <pre>  <code class="language-java">weex_switch:全局开关    version:文件版本号    interfaceVersion:页面访问接口版本号    support_city_list:支持Weex城市列表,区域控制,减小影响范围</code></pre>    <p>列表为单个js页面相关参数:</p>    <pre>  <code class="language-java">h5Url:降级H5链接    md5:js文件md5值    activityName:对应的activity页面名,完整包名    pageName:对应js文件名    jsDownloadUrl:js文件下载链接    version:js文件版本号    jsSwitch:单个js开关</code></pre>    <p>访问页面时,会通过Intent中的class或者Url链接查找配置文件对应配置,如果未找到或者开关关闭,就直接访问Native页面(前提是对应的Native页面存在),反之则进行js参数组装,然后调用WeexPageActivity渲染页面。</p>    <pre>  <code class="language-java">public Intent getIntentByActivityName(Context context, Intent paramsIntent) {          if (!ConfigManager.getWeexEnable(mContext) || mConfigList == null) {              return paramsIntent;          }          ComponentName componentName = paramsIntent.getComponent();          if (componentName == null) {              return paramsIntent;          }          String className = componentName.getClassName();          for (WeexConfig weexConfig : mConfigList) {              if (TextUtils.equals(className, weexConfig.name) && weexConfig.jsSwitch                    == 1) {                  Bundle bundle = paramsIntent.getExtras();                  String urlParams = WeexNav.makeUrl(bundle, "");                  String url = weexConfig.page + urlParams;                  Intent intent = getIntentByUrl(context, url, weexConfig);                  if (intent != null) {                      return intent;                  }              }          }          return paramsIntent;      }</code></pre>    <p>2、JS预下载</p>    <p>为了加快Weex页面打开时间,推荐采用本地加载js的方式。为此在程序启动之后更新配置文件同时下载js文件。具体流程如下:</p>    <p>1) 应用启动时校验配置文件MD5以及版本号,如果不一致更新配置文件,配置文件与应用版本绑定,防止程升级后加载老版本Weex配置文件。另外为了确保实时性,增加了轮询服务进行同步;</p>    <p>2)遍历js列表,校验本地是否存在该js文件,如果存在,则继续校验js文件版本号(由于目前配置文件为手动上传,无法生成MD5,暂时不比较文件MD5,此处会有风险),若文件不存在或者版本不一致则下载更新js文件;</p>    <p>更新关键方法如下:</p>    <pre>  <code class="language-java">public void update() {          if (mConfigList == null || mConfigList.size() <= 0) {              return;          }          try {              for (WeexConfig config : mConfigList) {                  File jsFile = new File(weexPath, config.page);                  if (jsFile.exists()) {                      if (!TextUtils.equals(config.version,                           ShareStoreHelper.getString(mContext, WEEX_FILE_VERSION +                              config.page))) {                          jsFile.delete();                          downloadJs(config);                      }                   } else {                      downloadJs(config);                  }              }          } catch (Exception e) {              e.printStackTrace();          }      }</code></pre>    <p>3、降级Native参数传值</p>    <p>降级Native时根据路由规则跳转对应的Native页面,同时组装相应的参数。如果是根据Url链接匹配页面则需要拆分链接生成对应参数。特别说明Native页面与js页面参数key需要统一,否则会出现参数无法识别异常。</p>    <pre>  <code class="language-java">public static Intent putIntentExtra(String url, Intent intent) {          if (TextUtils.isEmpty(url) || intent == null) {              return intent;          }          Uri uri = Uri.parse(url);          Set<String> set = uri.getQueryParameterNames();          if (set == null || set.size() <= 0) {              return intent;          }          for (String name : set) {              intent.putExtra(name, uri.getQueryParameter(name));          }          return intent;      }</code></pre>    <h3>四、后语</h3>    <p>至此整个集成过程已经完成,总体来说集成过程比较顺利,在此感谢Weex技术社区的力量,也希望此文能给后来者一些帮助。不过由于Weex开源时间比较短,整个生态圈还不太完善,在Weex开发的过程中也踩了一些坑,期待更多的Weex爱好者能为此贡献自己的一份力量,让Weex发扬光大,一统江湖!</p>    <p> </p>    <p>来自:http://tech.dianwoda.com/2017/12/25/android-dian-wo-da-qi-shou-weexjie-ru-guo-cheng-xiang-shu/</p>    <p> </p>