weex在原生端的主要原理是,通过 v8 引擎,将 bundlejsweex-framework 结合在一起在 v8 上跑出结果,然后,通过返回对象动态布局 native 端页面

  • v8
  • 引擎接口:jsbridge
  • 引擎调用JAVA
  • JNI 实践小 Demo

V8

weex 中使用的 v8 引擎代码,代码仓库: weex_js_engine.

其中,主要使用到的技术为 Android 中的 JNI 技术。

在编程领域, JNI (Java Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的, 并且被编译为基于本机硬件和操作系统的程序。

weex 通过调用使用 C/C++ 编写的自定义 JS Engine, 将 JS 代码跑在该引擎上,并将 View 和需要与原生交互的业务代码通过 JSBridge 与原生交互。

引擎接口

通过查看 com_taobao_weex_bridge_WXBridge.cpp, 我们不难发现,其中从 V8 主要暴露给原生的的接口有:

  • Java_com_taobao_weex_bridge_WXBridge_initFramework: initFramework 方法

    该方法主要在应用初始化时候调用,由weex 项目生成,在 weex-SDKassets/main.js 中能找到具体的框架实现文件。

  • Java_com_taobao_weex_bridge_WXBridge_execJS: execJS 方法

引擎调用 JAVA

C/C++ 中,如果在调用接口需要返回的,直接 return 即可。若是需要异步调用 JAVA 的,可以做以下类似的操作:

// 如 WXBridge.cpp 中,调用 Bridge.java 中的 callAddElement 方法。
if (jCallAddElementMethodId == NULL) {
  jCallAddElementMethodId = env->GetMethodID(
    jBridgeClazz,
   "callAddElement",
   "(Ljava/lang/String;Ljava/lang/String;[BLjava/lang/String;Ljava/lang/String;)I");
}

int flag = env->CallIntMethod(
  jThis,
  jCallAddElementMethodId,
  jInstanceId,
  jref,
  jdomString,
  jindex,
  jCallback);

接下来,是 JNI 的一个 DEMO 实践

简要地实现一个 C++ 接口,类比 weex_js_engineV8 引擎上做的一个接口扩展。然后 Java 将“引擎”载入到应用中,应用通过调用相应的接口,通过返回的数据,动态绘制按钮。类似 weex 在运行 bundlejs 后,获取 view 对象,然后再通过 android 动态绘制 view

安装 NDK

NDK (Native Development Kit), 主要将 C/C++ 代码编译成 .so 文件的一个库。在该样例代码中,可以在运行后的 app\build\intermediates\transforms\stripDebugSymbol\debug\folders\2000\1f\main\lib 目录下找到 .so 的库文件。当然,在编译后的 apk 文件的 lib 目录下也能找到该文件。

原生开发工具包(英语:native development kit,简称NDK)是一种基于原生程序接口的软件开发工具。通过此工具开发的程序直接以本地语言运行,而非虚拟机。因此只有java等基于虚拟机运行的语言的程序才会有原生开发工具包。

android -> SDK Manager -> Android SDK -> NDK

tim 20170808113240

新建项目

记得在新建时,把 C++ 支持勾上。

tim 20170808111556

编写“引擎”接口

这里是简单展示一下 weex_js_engine 的大概原理。样例中,为 MainActivity 新增一个 newBtn 接口,接口返回一个 hashMap 对象,将绘制时需要的参数以 map 的形式传给 Java

JNIEXPORT jobject JNICALL
Java_io_github_zhoukekestar_hellojni_MainActivity_newBtn(
        JNIEnv *env,
        jobject
) {
    jclass mapClass = env->FindClass("java/util/HashMap");
    jsize map_len = 5;
    jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
    jobject hashMap = env->NewObject(mapClass, init, map_len);

    jmethodID put = env->GetMethodID(mapClass, "put",
                                     "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

    env->CallObjectMethod(hashMap, put,
                          env->NewStringUTF(((string)"height").c_str()),
                          env->NewStringUTF(((string)"100").c_str()));
    env->CallObjectMethod(hashMap, put,
                          env->NewStringUTF(((string)"width").c_str()),
                          env->NewStringUTF(((string)"500").c_str()));
    env->CallObjectMethod(hashMap, put,
                          env->NewStringUTF(((string)"x").c_str()),
                          env->NewStringUTF(((string)"100").c_str()));
    env->CallObjectMethod(hashMap, put,
                          env->NewStringUTF(((string)"y").c_str()),
                          env->NewStringUTF(((string)"100").c_str()));
    env->CallObjectMethod(hashMap, put,
                          env->NewStringUTF(((string)"text").c_str()),
                          env->NewStringUTF(((string)"hello jni").c_str()));
    return hashMap;
}

获取“引擎”数据

获取“引擎”数据,并动态绘制原生控件

public class MainActivity extends AppCompatActivity {

    // 应用启动时即加载 C++ 库
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 通过返回的数据,我们就可以动态新建一个 button 了
        ConstraintLayout layout = (ConstraintLayout)findViewById(R.id.content);
        HashMap btnConfig = newBtn();
        Button button = new Button(this);
        button.setWidth(Integer.parseInt((String) btnConfig.get("width")));
        button.setHeight(Integer.parseInt((String) btnConfig.get("height")));
        button.setText((String) btnConfig.get("text"));
        button.setX(Float.parseFloat((String) btnConfig.get("x")));
        button.setY(Float.parseFloat((String) btnConfig.get("y")));
        layout.addView(button);
    }

    // 定义了 newBtn 接口,通过该方法,可以调用 C++ 代码,并返回数据
    public native HashMap newBtn();
}

运行截图

wechat image_20170808165801

样例源码

drafts/hellojni

References