Skip to content

Commit

Permalink
Merge branch 'release/0.3.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
yulingtianxia committed Nov 15, 2020
2 parents d5e6a0c + dcc4a9f commit bea8f9c
Show file tree
Hide file tree
Showing 42 changed files with 4,861 additions and 476 deletions.
5 changes: 5 additions & 0 deletions dart_native/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.3.5

* [Feature] Object lifecycle management for Android.
* [Feature] Callback from Android to Flutter.

## 0.3.4

* Performance optimization for iOS `NSString`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

cmake_minimum_required(VERSION 3.4.1)

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../android/jniLibs/${ANDROID_ABI})

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

# Provides a relative path to your source file(s).
../../../android/src/main/jni/dart_native.cpp )
src/main/jni/include/dart_api_dl.c
src/main/jni/dart_native.cpp )

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

# Links the target library to the log library
# included in the NDK.
${log-lib} )
${log-lib} )

include_directories(src/main/jni/include)
10 changes: 10 additions & 0 deletions dart_native/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion 28

// Encapsulates your external native build configurations.
externalNativeBuild {

// Encapsulates your CMake build configurations.
cmake {
// Provides a relative path to your CMake build script.
path "CMakeLists.txt"
}
}

defaultConfig {
minSdkVersion 16
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Binary file not shown.
Binary file not shown.
Binary file added dart_native/android/jniLibs/x86/libdart_native.so
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.dartnative.dart_native;

import android.util.Log;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
* Created by huizzzhou on 2020/11/11.
*/
public class CallbackInvocationHandler implements InvocationHandler {
private static final String TAG = "CallbackHandler";

private static HashMap<Class<?>, String> sTypeConvert = new HashMap<Class<?>, String>() {{
put(int.class, "I");
put(float.class, "F");
put(double.class, "D");
put(String.class, "Ljava/lang/String;");
}};

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d(TAG, "invoke method: " + method.getName());
Class<?>[] paramTypes = method.getParameterTypes();
String[] params = new String[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
params[i] = sTypeConvert.get(paramTypes[i]);
}
String funName = method.getName();
long dartObjectAddr = CallbackManager.getInstance().getRegisterDartAddr(proxy);
hookCallback(dartObjectAddr, funName, paramTypes.length, params, args);
return proxy;
}

static native void hookCallback(long dartObjectAddr, String funName, int argCount, String[] argTypes, Object[] args);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.dartnative.dart_native;

import android.util.Log;

import java.lang.reflect.Proxy;
import java.util.HashMap;

import androidx.annotation.Nullable;

/**
* Created by huizzzhou on 2020/11/11.
*/
public class CallbackManager {
private static final String TAG = "CallbackManager";
private static CallbackManager sCallbackManager;

private CallbackInvocationHandler mCallbackHandler = new CallbackInvocationHandler();
private HashMap<Integer, Long> mObjectMap = new HashMap<>();

static CallbackManager getInstance() {
if (sCallbackManager == null) {
synchronized (CallbackManager.class) {
if (sCallbackManager == null) {
sCallbackManager = new CallbackManager();
}
}
}
return sCallbackManager;
}

@Nullable
public static Object registerCallback(long dartAddr, String clsName) {
try {
Class<?> clz = Class.forName(clsName.replace("/", "."));
Object proxyObject = Proxy.newProxyInstance(
clz.getClassLoader(),
new Class[] { clz },
getInstance().mCallbackHandler);

getInstance().mObjectMap.put(System.identityHashCode(proxyObject), dartAddr);
return proxyObject;
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return null;
}

long getRegisterDartAddr(Object proxyObject) {
if (proxyObject == null) {
return 0L;
}

Long dartAddress = getInstance().mObjectMap.get(System.identityHashCode(proxyObject));
return dartAddress == null ? 0L : dartAddress;
}

}
205 changes: 196 additions & 9 deletions dart_native/android/src/main/jni/dart_native.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <map>
#include <string>
#include <regex>
#include <dart_api_dl.h>

extern "C" {

Expand Down Expand Up @@ -53,8 +54,22 @@ jclass findClass(JNIEnv *env, const char *name) {
env->NewStringUTF(name)));
}

// todo 泄漏
typedef void (*NativeMethodCallback)(
void *targetPtr,
char *funNamePtr,
void **args,
char **argTypes,
int argCount
);

static std::map<jobject, jclass> cache;
static std::map<jobject, int> referenceCount;

// todo too many cache
// for callback
static std::map<void *, jobject> callbackObjCache;
static std::map<jlong, std::map<std::string, NativeMethodCallback>> callbackManagerCache;
static std::map<jlong, void *> targetCache;

void *createTargetClass(char *targetClassName) {
JNIEnv *curEnv;
Expand Down Expand Up @@ -101,14 +116,25 @@ void releaseTargetClass(void *classPtr) {
}
}

char *findReturnType(JNIEnv *curEnv, jclass cls, jobject object, char* methondName, char **argType) {
jclass nativeClass = curEnv->FindClass("com/dartnative/dart_native/DartNative");
jmethodID nativeMethodID = curEnv->GetStaticMethodID(nativeClass,
"getMethodReturnType", "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/String;");

// jobjectArray stringArray = new jobjectArray()
void retain(void *classPtr) {
jobject object = static_cast<jobject>(classPtr);
int refCount = referenceCount[object] == NULL ? 1 : referenceCount[object] + 1;
referenceCount[object] = refCount;
}

// curEnv->CallStaticObjectMethodA(nativeClass, nativeMethodID, argType);
void release(void *classPtr) {
jobject object = static_cast<jobject>(classPtr);
if (referenceCount[object] == NULL) {
NSLog("not contain object");
return;
}
int count = referenceCount[object];
if (count <= 1) {
releaseTargetClass(classPtr);
referenceCount[object] = NULL;
return;
}
referenceCount[object] = count - 1;
}

char *spliceChar(char *dest, char *src) {
Expand All @@ -135,7 +161,7 @@ void fillArgs(void **args, char **argTypes, jvalue *argValues, JNIEnv *curEnv) {
argValues[index].l = curEnv->NewStringUTF((char *)*args);
}
else {
jobject object = static_cast<jobject>(*args);
jobject object = callbackObjCache.count(*args) ? callbackObjCache[*args] : static_cast<jobject>(*args);
argValues[index].l = object;
}
}
Expand Down Expand Up @@ -248,5 +274,166 @@ void *invokeNativeMethodNeo(void *classPtr, char *methodName, void **args, char
return nativeInvokeResult;
}

void registerCallbackManager(jlong targetAddr, char *functionName, void *callback) {
std::map<std::string, NativeMethodCallback> methodsMap;
if (!callbackManagerCache.count(targetAddr)) {
methodsMap[functionName] = (NativeMethodCallback) callback;
callbackManagerCache[targetAddr] = methodsMap;
return;
}

methodsMap = callbackManagerCache[targetAddr];
methodsMap[functionName] = (NativeMethodCallback) callback;
callbackManagerCache[targetAddr] = methodsMap;
}

NativeMethodCallback getCallbackMethod(jlong targetAddr, char *functionName) {
if (!callbackManagerCache.count(targetAddr)) {
NSLog("getCallbackMethod error not register %s", functionName);
return NULL;
}
std::map<std::string, NativeMethodCallback> methodsMap = callbackManagerCache[targetAddr];
return methodsMap[functionName];
}

void registerNativeCallback(void *target, char* targetName, char *funName, void *callback) {
JNIEnv *curEnv;
bool bShouldDetach = false;
auto error = gJvm->GetEnv((void **) &curEnv, JNI_VERSION_1_6);
if (error < 0) {
error = gJvm->AttachCurrentThread(&curEnv, nullptr);
bShouldDetach = true;
NSLog("AttachCurrentThread : %d", error);
}

jclass callbackManager = findClass(curEnv, "com/dartnative/dart_native/CallbackManager");
jmethodID registerCallback = curEnv->GetStaticMethodID(callbackManager, "registerCallback", "(JLjava/lang/String;)Ljava/lang/Object;");
jlong targetAddr = (jlong)target;
jvalue *argValues = new jvalue[2];
argValues[0].j = targetAddr;
argValues[1].l = curEnv->NewStringUTF(targetName);
jobject callbackOJ = curEnv->NewGlobalRef(curEnv->CallStaticObjectMethodA(callbackManager, registerCallback, argValues));
callbackObjCache[target] = callbackOJ;
targetCache[targetAddr] = target;

registerCallbackManager(targetAddr, funName, callback);
curEnv->DeleteLocalRef(callbackManager);
free(argValues);
if (bShouldDetach) {
gJvm->DetachCurrentThread();
}
}

// Dart extensions
Dart_Port native_callback_send_port;

intptr_t InitDartApiDL(void *data, Dart_Port port) {
native_callback_send_port = port;
return Dart_InitializeApiDL(data);
}

static void RunFinalizer(void *isolate_callback_data,
Dart_WeakPersistentHandle handle,
void *peer) {
NSLog("finalizer");
release(peer);
}

void PassObjectToCUseDynamicLinking(Dart_Handle h, void *classPtr) {
if (Dart_IsError_DL(h)) {
return;
}
NSLog("retain");
retain(classPtr);
intptr_t size = 8;
Dart_NewWeakPersistentHandle_DL(h, classPtr, size, RunFinalizer);
}

typedef std::function<void()> Work;

void NotifyDart(Dart_Port send_port, const Work* work) {
const intptr_t work_addr = reinterpret_cast<intptr_t>(work);

Dart_CObject dart_object;
dart_object.type = Dart_CObject_kInt64;
dart_object.value.as_int64 = work_addr;

const bool result = Dart_PostCObject_DL(send_port, &dart_object);
if (!result) {
NSLog("Native callback to Dart failed! Invalid port or isolate died");
}
}

void ExecuteCallback(Work* work_ptr) {
const Work work = *work_ptr;
work();
delete work_ptr;
}

JNIEXPORT void JNICALL Java_com_dartnative_dart_1native_CallbackInvocationHandler_hookCallback(JNIEnv *env,
jclass clazz,
jlong dartObject,
jstring fun_name,
jint arg_count,
jobjectArray arg_types,
jobjectArray args) {
jsize argTypeLength = env->GetArrayLength(arg_types);
char **argTypes = new char *[argTypeLength];
void **arguments = new void *[argTypeLength];
for (int i = 0; i < argTypeLength; ++i) {
jobject argType = env->GetObjectArrayElement(arg_types, i);
jobject argument = env->GetObjectArrayElement(args, i);

jstring argTypeString = (jstring) argType;
argTypes[i] = (char *) env->GetStringUTFChars(argTypeString, 0);
env->DeleteLocalRef(argTypeString);
//todo optimization
if(strcmp(argTypes[i], "I") == 0) {
jclass cls = env->FindClass("java/lang/Integer");
if (env->IsInstanceOf(argument, cls) == JNI_TRUE) {
jmethodID integerToInt = env->GetMethodID(cls, "intValue", "()I");
jint result = env->CallIntMethod(argument, integerToInt);
arguments[i] = (void *) result;
}
env->DeleteLocalRef(cls);
}
else if (strcmp(argTypes[i], "F") == 0) {
jclass cls = env->FindClass("java/lang/Float");
if (env->IsInstanceOf(argument, cls) == JNI_TRUE) {
jmethodID toJfloat = env->GetMethodID(cls, "floatValue", "()F");
jfloat result = env->CallFloatMethod(argument, toJfloat);
float templeFloat = (float) result;
memcpy(&arguments[i], &templeFloat, sizeof(float));
}
env->DeleteLocalRef(cls);
}
else if (strcmp(argTypes[i], "D") == 0) {
jclass cls = env->FindClass("java/lang/Double");
if (env->IsInstanceOf(argument, cls) == JNI_TRUE) {
jmethodID toJfloat = env->GetMethodID(cls, "doubleValue", "()D");
jdouble result = env->CallDoubleMethod(argument, toJfloat);
double templeDouble = (double) result;
memcpy(&arguments[i], &templeDouble, sizeof(double));
}
env->DeleteLocalRef(cls);
}
else if (strcmp(argTypes[i], "Ljava/lang/String;") == 0) {
jstring argString = (jstring) argument;
arguments[i] = (char *) env->GetStringUTFChars(argString, 0);
env->DeleteLocalRef(argString);
}
}
char *funName = (char *) env->GetStringUTFChars(fun_name, 0);
const Work work = [dartObject, argTypes, arguments, arg_count, funName]() {
NativeMethodCallback methodCallback = getCallbackMethod(dartObject, funName);
void *target = targetCache[dartObject];
if (methodCallback != NULL && target != nullptr) {
methodCallback(target, funName, arguments, argTypes, arg_count);
}
};
const Work* work_ptr = new Work(work);
NotifyDart(native_callback_send_port, work_ptr);
}

}

Loading

0 comments on commit bea8f9c

Please sign in to comment.