Skip to content

Commit bea8f9c

Browse files
committed
Merge branch 'release/0.3.5'
2 parents d5e6a0c + dcc4a9f commit bea8f9c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+4861
-476
lines changed

dart_native/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.3.5
2+
3+
* [Feature] Object lifecycle management for Android.
4+
* [Feature] Callback from Android to Flutter.
5+
16
## 0.3.4
27

38
* Performance optimization for iOS `NSString`.

dart_native/example/android/app/CMakeLists.txt renamed to dart_native/android/CMakeLists.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
cmake_minimum_required(VERSION 3.4.1)
66

7-
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
7+
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../android/jniLibs/${ANDROID_ABI})
88

99
# Specifies a library name, specifies whether the library is STATIC or
1010
# SHARED, and provides relative paths to the source code. You can
@@ -19,7 +19,8 @@ add_library( # Specifies the name of the library.
1919
SHARED
2020

2121
# Provides a relative path to your source file(s).
22-
../../../android/src/main/jni/dart_native.cpp )
22+
src/main/jni/include/dart_api_dl.c
23+
src/main/jni/dart_native.cpp )
2324

2425
find_library( # Sets the name of the path variable.
2526
log-lib
@@ -33,4 +34,6 @@ add_library( # Specifies the name of the library.
3334

3435
# Links the target library to the log library
3536
# included in the NDK.
36-
${log-lib} )
37+
${log-lib} )
38+
39+
include_directories(src/main/jni/include)

dart_native/android/build.gradle

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ apply plugin: 'com.android.library'
2424
android {
2525
compileSdkVersion 28
2626

27+
// Encapsulates your external native build configurations.
28+
externalNativeBuild {
29+
30+
// Encapsulates your CMake build configurations.
31+
cmake {
32+
// Provides a relative path to your CMake build script.
33+
path "CMakeLists.txt"
34+
}
35+
}
36+
2737
defaultConfig {
2838
minSdkVersion 16
2939
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.dartnative.dart_native;
2+
3+
import android.util.Log;
4+
5+
import java.lang.reflect.InvocationHandler;
6+
import java.lang.reflect.Method;
7+
import java.util.HashMap;
8+
9+
/**
10+
* Created by huizzzhou on 2020/11/11.
11+
*/
12+
public class CallbackInvocationHandler implements InvocationHandler {
13+
private static final String TAG = "CallbackHandler";
14+
15+
private static HashMap<Class<?>, String> sTypeConvert = new HashMap<Class<?>, String>() {{
16+
put(int.class, "I");
17+
put(float.class, "F");
18+
put(double.class, "D");
19+
put(String.class, "Ljava/lang/String;");
20+
}};
21+
22+
@Override
23+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
24+
Log.d(TAG, "invoke method: " + method.getName());
25+
Class<?>[] paramTypes = method.getParameterTypes();
26+
String[] params = new String[paramTypes.length];
27+
for (int i = 0; i < paramTypes.length; i++) {
28+
params[i] = sTypeConvert.get(paramTypes[i]);
29+
}
30+
String funName = method.getName();
31+
long dartObjectAddr = CallbackManager.getInstance().getRegisterDartAddr(proxy);
32+
hookCallback(dartObjectAddr, funName, paramTypes.length, params, args);
33+
return proxy;
34+
}
35+
36+
static native void hookCallback(long dartObjectAddr, String funName, int argCount, String[] argTypes, Object[] args);
37+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.dartnative.dart_native;
2+
3+
import android.util.Log;
4+
5+
import java.lang.reflect.Proxy;
6+
import java.util.HashMap;
7+
8+
import androidx.annotation.Nullable;
9+
10+
/**
11+
* Created by huizzzhou on 2020/11/11.
12+
*/
13+
public class CallbackManager {
14+
private static final String TAG = "CallbackManager";
15+
private static CallbackManager sCallbackManager;
16+
17+
private CallbackInvocationHandler mCallbackHandler = new CallbackInvocationHandler();
18+
private HashMap<Integer, Long> mObjectMap = new HashMap<>();
19+
20+
static CallbackManager getInstance() {
21+
if (sCallbackManager == null) {
22+
synchronized (CallbackManager.class) {
23+
if (sCallbackManager == null) {
24+
sCallbackManager = new CallbackManager();
25+
}
26+
}
27+
}
28+
return sCallbackManager;
29+
}
30+
31+
@Nullable
32+
public static Object registerCallback(long dartAddr, String clsName) {
33+
try {
34+
Class<?> clz = Class.forName(clsName.replace("/", "."));
35+
Object proxyObject = Proxy.newProxyInstance(
36+
clz.getClassLoader(),
37+
new Class[] { clz },
38+
getInstance().mCallbackHandler);
39+
40+
getInstance().mObjectMap.put(System.identityHashCode(proxyObject), dartAddr);
41+
return proxyObject;
42+
} catch (Exception e) {
43+
Log.e(TAG, e.getMessage());
44+
}
45+
return null;
46+
}
47+
48+
long getRegisterDartAddr(Object proxyObject) {
49+
if (proxyObject == null) {
50+
return 0L;
51+
}
52+
53+
Long dartAddress = getInstance().mObjectMap.get(System.identityHashCode(proxyObject));
54+
return dartAddress == null ? 0L : dartAddress;
55+
}
56+
57+
}

dart_native/android/src/main/jni/dart_native.cpp

Lines changed: 196 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <map>
77
#include <string>
88
#include <regex>
9+
#include <dart_api_dl.h>
910

1011
extern "C" {
1112

@@ -53,8 +54,22 @@ jclass findClass(JNIEnv *env, const char *name) {
5354
env->NewStringUTF(name)));
5455
}
5556

56-
// todo 泄漏
57+
typedef void (*NativeMethodCallback)(
58+
void *targetPtr,
59+
char *funNamePtr,
60+
void **args,
61+
char **argTypes,
62+
int argCount
63+
);
64+
5765
static std::map<jobject, jclass> cache;
66+
static std::map<jobject, int> referenceCount;
67+
68+
// todo too many cache
69+
// for callback
70+
static std::map<void *, jobject> callbackObjCache;
71+
static std::map<jlong, std::map<std::string, NativeMethodCallback>> callbackManagerCache;
72+
static std::map<jlong, void *> targetCache;
5873

5974
void *createTargetClass(char *targetClassName) {
6075
JNIEnv *curEnv;
@@ -101,14 +116,25 @@ void releaseTargetClass(void *classPtr) {
101116
}
102117
}
103118

104-
char *findReturnType(JNIEnv *curEnv, jclass cls, jobject object, char* methondName, char **argType) {
105-
jclass nativeClass = curEnv->FindClass("com/dartnative/dart_native/DartNative");
106-
jmethodID nativeMethodID = curEnv->GetStaticMethodID(nativeClass,
107-
"getMethodReturnType", "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/String;");
108-
109-
// jobjectArray stringArray = new jobjectArray()
119+
void retain(void *classPtr) {
120+
jobject object = static_cast<jobject>(classPtr);
121+
int refCount = referenceCount[object] == NULL ? 1 : referenceCount[object] + 1;
122+
referenceCount[object] = refCount;
123+
}
110124

111-
// curEnv->CallStaticObjectMethodA(nativeClass, nativeMethodID, argType);
125+
void release(void *classPtr) {
126+
jobject object = static_cast<jobject>(classPtr);
127+
if (referenceCount[object] == NULL) {
128+
NSLog("not contain object");
129+
return;
130+
}
131+
int count = referenceCount[object];
132+
if (count <= 1) {
133+
releaseTargetClass(classPtr);
134+
referenceCount[object] = NULL;
135+
return;
136+
}
137+
referenceCount[object] = count - 1;
112138
}
113139

114140
char *spliceChar(char *dest, char *src) {
@@ -135,7 +161,7 @@ void fillArgs(void **args, char **argTypes, jvalue *argValues, JNIEnv *curEnv) {
135161
argValues[index].l = curEnv->NewStringUTF((char *)*args);
136162
}
137163
else {
138-
jobject object = static_cast<jobject>(*args);
164+
jobject object = callbackObjCache.count(*args) ? callbackObjCache[*args] : static_cast<jobject>(*args);
139165
argValues[index].l = object;
140166
}
141167
}
@@ -248,5 +274,166 @@ void *invokeNativeMethodNeo(void *classPtr, char *methodName, void **args, char
248274
return nativeInvokeResult;
249275
}
250276

277+
void registerCallbackManager(jlong targetAddr, char *functionName, void *callback) {
278+
std::map<std::string, NativeMethodCallback> methodsMap;
279+
if (!callbackManagerCache.count(targetAddr)) {
280+
methodsMap[functionName] = (NativeMethodCallback) callback;
281+
callbackManagerCache[targetAddr] = methodsMap;
282+
return;
283+
}
284+
285+
methodsMap = callbackManagerCache[targetAddr];
286+
methodsMap[functionName] = (NativeMethodCallback) callback;
287+
callbackManagerCache[targetAddr] = methodsMap;
288+
}
289+
290+
NativeMethodCallback getCallbackMethod(jlong targetAddr, char *functionName) {
291+
if (!callbackManagerCache.count(targetAddr)) {
292+
NSLog("getCallbackMethod error not register %s", functionName);
293+
return NULL;
294+
}
295+
std::map<std::string, NativeMethodCallback> methodsMap = callbackManagerCache[targetAddr];
296+
return methodsMap[functionName];
297+
}
298+
299+
void registerNativeCallback(void *target, char* targetName, char *funName, void *callback) {
300+
JNIEnv *curEnv;
301+
bool bShouldDetach = false;
302+
auto error = gJvm->GetEnv((void **) &curEnv, JNI_VERSION_1_6);
303+
if (error < 0) {
304+
error = gJvm->AttachCurrentThread(&curEnv, nullptr);
305+
bShouldDetach = true;
306+
NSLog("AttachCurrentThread : %d", error);
307+
}
308+
309+
jclass callbackManager = findClass(curEnv, "com/dartnative/dart_native/CallbackManager");
310+
jmethodID registerCallback = curEnv->GetStaticMethodID(callbackManager, "registerCallback", "(JLjava/lang/String;)Ljava/lang/Object;");
311+
jlong targetAddr = (jlong)target;
312+
jvalue *argValues = new jvalue[2];
313+
argValues[0].j = targetAddr;
314+
argValues[1].l = curEnv->NewStringUTF(targetName);
315+
jobject callbackOJ = curEnv->NewGlobalRef(curEnv->CallStaticObjectMethodA(callbackManager, registerCallback, argValues));
316+
callbackObjCache[target] = callbackOJ;
317+
targetCache[targetAddr] = target;
318+
319+
registerCallbackManager(targetAddr, funName, callback);
320+
curEnv->DeleteLocalRef(callbackManager);
321+
free(argValues);
322+
if (bShouldDetach) {
323+
gJvm->DetachCurrentThread();
324+
}
325+
}
326+
327+
// Dart extensions
328+
Dart_Port native_callback_send_port;
329+
330+
intptr_t InitDartApiDL(void *data, Dart_Port port) {
331+
native_callback_send_port = port;
332+
return Dart_InitializeApiDL(data);
333+
}
334+
335+
static void RunFinalizer(void *isolate_callback_data,
336+
Dart_WeakPersistentHandle handle,
337+
void *peer) {
338+
NSLog("finalizer");
339+
release(peer);
340+
}
341+
342+
void PassObjectToCUseDynamicLinking(Dart_Handle h, void *classPtr) {
343+
if (Dart_IsError_DL(h)) {
344+
return;
345+
}
346+
NSLog("retain");
347+
retain(classPtr);
348+
intptr_t size = 8;
349+
Dart_NewWeakPersistentHandle_DL(h, classPtr, size, RunFinalizer);
350+
}
351+
352+
typedef std::function<void()> Work;
353+
354+
void NotifyDart(Dart_Port send_port, const Work* work) {
355+
const intptr_t work_addr = reinterpret_cast<intptr_t>(work);
356+
357+
Dart_CObject dart_object;
358+
dart_object.type = Dart_CObject_kInt64;
359+
dart_object.value.as_int64 = work_addr;
360+
361+
const bool result = Dart_PostCObject_DL(send_port, &dart_object);
362+
if (!result) {
363+
NSLog("Native callback to Dart failed! Invalid port or isolate died");
364+
}
365+
}
366+
367+
void ExecuteCallback(Work* work_ptr) {
368+
const Work work = *work_ptr;
369+
work();
370+
delete work_ptr;
371+
}
372+
373+
JNIEXPORT void JNICALL Java_com_dartnative_dart_1native_CallbackInvocationHandler_hookCallback(JNIEnv *env,
374+
jclass clazz,
375+
jlong dartObject,
376+
jstring fun_name,
377+
jint arg_count,
378+
jobjectArray arg_types,
379+
jobjectArray args) {
380+
jsize argTypeLength = env->GetArrayLength(arg_types);
381+
char **argTypes = new char *[argTypeLength];
382+
void **arguments = new void *[argTypeLength];
383+
for (int i = 0; i < argTypeLength; ++i) {
384+
jobject argType = env->GetObjectArrayElement(arg_types, i);
385+
jobject argument = env->GetObjectArrayElement(args, i);
386+
387+
jstring argTypeString = (jstring) argType;
388+
argTypes[i] = (char *) env->GetStringUTFChars(argTypeString, 0);
389+
env->DeleteLocalRef(argTypeString);
390+
//todo optimization
391+
if(strcmp(argTypes[i], "I") == 0) {
392+
jclass cls = env->FindClass("java/lang/Integer");
393+
if (env->IsInstanceOf(argument, cls) == JNI_TRUE) {
394+
jmethodID integerToInt = env->GetMethodID(cls, "intValue", "()I");
395+
jint result = env->CallIntMethod(argument, integerToInt);
396+
arguments[i] = (void *) result;
397+
}
398+
env->DeleteLocalRef(cls);
399+
}
400+
else if (strcmp(argTypes[i], "F") == 0) {
401+
jclass cls = env->FindClass("java/lang/Float");
402+
if (env->IsInstanceOf(argument, cls) == JNI_TRUE) {
403+
jmethodID toJfloat = env->GetMethodID(cls, "floatValue", "()F");
404+
jfloat result = env->CallFloatMethod(argument, toJfloat);
405+
float templeFloat = (float) result;
406+
memcpy(&arguments[i], &templeFloat, sizeof(float));
407+
}
408+
env->DeleteLocalRef(cls);
409+
}
410+
else if (strcmp(argTypes[i], "D") == 0) {
411+
jclass cls = env->FindClass("java/lang/Double");
412+
if (env->IsInstanceOf(argument, cls) == JNI_TRUE) {
413+
jmethodID toJfloat = env->GetMethodID(cls, "doubleValue", "()D");
414+
jdouble result = env->CallDoubleMethod(argument, toJfloat);
415+
double templeDouble = (double) result;
416+
memcpy(&arguments[i], &templeDouble, sizeof(double));
417+
}
418+
env->DeleteLocalRef(cls);
419+
}
420+
else if (strcmp(argTypes[i], "Ljava/lang/String;") == 0) {
421+
jstring argString = (jstring) argument;
422+
arguments[i] = (char *) env->GetStringUTFChars(argString, 0);
423+
env->DeleteLocalRef(argString);
424+
}
425+
}
426+
char *funName = (char *) env->GetStringUTFChars(fun_name, 0);
427+
const Work work = [dartObject, argTypes, arguments, arg_count, funName]() {
428+
NativeMethodCallback methodCallback = getCallbackMethod(dartObject, funName);
429+
void *target = targetCache[dartObject];
430+
if (methodCallback != NULL && target != nullptr) {
431+
methodCallback(target, funName, arguments, argTypes, arg_count);
432+
}
433+
};
434+
const Work* work_ptr = new Work(work);
435+
NotifyDart(native_callback_send_port, work_ptr);
436+
}
437+
251438
}
252439

0 commit comments

Comments
 (0)