2018-12-21
在之前的文章中我们编译了供 Android 平台使用的 JavaScriptCore。在本文中,我们的目标是创建一个 Android 工程,导入编译的 JavaScriptCore,并进行 JavaScript 脚本解析。
主要有以下步骤:
在本文中,我们直接从第二步开始。
首先,我们在 Android Studio 中创建一个新项目,需要注意的是,要勾选 **Include C++ support** 。
项目创建好后,打开项目,C++ 代码都位于:native-lib.cpp 中。
在 app/src/main 下创建目录 jniLibs,并将所有 so 文件拷贝到该目录下:
.
├── AndroidManifest.xml
├── cpp
│ └── native-lib.cpp
├── java
│ └── com
│ └── maxiee
│ └── jsclearning1
│ └── MainActivity.java
├── jniLibs
│ ├── arm64-v8a
│ │ ├── libc++_shared.so
│ │ └── libjsc.so
│ ├── armeabi-v7a
│ │ ├── libc++_shared.so
│ │ └── libjsc.so
│ ├── x86
│ │ ├── libc++_shared.so
│ │ └── libjsc.so
│ └── x86_64
│ ├── libc++_shared.so
│ └── libjsc.so
导入了 so 库之后,我们还需要导入头文件。需要修改 CMakeLsits.txt,添加以下代码:
include_directories(../../path/to/your/header/files)
这里遇到一点问题,我用 jsc-android-buildscripts 编译出来的 dist 目录下,头文件被打平到一个目录下了,而 Header 文件中还是使用 JavaScriptCore 嵌套目录。我的解决方法是修改头文件,修复扁平化引用。
看起来是 native-lib 依赖了 jsc,但是在具体连接的时候出现了问题。
通过这篇文章:Android Studio cmake和jni的一些坑 找到了原因,是路径的问题。
修改 CMakeLists.txt 为:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
include_directories(/Users/maxiee/Code/Android/native-js-android/nativejs/src/main/jni/include)
set(CURRENT_DIR ${CMAKE_SOURCE_DIR})
message("CURRENT_DIR:" ${CMAKE_SOURCE_DIR})
add_library(jsc SHARED IMPORTED)
set_target_properties( # Specifies the target library.
jsc PROPERTIES
IMPORTED_LOCATION "${CURRENT_DIR}/src/main/jniLibs/${ANDROID_ABI}/libjsc.so")
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib
jsc
${log-lib} )
出现这个问题,是因为我使用的头文件不对。之前编译出来的 jsc 的头文件有问题,得到的可能是 macOS 下的头文件。
我换了一份头文件可以成功编译了,正确的头文件是不包含 CoreFoundation 依赖的。
下面来到 native-lib.cpp,加入以下代码:
#include <jni.h> #include <string> #include "JavaScriptCore/JavaScript.h" std::string JSStringToStdString(JSStringRef jsString) { size_t maxBufferSize = JSStringGetMaximumUTF8CStringSize(jsString); char* utf8Buffer = new char[maxBufferSize]; size_t bytesWritten = JSStringGetUTF8CString(jsString, utf8Buffer, maxBufferSize); std::string utf_string = std::string(utf8Buffer, bytesWritten - 1); delete [] utf8Buffer; return utf_string; } extern "C" JNIEXPORT jstring JNICALL Java_com_maxiee_jsclearning1_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { JSContextGroupRef contextGroup = JSContextGroupCreate(); JSGlobalContextRef globalContext = JSGlobalContextCreateInGroup(contextGroup, nullptr); JSStringRef statement = JSStringCreateWithUTF8CString( "function degToRad(value) {return (value * Math.PI) / 180; } ('90 deg = ' + degToRad(90))"); JSValueRef retValue = JSEvaluateScript(globalContext, statement, nullptr, nullptr, 1, nullptr); JSStringRef retString = JSValueToStringCopy(globalContext, retValue, nullptr); std::string hello = JSStringToStdString(retString); JSGlobalContextRelease(globalContext); JSContextGroupRelease(contextGroup); JSStringRelease(statement); JSStringRelease(retString); return env->NewStringUTF(hello.c_str()); }
其中:
当成功连接后,编译 App,可以看到成功运行了一个 JavaScript 方法,并在屏幕上展示结果:
通过这次实践,虽然最终把代码跑起来了,但整个过程中懵懵懂懂,还是非常模糊。下一步要做的,是把概念不清晰的地方搞明白。
本文参考了以下文章: