End

混合开发 weex 跨平台

本文地址


目录

混合开发 weex 跨平台

Weex 是一个可以使用现代化的 Web 技术开发高性能原生应用的框架。

简介

Weex 致力于使开发者能基于通用跨平台的 Web 开发语言和开发经验,来构建 AndroidiOSWeb 应用。简单来说,在集成了 WeexSDK 之后,你可以使用 JavaScript 语言和前端开发经验来开发移动应用。

在开发阶段,一个 Weex 页面就像开发普通网页一样;在运行时,Weex 页面又充分利用了各种操作系统的原生组件和能力。

  • 高性能:Weex 使用原生组件和原生模块,来最大化利用原生渲染的性能优势以及平台能力,所有的组件和模块都是可插拔、可扩展的。
  • 跨平台:你可以使用同一份代码编译成不同目标文件分别在 Web、Android 和 iOS 平台上运行。原生的组件和模块在不同平台中有不同的实现,但是它们都提供了相同的接口。
  • 贴近前端生态:Weex 拥抱已有的 Web 生态,你可以使用现代化的前端技术开发移动应用。 Weex 支持了最常用 CSS 样式以及最流行的前端框架,如 Vue 和 Rax,在未来或许还可以支持更多。

Weex并不是一个前端框架。实际上,前端框架仅仅是 Weex 的语法层或称之为 DSL (Domain-specific Language),它们与原生渲染引擎是分离的。换句话说,Weex 并不依赖于特定的前端框架,随着前端技术的发展,Weex 也可以集成更多广泛使用的前端框架。

第一个项目

开发环境配置

  • 安装 Node.jsnpm 包管理工具

    npm -v

  • 安装命令行工具 Weex CLI:用来快速创建一个空项目、初始化 iOS 和 Android 开发环境、调试、安装插件等操作

    npm i -g weex-toolkit // i 是 install 的缩写,-g 代表全局安装
    weex -v
    weex help

  • 初始化 Weex 项目

    weex create project_name //在当前目录下创建指定目录,创建一个空的模板项目,源代码在 src/ 目录中

  • 运行

    npm install //安装项目所需依赖。如果在初始化项目时选择了自动安装依赖,可跳过这一步
    npm start //会启动一个本地的 web 服务,监听 8081 端口

  • 默认情况下并不初始化 iOS 和 Android 项目,可以通过如下命令来添加特定平台的项目

    weex platform add android/ios

  • 启动应用

    weex run android/ios/web

  • 调试

    weex debug //启动一个调试服务,并且在 Chrome 中打开调试页面

集成到 Android 项目

Weex 支持以下 ABI:x86armeabi-v7aarm64-v8a

  • 设置 gradle 依赖:weex_sdk、fastjson、support library
  • 配置混淆规则
  • 声明权限:网络和SD卡
  • 初始化 sdk
  • 扩展配置
  • 创建 XSDKInstance,WXSDKInstance 是 weex 渲染页面的基本单元
    • 通过 instance.render(url) 拉取 bundle
    • 在 IWXRenderListener 的回调方法 onViewCreated 中返回创建的 view
    • 将返回的 view 添加到 Activity 的 view 上
  • 运行 app,您将会看到一个 hello world 页面

最简单的案例

//初始化
InitConfig.Builder builder = new InitConfig.Builder();
WXSDKEngine.initialize(this, builder.build());
<template>
  <div style="justify-content:center;">
    <text class="freestyle">白乾涛</text>
  </div>
</template>
http://dotwe.org/
<style scoped>
  .freestyle {
    color: #41B883;
    text-align: center;
    font-size: 233px;
  }
</style>

在上面的例子中,<div><text> 在移动端上渲染出来的都是原生组件,充分利用了操作系统组件的能力与渲染速度。

调试

调试组件 Devtools

Android Devtools for Apache Weex 能够方便调试 Weex 页面

implementation 'com.taobao.android:weex_inspector:0.24.2.11'

//需要引入 okhttp
implementation 'com.squareup.okhttp:okhttp:2.3.0'
implementation 'com.squareup.okhttp:okhttp-ws:2.3.0'

调试命令行 weex-toolkit

weex-toolkit

npm i -g weex-toolkit // i 是 install 的缩写,-g 代表全局安装
weex -v
weex help
weex debug // 启动调试控制台

如果你有需要编译的页面,也可以通过 weex debug [ folder | file ] 命令进行编译,编译后的页面将会在 页面 这个标签下显示。

开始调试

调试需要使用集成了 Weex 调试工具 Devtools 的 APP 进行扫码调试。

在调试开始前,请确保安装了调试应用的手机与 PC 处于同一局域网下,同时关闭 VPN 等代理设置,否则将无法正常进行调试。同时,我们在使用功能时,尽量保证其他功能为关闭状态,如在使用JS调试功能时将无关的网络审查功能关闭,将会有更好的开发体验。

你也可以使用 Weex 官方提供的 Playground App 进行代码调试。

回到调试二维码页面,用应用的扫码功能进行扫码,即可进入调试控制台。

其他调试技巧

  • JS 调试:开启 JS 调试功能即可进入 Weex 代码调试模式
  • 日志等级:通过控制台及日志等级选项对日志进行筛选过滤,保留你关注的日志内容
  • 网络审查:开启网络审查功能可以查看应用的网络请求信息,对页面的请求进行有效的分析
  • 节点审查:节点审查模式下会发送大量的页面信息,默认开启,建议复杂页面情况下关闭该功能
  • 拓展功能:快速导航、文件替换等拓展功能需在 JS 调试功能开启并且处于 Weex 页面内才可使用

Weex 和 Web 的平台差异

Weex 是一个跨平台解决方案,Web 平台只是其中一种运行环境,除此之外还可以在 Android 和 iOS 客户端中运行。原生开发平台和 Web 平台之间存在很多差异,在功能和开发体验上都有一些差异。

  • Weex 环境中没有 DOM
    • 不支持 Web API
    • 没有 ElementEventFile 等对象
    • 不支持选中元素
    • 不支持基于 DOM API 的程序库(如 jQuery)
    • 有限的事件类型
  • Weex 环境中没有 BOM
    • 没有 windowscreen 对象
    • 不支持使用全局变量
    • 如果是想要获取设备的屏幕或环境信息,可以使用 WXEnvironment 变量
    • 没有 document 对象
    • 没有 historylocationnavigator 对象
  • 能够调用移动设备原生 API
    • 在 Weex 中能够调用移动设备原生 API,使用方法是通过注册、调用模块来实现
    • 内置的模块:如 clipboard(剪切板)、 navigator(导航控制)、storage(本地存储)
    • Weex 提供了横向扩展的能力,可以扩展原生模块

Weex 环境变量

每个 Weex 页面的 JS 上下文中都有一个相互独立的 weex 变量,它可以像全局变量一样使用,不过它在不同页面中是隔离而且只读的。

Weex 提供了 weex.config.env 和全局的 WXEnvironment 变量(它们是等价的)来获取当前执行环境的信息

这个例子 打印出了 Weex 环境对象中的所有值。

WXEnvironment.setOpenDebugLog(BuildConfig.DEBUG);
WXEnvironment.setApkDebugable(BuildConfig.DEBUG);

Android 功能扩展

Weex 提供了扩展机制,可以根据自己的业务进行定制自己的功能,主要分为:

  • Module 扩展:扩展 非 UI 的特定功能,例如 sendHttp、openURL
  • Component 扩展:实现特别功能的控件,例如 RichTextview,RefreshListview
  • Adapter 扩展:Weex 对一些基础功能实现了统一的接口,可实现这些接口来定制自己的业务,例如 图片下载
  • JS 全局变量自定义扩展

Module 扩展

public class MyModule extends WXModule {

    @JSMethod() // 不加参数时,run ui thread
    public void printLog(String msg) {
        Toast.makeText(mWXSDKInstance.getContext(), msg, Toast.LENGTH_SHORT).show();
    }

    @JSMethod(uiThread = false) //run JS thread
    public void fireEventSyncCall() {
    }
}
  • Weex 会根据注解 @JSMethod 来判断当前方法是否是扩展方法,以及当前方法是否要运行在 UI 线程
  • 扩展方法仅支持 int, double, float, String, Map, List 类型的参数
  • 扩展方法是通过反射来调用的,所以 Module 不能被混淆,扩展方法也必须是 public 类型
-keep public class * extends com.taobao.weex.common.WXModule{*;}

使用前必须先注册

WXSDKEngine.registerModule("MyModule", MyModule.class);

JS 调用如下:

<template>
  <div>
    <text onclick="click">testMyModule</text>
  </div>
</template>

<script>
  module.exports = {
    methods: {
      click: function() {
        weex.requireModule('MyModule').printLog("I am a weex Module");
      }
    }
  }
</script>

Component 扩展

public class RichText extends WXComponent<TextView> {

    public RichText(WXSDKInstance instance, WXVContainer parent, BasicComponentData basicComponentData) {
        super(instance, parent, basicComponentData);
    }

    @Override
    protected TextView initComponentHostView(@NonNull Context context) {
        TextView textView = new TextView(context);
        textView.setTextSize(20);
        textView.setTextColor(Color.BLACK);
        return textView;
    }

    @WXComponentProp(name = "tel")
    public void setTel(String telNumber) {
        getHostView().setText("tel: " + telNumber);
    }
}
  • Component 扩展的方法必须添加注解 @WXComponentProp(name=value)
  • Component 扩展的方法仅支持 int, double, float, String, Map, List 类型的参数
  • Component 扩展的方法是通过反射来调用的,所以不能被混淆,扩展方法也必须是 public 类型
-keep public class * extends com.taobao.weex.ui.component.WXComponent{*;}

使用前必须先注册

WXSDKEngine.registerComponent("richText", RichText.class);

JS 调用如下:

<template>
  <div>
    <richText tel="12305" style="width:200;height:100">12305</richText>
  </div>
</template>

从 WeexSDK 0.9.5 开始,支持在一个 Component 中他通过 @JSMethod 声明一个组件方法

Adapter 扩展

  • WXUserTrackAdapter:打点相关
  • IActivityNavBarSetter:WXNavigatorModule的实现依赖这个接口,用来操作navigation
  • IWXStorageAdapter:WXStorageModule实现依赖这个接口,用来实现数据的存、取默认使用DefaultWXStorage实现
  • IWXJSExceptionAdapter:WEEX的异常上报接口

ImageAdapter

Weex 和图片库完全解耦,Weex 的图片加载,都是通过调用公共接口,由实现类决定调用哪个图片库

  • IWXImgLoaderAdapter:根据url,load 图片给某个 view
  • IDrawableLoader:根据url,load 图片给某个 drawable
public interface IWXImgLoaderAdapter {
    void setImage(String url, ImageView view, WXImageQuality quality, WXImageStrategy strategy);
}
  • WXImageQuality:图片质量,有 LOW(默认), NORMAL, HIGH, ORIGINAL
  • WXImageStrategy:扩展类参数,配置图像是否可以剪切isClipping、锐化isSharpen以及配置占位符placeHolder

IWXHttpAdapter

Weex 和网络库也是解耦的,通过接口形式调用,由实现类决定调用哪个网络库。

public interface IWXHttpAdapter {
    void sendRequest(WXRequest request, OnHttpListener listener);
}
public class WXRequest {
    public Map<String, String> paramMap; // 请求参数
    public String url; // 目标 url
    public String method; // 请求方法 post、get
    public String body; //请求体
    public int timeoutMs = 3000; // 请求超时时间
    public static final int DEFAULT_TIMEOUT_MS = 3000;
    public String instanceId; // 页面 id

    public WXRequest() {
    }
}
public interface IWXHttpAdapter {
    void sendRequest(WXRequest var1, IWXHttpAdapter.OnHttpListener var2);

    public interface OnHttpListener {
        void onHttpStart(); //开始请求
        void onHeadersReceived(int statusCode, Map<String, List<String>> headers); //收到http header内容
        void onHttpUploadProgress(int uploadProgress); //上传进度
        void onHttpResponseProgress(int loadedLength); //接收到的数据长度
        void onHttpFinish(WXResponse response); //请求结束
    }
}

URIAdapter

默认实现是 DefaultUriAdapter

public interface URIAdapter {
    String REQUEST = "request";
    String IMAGE = "image";
    String FONT = "font";
    String VIDEO = "video";
    String LINK = "link";
    String BUNDLE = "bundle";
    String WEB = "web";
    String OTHERS = "others";

    @NonNull
    Uri rewrite(WXSDKInstance instance, String type, Uri uri);

    @NonNull
    Uri rewrite(String bundleURL, String type, Uri uri);
}

Weex SDK 提供 local scheme 来访问打包在应用程序中的资源,在 Android 中,image 组件将从 drawable 资源文件夹加载,字体文件将从 asserts 文件夹加载。

Native 和 JS 交互

跨页面通信

自定义发送事件

向 JS 环境发送一些事件,比如click事件

//推荐使用 WXComponent 中的 fireEvent 方法
fireEvent(String type)
fireEvent(String type, Map<String, Object> params)
fireEvent(String type, Map<String, Object> params, Map<String, Object> domChanges)

//WXSDKManager 中定义的 fireEvent 方法均已废弃(相比上面的方法,都是增加了两个参数)
fireEvent(String instanceId, String ref, String type)
  • instanceId:使用 WXComponent.getInstanceId() 获取
  • ref:产生事件的组件 id,使用 WXComponent.getRef() 获取
  • type:事件名称,weex 默认事件名称格式为"onXXX",比如OnPullDown
  • params:需要发送的一些额外数据,比如click时 view 大小、点击坐标等等
  • domChanges:目标组件的属性和样式发生的修改内容

结果回调

JS调用时,有的场景需要返回一些数

public interface JSCallback {
    void invoke(Object data);
    void invokeAndKeepAlive(Object data);
}
public class WXLocation extends WXModule {
    @JSMethod
    public void getLocation(JSCallback callback) {
        Map<String, String> data = new HashMap<>();
        data.put("x", "x");
        data.put("y", "y");
        callback.invoke(data); //notify once
        callback.invokeAndKeepAlive(data); //Continuous connection
    }
}

EEUI 框架

EEUI:Everyone Easy User Interface

使用 Vue.js 跨平台开发高质量原生(Android/iOS)应用。

EEUI 是一个基于 WeexSDK 开发的独立完整框架(EEUI 仅支持 Android、iOS两端,不支持WEB端)。

除了 Weex 原有的组件外,EEUI 还自带了很多实用的组件模块。同时 EEUI 提供完整的插件市场,在原有的组件、模块不够业务需求时可以通过插件市场来补充更多的业务需求

降级

Weex 在开发过程中,会不断增加新的 feature,但是这些 feature 可能在低版本上不兼容,此时就可以使用降级的方式在低版本 App 中以普通 Web 页面的模式来渲染。

在 Weex 里,降级行为是在前端中触发的,由客户端来实现。触发方式是调用客户端提供的 instanceWrap 模块中的 error 方法来实现。传递的参数主要用于区分降级的类型和原因,与具体业务场景相关,不做强限制。

const instanceWrap = weex.requireModule('instanceWrap')
instanceWrap.error(errorType, errorCode, message)
  • errorType:【数字】 错误类型,由前端触发的降级通常约定为 1
  • errorCode:【数字】 错误代码
  • message:【字符串】 错误信息

2020-12-18

posted @ 2020-12-18 11:00  白乾涛  阅读(504)  评论(0编辑  收藏  举报