mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 13:27:24 +00:00
android type wrapper
This commit is contained in:
19
.idea/libraries/Dart_SDK.xml
generated
Normal file
19
.idea/libraries/Dart_SDK.xml
generated
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="Dart SDK">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/async" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/collection" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/convert" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/core" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/developer" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/html" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/io" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/isolate" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/math" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/mirrors" />
|
||||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/typed_data" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
10
.idea/modules.xml
generated
Normal file
10
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/flutter_qjs.iml" filepath="$PROJECT_DIR$/flutter_qjs.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/android/flutter_qjs_android.iml" filepath="$PROJECT_DIR$/android/flutter_qjs_android.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/example/android/flutter_qjs_example_android.iml" filepath="$PROJECT_DIR$/example/android/flutter_qjs_example_android.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/runConfigurations/example_lib_main_dart.xml
generated
Normal file
6
.idea/runConfigurations/example_lib_main_dart.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="example/lib/main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/example/lib/main.dart" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
45
.idea/workspace.xml
generated
Normal file
45
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="FileEditorManager">
|
||||||
|
<leaf>
|
||||||
|
<file leaf-file-name="flutter_qjs.dart" pinned="false" current-in-tab="true">
|
||||||
|
<entry file="file://$PROJECT_DIR$/lib/flutter_qjs.dart">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="0">
|
||||||
|
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
</file>
|
||||||
|
<file leaf-file-name="main.dart" pinned="false" current-in-tab="false">
|
||||||
|
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
|
||||||
|
<provider selected="true" editor-type-id="text-editor">
|
||||||
|
<state relative-caret-position="0">
|
||||||
|
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||||
|
</state>
|
||||||
|
</provider>
|
||||||
|
</entry>
|
||||||
|
</file>
|
||||||
|
</leaf>
|
||||||
|
</component>
|
||||||
|
<component name="ToolWindowManager">
|
||||||
|
<editor active="true" />
|
||||||
|
<layout>
|
||||||
|
<window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
|
||||||
|
</layout>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectView">
|
||||||
|
<navigator currentView="ProjectPane" proportions="" version="1">
|
||||||
|
</navigator>
|
||||||
|
<panes>
|
||||||
|
<pane id="ProjectPane">
|
||||||
|
<option name="show-excluded-files" value="false" />
|
||||||
|
</pane>
|
||||||
|
</panes>
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent">
|
||||||
|
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||||
|
<property name="dart.analysis.tool.window.force.activate" value="true" />
|
||||||
|
<property name="show.migrate.to.gradle.popup" value="false" />
|
||||||
|
</component>
|
||||||
|
</project>
|
37
.vscode/c_cpp_properties.json
vendored
Normal file
37
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Win32",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/example/windows/flutter/ephemeral/cpp_client_wrapper/include/**"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"_DEBUG",
|
||||||
|
"UNICODE",
|
||||||
|
"_UNICODE"
|
||||||
|
],
|
||||||
|
"windowsSdkVersion": "10.0.18362.0",
|
||||||
|
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.27.29110/bin/Hostx64/x64/cl.exe",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"cppStandard": "c++17",
|
||||||
|
"intelliSenseMode": "msvc-x64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Linux",
|
||||||
|
"includePath": [
|
||||||
|
"C:/Users/ekibun/AppData/Local/Android/Sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/windows-x86_64/sysroot/usr/include"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"_DEBUG",
|
||||||
|
"UNICODE",
|
||||||
|
"_UNICODE"
|
||||||
|
],
|
||||||
|
"windowsSdkVersion": "10.0.18362.0",
|
||||||
|
"compilerPath": "C:/Users/ekibun/AppData/Local/Android/Sdk/cmake/3.10.2.4988404/bin/cmake.exe",
|
||||||
|
"cStandard": "c11",
|
||||||
|
"cppStandard": "c++17",
|
||||||
|
"intelliSenseMode": "gcc-x64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -99,7 +99,8 @@
|
|||||||
"cfenv": "cpp",
|
"cfenv": "cpp",
|
||||||
"cinttypes": "cpp",
|
"cinttypes": "cpp",
|
||||||
"typeindex": "cpp",
|
"typeindex": "cpp",
|
||||||
"__functional_03": "cpp"
|
"__functional_03": "cpp",
|
||||||
|
"compare": "cpp"
|
||||||
},
|
},
|
||||||
"java.configuration.updateBuildConfiguration": "interactive"
|
"java.configuration.updateBuildConfiguration": "interactive"
|
||||||
}
|
}
|
2
android/.idea/misc.xml
generated
2
android/.idea/misc.xml
generated
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="11" project-jdk-type="JavaSDK" />
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK" />
|
||||||
</project>
|
</project>
|
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 3.4.1)
|
cmake_minimum_required(VERSION 3.4.1)
|
||||||
set(JNI_LIB_NAME libjsengine)
|
set(JNI_LIB_NAME libjsengine)
|
||||||
set(QUICK_JS_LIB_DIR ../../../../cxx)
|
set(QUICK_JS_LIB_DIR ../../../../cxx/quickjspp)
|
||||||
|
|
||||||
|
|
||||||
# Creates and names a library, sets it as either STATIC
|
# Creates and names a library, sets it as either STATIC
|
||||||
|
195
android/src/main/jni/java_js_wrapper.hpp
Normal file
195
android/src/main/jni/java_js_wrapper.hpp
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* @Description:
|
||||||
|
* @Author: ekibun
|
||||||
|
* @Date: 2020-08-16 11:08:23
|
||||||
|
* @LastEditors: ekibun
|
||||||
|
* @LastEditTime: 2020-08-16 19:51:11
|
||||||
|
*/
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "jni_helper.hpp"
|
||||||
|
#include <android/log.h>
|
||||||
|
#include "../../../../cxx/js_engine.hpp"
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<qjs::Value>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const qjs::Value &key) const
|
||||||
|
{
|
||||||
|
return std::hash<std::string>()((std::string)key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
namespace qjs
|
||||||
|
{
|
||||||
|
|
||||||
|
JSValue javaToJs(JSContext *ctx, JNIEnv *env, jobject val, std::unordered_map<jobject, JSValue> cache = std::unordered_map<jobject, JSValue>())
|
||||||
|
{
|
||||||
|
if (val == nullptr)
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
if (cache.find(val) != cache.end())
|
||||||
|
return cache[val];
|
||||||
|
jclass objclass = env->GetObjectClass(val);
|
||||||
|
jclass classclass = env->GetObjectClass((jobject)objclass);
|
||||||
|
jmethodID mid = env->GetMethodID(classclass, "getName", "()Ljava/lang/String;");
|
||||||
|
jobject clsObj = env->CallObjectMethod(objclass, mid);
|
||||||
|
std::string className(env->GetStringUTFChars((jstring)clsObj, 0));
|
||||||
|
__android_log_print(ANDROID_LOG_DEBUG, "class", "class: %s", className.c_str());
|
||||||
|
if (className.compare("[B") == 0)
|
||||||
|
{
|
||||||
|
jsize len = env->GetArrayLength((jbyteArray)val);
|
||||||
|
return JS_NewArrayBufferCopy(ctx, (uint8_t *)env->GetByteArrayElements((jbyteArray)val, 0), len);
|
||||||
|
}
|
||||||
|
else if (className.compare("java.lang.Boolean") == 0)
|
||||||
|
{
|
||||||
|
jmethodID getVal = env->GetMethodID(objclass, "booleanValue", "()Z");
|
||||||
|
return JS_NewBool(ctx, env->CallBooleanMethod(val, getVal));
|
||||||
|
}
|
||||||
|
else if (className.compare("java.lang.Integer") == 0)
|
||||||
|
{
|
||||||
|
jmethodID getVal = env->GetMethodID(objclass, "intValue", "()I");
|
||||||
|
return JS_NewInt32(ctx, env->CallIntMethod(val, getVal));
|
||||||
|
}
|
||||||
|
else if (className.compare("java.lang.Long") == 0)
|
||||||
|
{
|
||||||
|
jmethodID getVal = env->GetMethodID(objclass, "longValue", "()J");
|
||||||
|
return JS_NewInt64(ctx, env->CallLongMethod(val, getVal));
|
||||||
|
}
|
||||||
|
else if (className.compare("java.lang.Double") == 0)
|
||||||
|
{
|
||||||
|
jmethodID getVal = env->GetMethodID(objclass, "doubleValue", "()D");
|
||||||
|
return JS_NewFloat64(ctx, env->CallDoubleMethod(val, getVal));
|
||||||
|
}
|
||||||
|
else if (className.compare("java.lang.String") == 0)
|
||||||
|
{
|
||||||
|
return JS_NewString(ctx, env->GetStringUTFChars((jstring)val, 0));
|
||||||
|
}
|
||||||
|
else if (className.compare("java.util.ArrayList") == 0)
|
||||||
|
{
|
||||||
|
jobjectArray list = jniToArray(env, val);
|
||||||
|
jsize size = env->GetArrayLength(list);
|
||||||
|
JSValue array = JS_NewArray(ctx);
|
||||||
|
cache[val] = array;
|
||||||
|
for (uint32_t i = 0; i < size; i++)
|
||||||
|
JS_DefinePropertyValue(
|
||||||
|
ctx, array, JS_NewAtomUInt32(ctx, i),
|
||||||
|
javaToJs(ctx, env, env->GetObjectArrayElement(list, i), cache),
|
||||||
|
JS_PROP_C_W_E);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
else if (className.compare("java.util.HashMap") == 0)
|
||||||
|
{
|
||||||
|
// 获取HashMap类entrySet()方法ID
|
||||||
|
jmethodID entrySetMID = env->GetMethodID(objclass, "entrySet", "()Ljava/util/Set;");
|
||||||
|
// 调用entrySet()方法获取Set对象
|
||||||
|
jobject setObj = env->CallObjectMethod(val, entrySetMID);
|
||||||
|
// 获取Set类中iterator()方法ID
|
||||||
|
jclass setClass = env->FindClass("java/util/Set");
|
||||||
|
jmethodID iteratorMID = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
|
||||||
|
// 调用iterator()方法获取Iterator对象
|
||||||
|
jobject iteratorObj = env->CallObjectMethod(setObj, iteratorMID);
|
||||||
|
// 获取Iterator类中hasNext()方法ID
|
||||||
|
// 用于while循环判断HashMap中是否还有数据
|
||||||
|
jclass iteratorClass = env->FindClass("java/util/Iterator");
|
||||||
|
jmethodID hasNextMID = env->GetMethodID(iteratorClass, "hasNext", "()Z");
|
||||||
|
// 获取Iterator类中next()方法ID
|
||||||
|
// 用于读取HashMap中的每一条数据
|
||||||
|
jmethodID nextMID = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
|
||||||
|
// 获取Map.Entry类中getKey()和getValue()的方法ID
|
||||||
|
// 用于读取“K-V”键值对,注意:内部类使用$符号表示
|
||||||
|
jclass entryClass = env->FindClass("java/util/Map$Entry");
|
||||||
|
jmethodID getKeyMID = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
|
||||||
|
jmethodID getValueMID = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
|
||||||
|
// JSObject
|
||||||
|
JSValue obj = JS_NewObject(ctx);
|
||||||
|
cache[val] = obj;
|
||||||
|
// 循环检测HashMap中是否还有数据
|
||||||
|
while (env->CallBooleanMethod(iteratorObj, hasNextMID))
|
||||||
|
{
|
||||||
|
// 读取一条数据
|
||||||
|
jobject entryObj = env->CallObjectMethod(iteratorObj, nextMID);
|
||||||
|
JS_DefinePropertyValue(
|
||||||
|
ctx, obj, JS_ValueToAtom(ctx, javaToJs(ctx, env, env->CallObjectMethod(entryObj, getKeyMID), cache)),
|
||||||
|
javaToJs(ctx, env, env->CallObjectMethod(entryObj, getValueMID), cache),
|
||||||
|
JS_PROP_C_W_E);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject jsToJava(JNIEnv *env, qjs::Value val, std::unordered_map<Value, jobject> cache = std::unordered_map<Value, jobject>())
|
||||||
|
{
|
||||||
|
if (cache.find(val) != cache.end())
|
||||||
|
return cache[val];
|
||||||
|
if (JS_IsBool(val.v))
|
||||||
|
return jniWrapPrimity<jboolean>(env, (bool)val);
|
||||||
|
{
|
||||||
|
int tag = JS_VALUE_GET_TAG(val.v);
|
||||||
|
if(tag == JS_TAG_INT) {
|
||||||
|
return jniWrapPrimity<jlong>(env, (int64_t)val);
|
||||||
|
} else if (JS_TAG_IS_FLOAT64(tag)) {
|
||||||
|
return jniWrapPrimity<jdouble>(env, (double)val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (JS_IsString(val.v))
|
||||||
|
return env->NewStringUTF(((std::string)val).c_str());
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
uint8_t *buf = JS_GetArrayBuffer(val.ctx, &size, val.v);
|
||||||
|
if (buf)
|
||||||
|
{
|
||||||
|
jbyteArray arr = env->NewByteArray(size);
|
||||||
|
env->SetByteArrayRegion(arr, 0, size, (int8_t *)buf);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (JS_IsUndefined(val.v) || JS_IsNull(val.v) || JS_IsUninitialized(val.v))
|
||||||
|
return nullptr;
|
||||||
|
if (JS_IsObject(val.v))
|
||||||
|
{
|
||||||
|
if (JS_IsFunction(val.ctx, val.v))
|
||||||
|
{
|
||||||
|
std::map<jobject, jobject> retMap;
|
||||||
|
retMap[env->NewStringUTF("__js_function__")] = jniWrapPrimity<jlong>(env, (int64_t) new JSValue{JS_DupValue(val.ctx, val.v)});
|
||||||
|
return jniWrapMap(env, retMap);
|
||||||
|
}
|
||||||
|
else if (JS_IsArray(val.ctx, val.v) > 0)
|
||||||
|
{
|
||||||
|
uint32_t arrlen = (uint32_t)val["length"];
|
||||||
|
jclass class_arraylist = env->FindClass("java/util/ArrayList");
|
||||||
|
jmethodID arraylist_init = env->GetMethodID(class_arraylist, "<init>", "()V");
|
||||||
|
jobject list = env->NewObject(class_arraylist, arraylist_init);
|
||||||
|
jmethodID arraylist_add = env->GetMethodID(class_arraylist, "add", "(Ljava/lang/Object;)Z");
|
||||||
|
for (uint32_t i = 0; i < arrlen; i++)
|
||||||
|
{
|
||||||
|
env->CallBooleanMethod(list, arraylist_add, jsToJava(env, val[i], cache));
|
||||||
|
}
|
||||||
|
cache[val] = list;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qjs::JSPropertyEnum *ptab;
|
||||||
|
uint32_t plen;
|
||||||
|
if (JS_GetOwnPropertyNames(val.ctx, &ptab, &plen, val.v, -1))
|
||||||
|
return nullptr;
|
||||||
|
std::map<jobject, jobject> retMap;
|
||||||
|
for (uint32_t i = 0; i < plen; i++)
|
||||||
|
{
|
||||||
|
retMap[jsToJava(env, {val.ctx, JS_AtomToValue(val.ctx, ptab[i].atom)}, cache)] =
|
||||||
|
jsToJava(env, {val.ctx, JS_GetProperty(val.ctx, val.v, ptab[i].atom)}, cache);
|
||||||
|
JS_FreeAtom(val.ctx, ptab[i].atom);
|
||||||
|
}
|
||||||
|
js_free(val.ctx, ptab);
|
||||||
|
jobject ret = jniWrapMap(env, retMap);
|
||||||
|
cache[val] = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} // namespace qjs
|
62
android/src/main/jni/jni_helper.hpp
Normal file
62
android/src/main/jni/jni_helper.hpp
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* @Description:
|
||||||
|
* @Author: ekibun
|
||||||
|
* @Date: 2020-08-16 13:20:03
|
||||||
|
* @LastEditors: ekibun
|
||||||
|
* @LastEditTime: 2020-08-16 17:56:52
|
||||||
|
*/
|
||||||
|
#include <jni.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
jclass jniInstanceOf(JNIEnv *env, jobject obj, const char *className)
|
||||||
|
{
|
||||||
|
jclass jclazz = env->FindClass(className);
|
||||||
|
return env->IsInstanceOf(obj, jclazz) ? jclazz : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobjectArray jniToArray(JNIEnv *env, jobject obj)
|
||||||
|
{
|
||||||
|
jmethodID mToArray = env->GetMethodID(env->GetObjectClass(obj), "toArray", "()[Ljava/lang/Object;");
|
||||||
|
return (jobjectArray)env->CallObjectMethod(obj, mToArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject jniWrapMap(JNIEnv *env, std::map<jobject, jobject> val)
|
||||||
|
{
|
||||||
|
jclass class_hashmap = env->FindClass("java/util/HashMap");
|
||||||
|
jmethodID hashmap_init = env->GetMethodID(class_hashmap, "<init>", "()V");
|
||||||
|
jobject map = env->NewObject(class_hashmap, hashmap_init);
|
||||||
|
jmethodID hashMap_put = env->GetMethodID(class_hashmap, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||||
|
for (auto it = val.begin(); it != val.end(); ++it)
|
||||||
|
{
|
||||||
|
env->CallObjectMethod(map, hashMap_put, it->first, it->second);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
jobject jniWrapPrimity(JNIEnv *env, T obj);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
jobject jniWrapPrimity(JNIEnv *env, jlong obj)
|
||||||
|
{
|
||||||
|
jclass jclazz = env->FindClass("java/lang/Long");
|
||||||
|
jmethodID jmethod = env->GetMethodID(jclazz, "<init>", "(J)V");
|
||||||
|
return env->NewObject(jclazz, jmethod, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
jobject jniWrapPrimity(JNIEnv *env, jboolean obj)
|
||||||
|
{
|
||||||
|
// TODO see https://github.com/flutter/flutter/issues/45066
|
||||||
|
std::map<jobject, jobject> retMap;
|
||||||
|
retMap[env->NewStringUTF("__js_boolean__")] = jniWrapPrimity<jlong>(env, (int64_t)obj);
|
||||||
|
return jniWrapMap(env, retMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
jobject jniWrapPrimity(JNIEnv *env, jdouble obj)
|
||||||
|
{
|
||||||
|
jclass jclazz = env->FindClass("java/lang/Double");
|
||||||
|
jmethodID jmethod = env->GetMethodID(jclazz, "<init>", "(D)V");
|
||||||
|
return env->NewObject(jclazz, jmethod, obj);
|
||||||
|
}
|
@@ -3,13 +3,9 @@
|
|||||||
* @Author: ekibun
|
* @Author: ekibun
|
||||||
* @Date: 2020-08-09 18:16:11
|
* @Date: 2020-08-09 18:16:11
|
||||||
* @LastEditors: ekibun
|
* @LastEditors: ekibun
|
||||||
* @LastEditTime: 2020-08-12 23:37:28
|
* @LastEditTime: 2020-08-16 19:00:06
|
||||||
*/
|
*/
|
||||||
#include <jni.h>
|
#include "java_js_wrapper.hpp"
|
||||||
#include <string>
|
|
||||||
#include "../../../../cxx/js_engine.hpp"
|
|
||||||
|
|
||||||
qjs::Engine *engine = nullptr;
|
|
||||||
|
|
||||||
JNIEnv *getEnv(JavaVM *gJvm)
|
JNIEnv *getEnv(JavaVM *gJvm)
|
||||||
{
|
{
|
||||||
@@ -26,48 +22,41 @@ JNIEnv *getEnv(JavaVM *gJvm)
|
|||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
void jniResultResolve(JavaVM *jvm, jobject result, std::string data)
|
void jniResultResolve(JNIEnv *env, jobject result, jobject data)
|
||||||
{
|
{
|
||||||
JNIEnv *env = getEnv(jvm);
|
|
||||||
jclass jclazz = env->GetObjectClass(result);
|
jclass jclazz = env->GetObjectClass(result);
|
||||||
jmethodID jmethod = env->GetMethodID(jclazz, "success", "(Ljava/lang/String;)V");
|
jmethodID jmethod = env->GetMethodID(jclazz, "success", "(Ljava/lang/Object;)V");
|
||||||
jstring jdata = env->NewStringUTF(data.c_str());
|
env->CallVoidMethod(result, jmethod, data);
|
||||||
env->CallVoidMethod(result, jmethod, jdata);
|
env->DeleteLocalRef(data);
|
||||||
env->DeleteLocalRef(jdata);
|
|
||||||
env->DeleteGlobalRef(result);
|
env->DeleteGlobalRef(result);
|
||||||
jvm->DetachCurrentThread();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void jniResultReject(JavaVM *jvm, jobject result, std::string reason)
|
void jniResultReject(JNIEnv *env, jobject result, std::string reason)
|
||||||
{
|
{
|
||||||
JNIEnv *env = getEnv(jvm);
|
|
||||||
jclass jclazz = env->GetObjectClass(result);
|
jclass jclazz = env->GetObjectClass(result);
|
||||||
jmethodID jmethod = env->GetMethodID(jclazz, "error", "(Ljava/lang/String;)V");
|
jmethodID jmethod = env->GetMethodID(jclazz, "error", "(Ljava/lang/String;)V");
|
||||||
jstring jreason = env->NewStringUTF(reason.c_str());
|
jstring jreason = env->NewStringUTF(reason.c_str());
|
||||||
env->CallVoidMethod(result, jmethod, jreason);
|
env->CallVoidMethod(result, jmethod, jreason);
|
||||||
env->DeleteLocalRef(jreason);
|
env->DeleteLocalRef(jreason);
|
||||||
env->DeleteGlobalRef(result);
|
env->DeleteGlobalRef(result);
|
||||||
jvm->DetachCurrentThread();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void jniChannelInvoke(JavaVM *jvm, jobject channel, std::promise<qjs::JSFutureReturn> *promise, std::string method, std::string argv)
|
void jniChannelInvoke(JNIEnv *env, jobject channel, std::promise<qjs::JSFutureReturn> *promise, std::string method, qjs::Value args, qjs::Engine *engine)
|
||||||
{
|
{
|
||||||
JNIEnv *env = nullptr;
|
|
||||||
jvm->GetEnv((void **)&env, JNI_VERSION_1_2);
|
|
||||||
jvm->AttachCurrentThread(&env, NULL);
|
|
||||||
jclass jclazz = env->GetObjectClass(channel);
|
jclass jclazz = env->GetObjectClass(channel);
|
||||||
jmethodID jmethod = env->GetMethodID(jclazz, "invokeMethod", "(Ljava/lang/String;Ljava/lang/String;J)V");
|
jmethodID jmethod = env->GetMethodID(jclazz, "invokeMethod", "(Ljava/lang/String;Ljava/lang/Object;J)V");
|
||||||
jstring jstrmethod = env->NewStringUTF(method.c_str());
|
jstring jstrmethod = env->NewStringUTF(method.c_str());
|
||||||
jstring jstrargv = env->NewStringUTF(argv.c_str());
|
std::map<jobject, jobject> retMap;
|
||||||
|
retMap[env->NewStringUTF("engine")] = jniWrapPrimity<jlong>(env, (int64_t) engine);
|
||||||
env->CallVoidMethod(channel, jmethod, jstrmethod, jstrargv, (jlong)promise);
|
retMap[env->NewStringUTF("args")] = qjs::jsToJava(env, args);
|
||||||
|
jobject jsargs = jniWrapMap(env, retMap);
|
||||||
|
env->CallVoidMethod(channel, jmethod, jstrmethod, jsargs, (jlong)promise);
|
||||||
env->DeleteLocalRef(jstrmethod);
|
env->DeleteLocalRef(jstrmethod);
|
||||||
env->DeleteLocalRef(jstrargv);
|
env->DeleteLocalRef(jsargs);
|
||||||
jvm->DetachCurrentThread();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT jint JNICALL
|
extern "C" JNIEXPORT jlong JNICALL
|
||||||
Java_soko_ekibun_flutter_1qjs_JniBridge_initEngine(
|
Java_soko_ekibun_flutter_1qjs_JniBridge_createEngine(
|
||||||
JNIEnv *env,
|
JNIEnv *env,
|
||||||
jobject thiz,
|
jobject thiz,
|
||||||
jobject channel)
|
jobject channel)
|
||||||
@@ -75,10 +64,12 @@ Java_soko_ekibun_flutter_1qjs_JniBridge_initEngine(
|
|||||||
JavaVM *jvm = nullptr;
|
JavaVM *jvm = nullptr;
|
||||||
env->GetJavaVM(&jvm);
|
env->GetJavaVM(&jvm);
|
||||||
jobject gchannel = env->NewGlobalRef(channel);
|
jobject gchannel = env->NewGlobalRef(channel);
|
||||||
engine = new qjs::Engine([jvm, gchannel](std::string name, std::string args) {
|
qjs::Engine *engine = new qjs::Engine([jvm, gchannel](std::string name, qjs::Value args, qjs::Engine *engine) {
|
||||||
auto promise = new std::promise<qjs::JSFutureReturn>();
|
auto promise = new std::promise<qjs::JSFutureReturn>();
|
||||||
jniChannelInvoke(jvm, gchannel, promise, name, args);
|
JNIEnv *env = getEnv(jvm);
|
||||||
return promise->get_future();
|
jniChannelInvoke(env, gchannel, promise, name, args, engine);
|
||||||
|
jvm->DetachCurrentThread();
|
||||||
|
return promise;
|
||||||
});
|
});
|
||||||
return (jlong)engine;
|
return (jlong)engine;
|
||||||
}
|
}
|
||||||
@@ -87,6 +78,7 @@ extern "C" JNIEXPORT void JNICALL
|
|||||||
Java_soko_ekibun_flutter_1qjs_JniBridge_evaluate(
|
Java_soko_ekibun_flutter_1qjs_JniBridge_evaluate(
|
||||||
JNIEnv *env,
|
JNIEnv *env,
|
||||||
jobject thiz,
|
jobject thiz,
|
||||||
|
jlong engine,
|
||||||
jstring script,
|
jstring script,
|
||||||
jstring name,
|
jstring name,
|
||||||
jobject result)
|
jobject result)
|
||||||
@@ -94,33 +86,86 @@ Java_soko_ekibun_flutter_1qjs_JniBridge_evaluate(
|
|||||||
JavaVM *jvm = nullptr;
|
JavaVM *jvm = nullptr;
|
||||||
env->GetJavaVM(&jvm);
|
env->GetJavaVM(&jvm);
|
||||||
jobject gresult = env->NewGlobalRef(result);
|
jobject gresult = env->NewGlobalRef(result);
|
||||||
engine->commit(qjs::EngineTask{
|
((qjs::Engine *)engine)->commit(qjs::EngineTask{
|
||||||
env->GetStringUTFChars(script, 0),
|
[script = std::string(env->GetStringUTFChars(script, 0)),
|
||||||
env->GetStringUTFChars(name, 0),
|
name = std::string(env->GetStringUTFChars(name, 0))](qjs::Context &ctx) {
|
||||||
[jvm, gresult](std::string resolve) {
|
return ctx.eval(script, name.c_str(), JS_EVAL_TYPE_GLOBAL);
|
||||||
jniResultResolve(jvm, gresult, resolve);
|
|
||||||
},
|
},
|
||||||
[jvm, gresult](std::string reject) {
|
[jvm, gresult](qjs::Value resolve) {
|
||||||
jniResultReject(jvm, gresult, reject);
|
JNIEnv *env = getEnv(jvm);
|
||||||
|
jniResultResolve(env, gresult, qjs::jsToJava(env, resolve));
|
||||||
|
jvm->DetachCurrentThread();
|
||||||
|
},
|
||||||
|
[jvm, gresult](qjs::Value reject) {
|
||||||
|
JNIEnv *env = getEnv(jvm);
|
||||||
|
jniResultReject(env, gresult, qjs::getStackTrack(reject));
|
||||||
|
jvm->DetachCurrentThread();
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
extern "C" JNIEXPORT void JNICALL
|
||||||
Java_soko_ekibun_flutter_1qjs_JniBridge_close(
|
Java_soko_ekibun_flutter_1qjs_JniBridge_close(
|
||||||
JNIEnv *env,
|
JNIEnv *env,
|
||||||
jobject /* this */)
|
jobject thiz,
|
||||||
|
jlong engine)
|
||||||
{
|
{
|
||||||
delete engine;
|
delete (qjs::Engine *)engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL
|
||||||
|
Java_soko_ekibun_flutter_1qjs_JniBridge_call(
|
||||||
|
JNIEnv *env,
|
||||||
|
jobject thiz,
|
||||||
|
jlong engine,
|
||||||
|
jlong function,
|
||||||
|
jobject args,
|
||||||
|
jobject result)
|
||||||
|
{
|
||||||
|
JavaVM *jvm = nullptr;
|
||||||
|
env->GetJavaVM(&jvm);
|
||||||
|
jobject gresult = env->NewGlobalRef(result);
|
||||||
|
jobject gargs = env->NewGlobalRef(args);
|
||||||
|
((qjs::Engine *)engine)->commit(qjs::EngineTask{
|
||||||
|
[jvm, function = (qjs::JSValue *)function, gargs](qjs::Context &ctx) {
|
||||||
|
JNIEnv *env = getEnv(jvm);
|
||||||
|
jobjectArray array = jniToArray(env, gargs);
|
||||||
|
jsize argscount = env->GetArrayLength(array);
|
||||||
|
qjs::JSValue *callargs = new qjs::JSValue[argscount];
|
||||||
|
for (jsize i = 0; i < argscount; i++)
|
||||||
|
{
|
||||||
|
callargs[i] = qjs::javaToJs(ctx.ctx, env, env->GetObjectArrayElement(array, i));
|
||||||
|
}
|
||||||
|
jvm->DetachCurrentThread();
|
||||||
|
qjs::JSValue ret = JS_Call(ctx.ctx, *function, ctx.global(), (int)argscount, callargs);
|
||||||
|
qjs::JS_FreeValue(ctx.ctx, *function);
|
||||||
|
if (qjs::JS_IsException(ret))
|
||||||
|
throw qjs::exception{};
|
||||||
|
return qjs::Value{ctx.ctx, ret};
|
||||||
|
},
|
||||||
|
[jvm, gresult](qjs::Value resolve) {
|
||||||
|
JNIEnv *env = getEnv(jvm);
|
||||||
|
jniResultResolve(env, gresult, qjs::jsToJava(env, resolve));
|
||||||
|
jvm->DetachCurrentThread();
|
||||||
|
},
|
||||||
|
[jvm, gresult](qjs::Value reject) {
|
||||||
|
JNIEnv *env = getEnv(jvm);
|
||||||
|
jniResultReject(env, gresult, qjs::getStackTrack(reject));
|
||||||
|
jvm->DetachCurrentThread();
|
||||||
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
extern "C" JNIEXPORT void JNICALL
|
||||||
Java_soko_ekibun_flutter_1qjs_JniBridge_resolve(
|
Java_soko_ekibun_flutter_1qjs_JniBridge_resolve(
|
||||||
JNIEnv *env,
|
JNIEnv *env,
|
||||||
jobject clazz,
|
jobject clazz,
|
||||||
jlong promise, jstring data)
|
jlong promise, jobject data)
|
||||||
{
|
{
|
||||||
((std::promise<qjs::JSFutureReturn> *)promise)->set_value((qjs::JSFutureReturn)[data = std::string(env->GetStringUTFChars(data, 0))](qjs::JSContext * ctx) {
|
JavaVM *jvm = nullptr;
|
||||||
qjs::JSValue *ret = new qjs::JSValue{JS_NewString(ctx, data.c_str())};
|
env->GetJavaVM(&jvm);
|
||||||
|
jobject gdata = env->NewGlobalRef(data);
|
||||||
|
((std::promise<qjs::JSFutureReturn> *)promise)->set_value((qjs::JSFutureReturn)[jvm, gdata](qjs::JSContext * ctx) {
|
||||||
|
JNIEnv *env = getEnv(jvm);
|
||||||
|
qjs::JSValue *ret = new qjs::JSValue{qjs::javaToJs(ctx, env, gdata)};
|
||||||
return qjs::JSOSFutureArgv{1, ret};
|
return qjs::JSOSFutureArgv{1, ret};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,6 @@ class FlutterQjsPlugin: FlutterPlugin, MethodCallHandler {
|
|||||||
/// when the Flutter Engine is detached from the Activity
|
/// when the Flutter Engine is detached from the Activity
|
||||||
private lateinit var channelwrapper : MethodChannelWrapper
|
private lateinit var channelwrapper : MethodChannelWrapper
|
||||||
private lateinit var channel : MethodChannel
|
private lateinit var channel : MethodChannel
|
||||||
private var engine : JniBridge? = null
|
|
||||||
private lateinit var applicationContext: android.content.Context
|
private lateinit var applicationContext: android.content.Context
|
||||||
private val handler by lazy { Handler() }
|
private val handler by lazy { Handler() }
|
||||||
|
|
||||||
@@ -28,20 +27,24 @@ class FlutterQjsPlugin: FlutterPlugin, MethodCallHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onMethodCall(call: MethodCall, result: Result) {
|
override fun onMethodCall(call: MethodCall, result: Result) {
|
||||||
if (call.method == "initEngine") {
|
if (call.method == "createEngine") {
|
||||||
// engine = JsEngine(channel)
|
val engine: Long = JniBridge.instance.createEngine(channelwrapper)
|
||||||
engine = JniBridge()
|
println(engine)
|
||||||
engine?.initEngine(channelwrapper)
|
result.success(engine)
|
||||||
result.success(1)
|
|
||||||
} else if (call.method == "evaluate") {
|
} else if (call.method == "evaluate") {
|
||||||
|
val engine: Long = call.argument<Long>("engine")!!
|
||||||
val script: String = call.argument<String>("script")!!
|
val script: String = call.argument<String>("script")!!
|
||||||
val name: String = call.argument<String>("name")!!
|
val name: String = call.argument<String>("name")!!
|
||||||
engine?.evaluate(script, name, ResultWrapper(handler, result))
|
JniBridge.instance.evaluate(engine, script, name, ResultWrapper(handler, result))
|
||||||
// engine?.evaluate(script, result)
|
} else if (call.method == "call") {
|
||||||
|
println(call.arguments<Map<*, *>>());
|
||||||
|
val engine: Long = call.argument<Long>("engine")!!
|
||||||
|
val function: Long = call.argument<Long>("function")!!
|
||||||
|
val args: List<Any> = call.argument<List<Any>>("arguments")!!
|
||||||
|
JniBridge.instance.call(engine, function, args, ResultWrapper(handler, result))
|
||||||
} else if (call.method == "close") {
|
} else if (call.method == "close") {
|
||||||
// engine?.release()
|
val engine: Long = call.argument<Long>("engine")!!
|
||||||
engine?.close()
|
JniBridge.instance.close(engine)
|
||||||
engine = null
|
|
||||||
result.success(null)
|
result.success(null)
|
||||||
} else {
|
} else {
|
||||||
result.notImplemented()
|
result.notImplemented()
|
||||||
|
@@ -10,11 +10,13 @@ class JniBridge {
|
|||||||
val instance by lazy { JniBridge() }
|
val instance by lazy { JniBridge() }
|
||||||
}
|
}
|
||||||
|
|
||||||
external fun initEngine(channel: MethodChannelWrapper): Int
|
external fun createEngine(channel: MethodChannelWrapper): Long
|
||||||
|
|
||||||
external fun evaluate(script: String, name: String, result: ResultWrapper)
|
external fun evaluate(engine: Long, script: String, name: String, result: ResultWrapper)
|
||||||
|
|
||||||
external fun close()
|
external fun close(engine: Long)
|
||||||
|
|
||||||
|
external fun call(engine: Long, function: Long, args: List<Any>, result: ResultWrapper)
|
||||||
|
|
||||||
external fun reject(promise: Long, reason: String)
|
external fun reject(promise: Long, reason: String)
|
||||||
|
|
||||||
|
@@ -1,80 +0,0 @@
|
|||||||
package soko.ekibun.flutter_qjs
|
|
||||||
|
|
||||||
// import android.util.Log
|
|
||||||
// import de.prosiebensat1digital.oasisjsbridge.*
|
|
||||||
import io.flutter.plugin.common.MethodChannel
|
|
||||||
// import kotlinx.coroutines.*
|
|
||||||
|
|
||||||
class JsEngine(private val channel: MethodChannel) {
|
|
||||||
// private val jsBridge = JsBridge(JsBridgeConfig.bareConfig())
|
|
||||||
|
|
||||||
// fun processPromiseQueue() {
|
|
||||||
// JsBridge::class.java.getDeclaredMethod("processPromiseQueue").invoke(jsBridge)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private fun dartInteract(resolve: (String) -> Unit, reject: (String) -> Unit, method: String, args: String) {
|
|
||||||
// println("dart: $method")
|
|
||||||
// jsBridge.launch(Dispatchers.Main) {
|
|
||||||
// channel.invokeMethod(method, args, object : MethodChannel.Result {
|
|
||||||
// override fun notImplemented() {
|
|
||||||
// println("dart error: notImplemented")
|
|
||||||
// jsBridge.launch(Dispatchers.Main) {
|
|
||||||
// reject("notImplemented")
|
|
||||||
// withContext(jsBridge.coroutineContext) {
|
|
||||||
// processPromiseQueue()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// override fun error(error_code: String?, error_message: String?, error_details: Any?) {
|
|
||||||
// println("dart error: ${error_message ?: "undefined"}")
|
|
||||||
// jsBridge.launch(Dispatchers.Main) {
|
|
||||||
// reject(error_message ?: "undefined")
|
|
||||||
// withContext(jsBridge.coroutineContext) {
|
|
||||||
// processPromiseQueue()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// override fun success(result: Any?) {
|
|
||||||
// println("dart success: $result")
|
|
||||||
// jsBridge.launch(Dispatchers.Main) {
|
|
||||||
// resolve(result.toString())
|
|
||||||
// withContext(jsBridge.coroutineContext) {
|
|
||||||
// processPromiseQueue()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// init {
|
|
||||||
// // jsBridge.initEngine()
|
|
||||||
// jsBridge.launch(Dispatchers.Main) {
|
|
||||||
// JsValue.fromNativeFunction4(jsBridge, ::dartInteract).assignToGlobal("__DartImpl__invoke")
|
|
||||||
// jsBridge.evaluateAsync<Deferred<String>>("""
|
|
||||||
// this.dart = (method, ...args) => new Promise((res, rej) =>
|
|
||||||
// this.__DartImpl__invoke((v) => res(JSON.parse(v)), rej, method, JSON.stringify(args)))
|
|
||||||
// """).await().await()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fun eval(script: String, result: MethodChannel.Result) {
|
|
||||||
// jsBridge.launch(Dispatchers.Main) {
|
|
||||||
// try {
|
|
||||||
// var ret = jsBridge.evaluateAsync<Deferred<String>>(script).await().await()
|
|
||||||
// println(ret)
|
|
||||||
// result.success(ret)
|
|
||||||
// } catch (e: Throwable) {
|
|
||||||
// e.printStackTrace()
|
|
||||||
// result.error("FlutterJSException", Log.getStackTraceString(e), null)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fun release() {
|
|
||||||
// jsBridge.release()
|
|
||||||
// }
|
|
||||||
}
|
|
@@ -7,7 +7,7 @@ import io.flutter.plugin.common.MethodChannel
|
|||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
class MethodChannelWrapper(private val handler: Handler, private val channel: MethodChannel) {
|
class MethodChannelWrapper(private val handler: Handler, private val channel: MethodChannel) {
|
||||||
fun invokeMethod(method: String, arguments: String, promise: Long) {
|
fun invokeMethod(method: String, arguments: Any?, promise: Long) {
|
||||||
handler.post {
|
handler.post {
|
||||||
channel.invokeMethod(method, arguments, object : MethodChannel.Result {
|
channel.invokeMethod(method, arguments, object : MethodChannel.Result {
|
||||||
override fun notImplemented() {
|
override fun notImplemented() {
|
||||||
@@ -19,7 +19,7 @@ class MethodChannelWrapper(private val handler: Handler, private val channel: Me
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun success(data: Any?) {
|
override fun success(data: Any?) {
|
||||||
JniBridge.instance.resolve(promise, data.toString())
|
JniBridge.instance.resolve(promise, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@@ -9,7 +9,7 @@ import io.flutter.plugin.common.MethodChannel.Result
|
|||||||
class ResultWrapper(private val handler: Handler, private val result: Result) {
|
class ResultWrapper(private val handler: Handler, private val result: Result) {
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
fun success(dat: String) {
|
fun success(dat: Any?) {
|
||||||
handler.post { result.success(dat) }
|
handler.post { result.success(dat) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
project(flutter_qjs_example LANGUAGES CXX)
|
project(example LANGUAGES CXX)
|
||||||
|
|
||||||
set(BINARY_NAME "flutter_qjs_example")
|
set(BINARY_NAME "example")
|
||||||
|
|
||||||
cmake_policy(SET CMP0063 NEW)
|
cmake_policy(SET CMP0063 NEW)
|
||||||
|
|
||||||
|
@@ -1 +1 @@
|
|||||||
4
|
5
|
||||||
|
@@ -43,6 +43,7 @@ list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
|
|||||||
)
|
)
|
||||||
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
|
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
|
||||||
list(APPEND CPP_WRAPPER_SOURCES_APP
|
list(APPEND CPP_WRAPPER_SOURCES_APP
|
||||||
|
"flutter_engine.cc"
|
||||||
"flutter_view_controller.cc"
|
"flutter_view_controller.cc"
|
||||||
)
|
)
|
||||||
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
|
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
|
||||||
@@ -79,11 +80,13 @@ add_dependencies(flutter_wrapper_app flutter_assemble)
|
|||||||
# _phony_ is a non-existent file to force this command to run every time,
|
# _phony_ is a non-existent file to force this command to run every time,
|
||||||
# since currently there's no way to get a full input/output list from the
|
# since currently there's no way to get a full input/output list from the
|
||||||
# flutter tool.
|
# flutter tool.
|
||||||
|
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
|
||||||
|
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
|
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
|
||||||
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
|
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||||
${CPP_WRAPPER_SOURCES_APP}
|
${CPP_WRAPPER_SOURCES_APP}
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/_phony_
|
${PHONY_OUTPUT}
|
||||||
COMMAND ${CMAKE_COMMAND} -E env
|
COMMAND ${CMAKE_COMMAND} -E env
|
||||||
${FLUTTER_TOOL_ENVIRONMENT}
|
${FLUTTER_TOOL_ENVIRONMENT}
|
||||||
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
|
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
|
||||||
|
@@ -8,15 +8,22 @@ FlutterWindow::FlutterWindow(RunLoop* run_loop,
|
|||||||
|
|
||||||
FlutterWindow::~FlutterWindow() {}
|
FlutterWindow::~FlutterWindow() {}
|
||||||
|
|
||||||
void FlutterWindow::OnCreate() {
|
bool FlutterWindow::OnCreate() {
|
||||||
Win32Window::OnCreate();
|
if (!Win32Window::OnCreate()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// The size here is arbitrary since SetChildContent will resize it.
|
// The size here is arbitrary since SetChildContent will resize it.
|
||||||
flutter_controller_ =
|
flutter_controller_ =
|
||||||
std::make_unique<flutter::FlutterViewController>(100, 100, project_);
|
std::make_unique<flutter::FlutterViewController>(100, 100, project_);
|
||||||
|
// Ensure that basic setup of the controller was successful.
|
||||||
|
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
RegisterPlugins(flutter_controller_.get());
|
RegisterPlugins(flutter_controller_.get());
|
||||||
run_loop_->RegisterFlutterInstance(flutter_controller_.get());
|
run_loop_->RegisterFlutterInstance(flutter_controller_.get());
|
||||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlutterWindow::OnDestroy() {
|
void FlutterWindow::OnDestroy() {
|
||||||
|
@@ -20,7 +20,7 @@ class FlutterWindow : public Win32Window {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Win32Window:
|
// Win32Window:
|
||||||
void OnCreate() override;
|
bool OnCreate() override;
|
||||||
void OnDestroy() override;
|
void OnDestroy() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -122,9 +122,11 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
|
|||||||
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
||||||
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
||||||
|
|
||||||
OnCreate();
|
if (!window) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return window != nullptr;
|
return OnCreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@@ -240,8 +242,9 @@ void Win32Window::SetQuitOnClose(bool quit_on_close) {
|
|||||||
quit_on_close_ = quit_on_close;
|
quit_on_close_ = quit_on_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Win32Window::OnCreate() {
|
bool Win32Window::OnCreate() {
|
||||||
// No-op; provided for subclasses.
|
// No-op; provided for subclasses.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Win32Window::OnDestroy() {
|
void Win32Window::OnDestroy() {
|
||||||
|
@@ -62,8 +62,8 @@ class Win32Window {
|
|||||||
LPARAM const lparam) noexcept;
|
LPARAM const lparam) noexcept;
|
||||||
|
|
||||||
// Called when CreateAndShow is called, allowing subclass window-related
|
// Called when CreateAndShow is called, allowing subclass window-related
|
||||||
// setup.
|
// setup. Subclasses should return false if setup fails.
|
||||||
virtual void OnCreate();
|
virtual bool OnCreate();
|
||||||
|
|
||||||
// Called when Destroy is called.
|
// Called when Destroy is called.
|
||||||
virtual void OnDestroy();
|
virtual void OnDestroy();
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#include "window_configuration.h"
|
#include "window_configuration.h"
|
||||||
|
|
||||||
const wchar_t* kFlutterWindowTitle = L"flutter_qjs_example";
|
const wchar_t* kFlutterWindowTitle = L"example";
|
||||||
const unsigned int kFlutterWindowOriginX = 10;
|
const unsigned int kFlutterWindowOriginX = 10;
|
||||||
const unsigned int kFlutterWindowOriginY = 10;
|
const unsigned int kFlutterWindowOriginY = 10;
|
||||||
const unsigned int kFlutterWindowWidth = 1280;
|
const unsigned int kFlutterWindowWidth = 1280;
|
||||||
|
@@ -3,9 +3,10 @@
|
|||||||
* @Author: ekibun
|
* @Author: ekibun
|
||||||
* @Date: 2020-08-08 08:29:09
|
* @Date: 2020-08-08 08:29:09
|
||||||
* @LastEditors: ekibun
|
* @LastEditors: ekibun
|
||||||
* @LastEditTime: 2020-08-15 16:21:56
|
* @LastEditTime: 2020-08-16 19:10:47
|
||||||
*/
|
*/
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
typedef JsMethodHandler = Future<dynamic> Function(String method, List args);
|
typedef JsMethodHandler = Future<dynamic> Function(String method, List args);
|
||||||
@@ -24,24 +25,28 @@ class _FlutterJs {
|
|||||||
print(methodHandlers.entries);
|
print(methodHandlers.entries);
|
||||||
print(methodHandlers[engine]);
|
print(methodHandlers[engine]);
|
||||||
if (methodHandlers[engine] == null) return call.noSuchMethod(null);
|
if (methodHandlers[engine] == null) return call.noSuchMethod(null);
|
||||||
return await methodHandlers[engine](call.method, _wrapFunctionArguments(args));
|
return await methodHandlers[engine](call.method, _wrapFunctionArguments(args, engine));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
dynamic _wrapFunctionArguments(dynamic val) {
|
dynamic _wrapFunctionArguments(dynamic val, dynamic engine) {
|
||||||
if (val is List) {
|
if (val is List && !(val is List<int>)) {
|
||||||
for (var i = 0; i < val.length; ++i) {
|
for (var i = 0; i < val.length; ++i) {
|
||||||
val[i] = _wrapFunctionArguments(val[i]);
|
val[i] = _wrapFunctionArguments(val[i], engine);
|
||||||
}
|
}
|
||||||
} else if (val is Map) {
|
} else if (val is Map) {
|
||||||
if (val["__js_function__"] != 0) {
|
// wrap boolean in Android see https://github.com/flutter/flutter/issues/45066
|
||||||
|
if (Platform.isAndroid && val["__js_boolean__"] != null) {
|
||||||
|
return val["__js_boolean__"] != 0;
|
||||||
|
}
|
||||||
|
if (val["__js_function__"] != null) {
|
||||||
var functionId = val["__js_function__"];
|
var functionId = val["__js_function__"];
|
||||||
return (List<dynamic> args) async {
|
return (List<dynamic> args) async {
|
||||||
var arguments = {"function": functionId, "arguments": args};
|
var arguments = {"engine": engine, "function": functionId, "arguments": args};
|
||||||
return await _channel.invokeMethod("call", arguments);
|
return _wrapFunctionArguments(await _channel.invokeMethod("call", arguments), engine);
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
for (var key in val.keys) {
|
for (var key in val.keys) {
|
||||||
val[key] = _wrapFunctionArguments(val[key]);
|
val[key] = _wrapFunctionArguments(val[key], engine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
@@ -80,6 +85,6 @@ class FlutterJs {
|
|||||||
ensureEngine();
|
ensureEngine();
|
||||||
var arguments = {"engine": _engine, "script": command, "name": command};
|
var arguments = {"engine": _engine, "script": command, "name": command};
|
||||||
return _FlutterJs.instance._wrapFunctionArguments(
|
return _FlutterJs.instance._wrapFunctionArguments(
|
||||||
await _FlutterJs.instance._channel.invokeMethod("evaluate", arguments));
|
await _FlutterJs.instance._channel.invokeMethod("evaluate", arguments), _engine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
* @Author: ekibun
|
* @Author: ekibun
|
||||||
* @Date: 2020-08-14 21:45:02
|
* @Date: 2020-08-14 21:45:02
|
||||||
* @LastEditors: ekibun
|
* @LastEditors: ekibun
|
||||||
* @LastEditTime: 2020-08-15 15:42:55
|
* @LastEditTime: 2020-08-16 19:52:35
|
||||||
*/
|
*/
|
||||||
#include "../cxx/js_engine.hpp"
|
#include "../cxx/js_engine.hpp"
|
||||||
#include <flutter/standard_method_codec.h>
|
#include <flutter/standard_method_codec.h>
|
||||||
@@ -32,8 +32,12 @@ namespace std
|
|||||||
|
|
||||||
namespace qjs
|
namespace qjs
|
||||||
{
|
{
|
||||||
JSValue dartToJsAtom(JSContext *ctx, flutter::EncodableValue val)
|
JSValue dartToJs(JSContext *ctx, flutter::EncodableValue val, std::unordered_map<flutter::EncodableValue, JSValue> cache = std::unordered_map<flutter::EncodableValue, JSValue>())
|
||||||
{
|
{
|
||||||
|
if (val.IsNull())
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
if (cache.find(val) != cache.end())
|
||||||
|
return cache[val];
|
||||||
if (std::holds_alternative<bool>(val))
|
if (std::holds_alternative<bool>(val))
|
||||||
return JS_NewBool(ctx, std::get<bool>(val));
|
return JS_NewBool(ctx, std::get<bool>(val));
|
||||||
if (std::holds_alternative<int32_t>(val))
|
if (std::holds_alternative<int32_t>(val))
|
||||||
@@ -44,20 +48,6 @@ namespace qjs
|
|||||||
return JS_NewFloat64(ctx, std::get<double>(val));
|
return JS_NewFloat64(ctx, std::get<double>(val));
|
||||||
if (std::holds_alternative<std::string>(val))
|
if (std::holds_alternative<std::string>(val))
|
||||||
return JS_NewString(ctx, std::get<std::string>(val).c_str());
|
return JS_NewString(ctx, std::get<std::string>(val).c_str());
|
||||||
return JS_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue dartToJs(JSContext *ctx, flutter::EncodableValue val, std::unordered_map<flutter::EncodableValue, JSValue> cache = std::unordered_map<flutter::EncodableValue, JSValue>())
|
|
||||||
{
|
|
||||||
if (val.IsNull())
|
|
||||||
return JS_UNDEFINED;
|
|
||||||
if (cache.find(val) != cache.end())
|
|
||||||
return cache[val];
|
|
||||||
{
|
|
||||||
JSValue atomValue = dartToJsAtom(ctx, val);
|
|
||||||
if (!JS_IsUndefined(atomValue))
|
|
||||||
return atomValue;
|
|
||||||
}
|
|
||||||
if (std::holds_alternative<std::vector<uint8_t>>(val))
|
if (std::holds_alternative<std::vector<uint8_t>>(val))
|
||||||
{
|
{
|
||||||
auto buf = std::get<std::vector<uint8_t>>(val);
|
auto buf = std::get<std::vector<uint8_t>>(val);
|
||||||
@@ -117,8 +107,17 @@ namespace qjs
|
|||||||
return cache[val];
|
return cache[val];
|
||||||
if (JS_IsBool(val.v))
|
if (JS_IsBool(val.v))
|
||||||
return (bool)val;
|
return (bool)val;
|
||||||
if (JS_IsNumber(val.v))
|
{
|
||||||
|
int tag = JS_VALUE_GET_TAG(val.v);
|
||||||
|
if (tag == JS_TAG_INT)
|
||||||
|
{
|
||||||
|
return (int64_t)val;
|
||||||
|
}
|
||||||
|
else if (JS_TAG_IS_FLOAT64(tag))
|
||||||
|
{
|
||||||
return (double)val;
|
return (double)val;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (JS_IsString(val.v))
|
if (JS_IsString(val.v))
|
||||||
return (std::string)val;
|
return (std::string)val;
|
||||||
{ // ArrayBuffer
|
{ // ArrayBuffer
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
#ifndef FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_
|
#ifndef FLUTTER_PLUGIN_FLUTTER_QJS_PLUGIN_H_
|
||||||
#define FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_
|
#define FLUTTER_PLUGIN_FLUTTER_QJS_PLUGIN_H_
|
||||||
|
|
||||||
#include <flutter_plugin_registrar.h>
|
#include <flutter_plugin_registrar.h>
|
||||||
|
|
||||||
@@ -20,4 +20,4 @@ FLUTTER_PLUGIN_EXPORT void FlutterQjsPluginRegisterWithRegistrar(
|
|||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_
|
#endif // FLUTTER_PLUGIN_FLUTTER_QJS_PLUGIN_H_
|
||||||
|
Reference in New Issue
Block a user