Skip to content

Commit 6eeb2d5

Browse files
committed
Merge branch 'release/0.3.13'
2 parents 13913c9 + 345456f commit 6eeb2d5

File tree

11 files changed

+93
-107
lines changed

11 files changed

+93
-107
lines changed

dart_native/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.3.13
2+
3+
* [Fix] Memory leaks on Android.
4+
15
## 0.3.12
26

37
* [Feature] Support List/Array for Android.

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

Lines changed: 76 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ extern "C" {
1616
static JavaVM *gJvm = nullptr;
1717
static jobject gClassLoader;
1818
static jmethodID gFindClassMethod;
19+
static JNIEnv *gCurEnv = nullptr;
20+
static pthread_key_t detachKey = 0;
1921

2022
typedef void (*NativeMethodCallback)(
2123
void *targetPtr,
@@ -46,18 +48,33 @@ JNIEnv *getEnv() {
4648
return env;
4749
}
4850

51+
void detachThreadDestructor(void* arg) {
52+
NSLog("detach from current thread");
53+
gJvm->DetachCurrentThread();
54+
detachKey = 0;
55+
}
56+
57+
void attachThread() {
58+
if (detachKey == 0) {
59+
NSLog("attach to current thread");
60+
pthread_key_create(&detachKey, detachThreadDestructor);
61+
gJvm->AttachCurrentThread(&gCurEnv, NULL);
62+
pthread_setspecific(detachKey, nullptr);
63+
}
64+
}
65+
4966
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
5067
NSLog("JNI_OnLoad");
5168
gJvm = pjvm; // cache the JavaVM pointer
52-
auto env = getEnv();
69+
gCurEnv = getEnv();
5370
//replace with one of your classes in the line below
54-
auto randomClass = env->FindClass("com/dartnative/dart_native/DartNativePlugin");
55-
jclass classClass = env->GetObjectClass(randomClass);
56-
auto classLoaderClass = env->FindClass("java/lang/ClassLoader");
57-
auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader",
71+
auto randomClass = gCurEnv->FindClass("com/dartnative/dart_native/DartNativePlugin");
72+
jclass classClass = gCurEnv->GetObjectClass(randomClass);
73+
auto classLoaderClass = gCurEnv->FindClass("java/lang/ClassLoader");
74+
auto getClassLoaderMethod = gCurEnv->GetMethodID(classClass, "getClassLoader",
5875
"()Ljava/lang/ClassLoader;");
59-
gClassLoader = env->NewGlobalRef(env->CallObjectMethod(randomClass, getClassLoaderMethod));
60-
gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass",
76+
gClassLoader = gCurEnv->NewGlobalRef(gCurEnv->CallObjectMethod(randomClass, getClassLoaderMethod));
77+
gFindClassMethod = gCurEnv->GetMethodID(classLoaderClass, "findClass",
6178
"(Ljava/lang/String;)Ljava/lang/Class;");
6279

6380
NSLog("JNI_OnLoad finish");
@@ -77,18 +94,20 @@ char *generateSignature(char **argTypes) {
7794
if (argTypes != nullptr)
7895
{
7996
for (; *argTypes; ++argTypes, ++argCount) {
80-
signature = spliceChar(signature, *argTypes);
97+
char *templeSignature = spliceChar(signature, *argTypes);
98+
signature = templeSignature;
99+
free(templeSignature);
81100
}
82101
}
83102
return spliceChar(signature, const_cast<char *>(")"));
84103
}
85104

86-
void fillArgs(void **args, char **argTypes, jvalue *argValues, JNIEnv *curEnv) {
105+
void fillArgs(void **args, char **argTypes, jvalue *argValues) {
87106
for(jsize index(0); *argTypes ; ++args, ++index, ++argTypes) {
88107
char *argType = *argTypes;
89108
if (strlen(argType) > 1) {
90109
if (strcmp(argType, "Ljava/lang/String;") == 0) {
91-
argValues[index].l = curEnv->NewStringUTF((char *)*args);
110+
argValues[index].l = gCurEnv->NewStringUTF((char *)*args);
92111
}
93112
else {
94113
jobject object = callbackObjCache.count(*args) ? callbackObjCache[*args] : static_cast<jobject>(*args);
@@ -137,61 +156,38 @@ jclass findClass(JNIEnv *env, const char *name) {
137156
return nativeClass;
138157
}
139158

140-
jobject newObject(JNIEnv *env, jclass cls, void **args, char **argTypes) {
159+
jobject newObject(jclass cls, void **args, char **argTypes) {
141160
char *signature = generateSignature(argTypes);
142161
jvalue *argValues = new jvalue[strlen(signature) - 2];
143162
if (strlen(signature) - 2 > 0) {
144-
fillArgs(args, argTypes, argValues, env);
163+
fillArgs(args, argTypes, argValues);
145164
}
146-
jmethodID constructor = env->GetMethodID(cls, "<init>", spliceChar(signature, const_cast<char *>("V")));
147-
jobject newObj = env->NewObjectA(cls, constructor, argValues);
165+
char *constructorSig = spliceChar(signature, const_cast<char *>("V"));
166+
jmethodID constructor = gCurEnv->GetMethodID(cls, "<init>", constructorSig);
167+
jobject newObj = gCurEnv->NewObjectA(cls, constructor, argValues);
148168
free(argValues);
169+
free(constructorSig);
149170
return newObj;
150171
}
151172

152173
void *createTargetClass(char *targetClassName, void **args, char **argTypes) {
153-
JNIEnv *curEnv;
154-
bool bShouldDetach = false;
155-
156-
auto error = gJvm->GetEnv((void **) &curEnv, JNI_VERSION_1_6);
157-
if (error < 0) {
158-
error = gJvm->AttachCurrentThread(&curEnv, nullptr);
159-
bShouldDetach = true;
160-
NSLog("AttachCurrentThread : %d", error);
161-
}
162-
163-
jclass cls = findClass(curEnv, targetClassName);
174+
attachThread();
164175

165-
jobject newObj = curEnv->NewGlobalRef(newObject(curEnv, cls, args, argTypes));
166-
cache[newObj] = static_cast<jclass>(curEnv->NewGlobalRef(cls));
176+
jclass cls = findClass(gCurEnv, targetClassName);
167177

168-
169-
if (bShouldDetach) {
170-
gJvm->DetachCurrentThread();
171-
}
178+
jobject newObj = gCurEnv->NewGlobalRef(newObject(cls, args, argTypes));
179+
cache[newObj] = static_cast<jclass>(gCurEnv->NewGlobalRef(cls));
172180

173181
return newObj;
174182
}
175183

176184

177185
void releaseTargetClass(void *classPtr) {
178-
JNIEnv *curEnv;
179-
bool bShouldDetach = false;
180-
181-
auto error = gJvm->GetEnv((void **) &curEnv, JNI_VERSION_1_6);
182-
if (error < 0) {
183-
error = gJvm->AttachCurrentThread(&curEnv, nullptr);
184-
bShouldDetach = true;
185-
NSLog("AttachCurrentThread : %d", error);
186-
}
186+
attachThread();
187187

188188
jobject object = static_cast<jobject>(classPtr);
189189
cache.erase(object);
190-
curEnv->DeleteGlobalRef(object);
191-
192-
if (bShouldDetach) {
193-
gJvm->DetachCurrentThread();
194-
}
190+
gCurEnv->DeleteGlobalRef(object);
195191
}
196192

197193
void retain(void *classPtr) {
@@ -216,82 +212,75 @@ void release(void *classPtr) {
216212
}
217213

218214
void *invokeNativeMethodNeo(void *classPtr, char *methodName, void **args, char **argTypes, char *returnType) {
219-
JNIEnv *curEnv;
220-
bool bShouldDetach = false;
221215
void *nativeInvokeResult = nullptr;
222216

223-
auto error = gJvm->GetEnv((void **) &curEnv, JNI_VERSION_1_6);
224-
if (error < 0) {
225-
gJvm->AttachCurrentThread(&curEnv, nullptr);
226-
bShouldDetach = true;
227-
}
217+
attachThread();
228218
jobject object = static_cast<jobject>(classPtr);
229219
jclass cls = cache[object];
230220
char *signature = generateSignature(argTypes);
231221
jvalue *argValues = new jvalue[strlen(signature) - 2];
232222
if ((strlen(signature) - 2) > 0) {
233-
fillArgs(args, argTypes, argValues, curEnv);
223+
fillArgs(args, argTypes, argValues);
234224
}
235-
jmethodID method = curEnv->GetMethodID(cls, methodName, spliceChar(signature, returnType));
236-
NSLog("call method: %s descriptor: %s", methodName, spliceChar(signature, returnType));
225+
char *methodSignature = spliceChar(signature, returnType);
226+
jmethodID method = gCurEnv->GetMethodID(cls, methodName, methodSignature);
227+
NSLog("call method: %s descriptor: %s", methodName, methodSignature);
237228

238-
if (strlen(returnType) > 1) {
229+
if (strlen(returnType) > 1) {
239230
if (strcmp(returnType, "Ljava/lang/String;") == 0) {
240-
jstring javaString = (jstring)curEnv->CallObjectMethodA(object, method, argValues);
231+
jstring javaString = (jstring)gCurEnv->CallObjectMethodA(object, method, argValues);
241232
jboolean isCopy = JNI_FALSE;
242-
nativeInvokeResult = (char *) curEnv->GetStringUTFChars(javaString, &isCopy);
233+
nativeInvokeResult = (char *) gCurEnv->GetStringUTFChars(javaString, &isCopy);
243234
}
244235
else {
245-
jobject obj = curEnv->NewGlobalRef(curEnv->CallObjectMethodA(object, method, argValues));
246-
jclass objCls = curEnv->GetObjectClass(obj);
236+
jobject obj = gCurEnv->NewGlobalRef(gCurEnv->CallObjectMethodA(object, method, argValues));
237+
jclass objCls = gCurEnv->GetObjectClass(obj);
247238
//store class value
248-
cache[obj] = static_cast<jclass>(curEnv->NewGlobalRef(objCls));
239+
cache[obj] = static_cast<jclass>(gCurEnv->NewGlobalRef(objCls));
249240
nativeInvokeResult = obj;
250241
}
251242
}
252243
else if (strcmp(returnType, "C") == 0) {
253-
auto nativeChar = curEnv->CallCharMethodA(object, method, argValues);
244+
auto nativeChar = gCurEnv->CallCharMethodA(object, method, argValues);
254245
nativeInvokeResult = (void *) nativeChar;
255246
}
256247
else if(strcmp(returnType, "I") == 0) {
257-
auto nativeInt = curEnv->CallIntMethodA(object, method, argValues);
248+
auto nativeInt = gCurEnv->CallIntMethodA(object, method, argValues);
258249
nativeInvokeResult = (void *) nativeInt;
259250
}
260251
else if(strcmp(returnType, "D") == 0) {
261-
auto nativeDouble = curEnv->CallDoubleMethodA(object, method, argValues);
252+
auto nativeDouble = gCurEnv->CallDoubleMethodA(object, method, argValues);
262253
double cDouble = (double) nativeDouble;
263254
memcpy(&nativeInvokeResult, &cDouble, sizeof(double));
264255
}
265256
else if(strcmp(returnType, "F") == 0) {
266-
auto nativeDouble = curEnv->CallFloatMethodA(object, method, argValues);
257+
auto nativeDouble = gCurEnv->CallFloatMethodA(object, method, argValues);
267258
float cDouble = (float) nativeDouble;
268259
memcpy(&nativeInvokeResult, &cDouble, sizeof(float));
269260
}
270261
else if(strcmp(returnType, "B") == 0) {
271-
auto nativeByte = curEnv->CallByteMethodA(object, method, argValues);
262+
auto nativeByte = gCurEnv->CallByteMethodA(object, method, argValues);
272263
nativeInvokeResult = (void *) nativeByte;
273264
}
274265
else if(strcmp(returnType, "S") == 0) {
275-
auto nativeShort = curEnv->CallShortMethodA(object, method, argValues);
266+
auto nativeShort = gCurEnv->CallShortMethodA(object, method, argValues);
276267
nativeInvokeResult = (void *) nativeShort;
277268
}
278269
else if(strcmp(returnType, "J") == 0) {
279-
auto nativeLong = curEnv->CallLongMethodA(object, method, argValues);
270+
auto nativeLong = gCurEnv->CallLongMethodA(object, method, argValues);
280271
nativeInvokeResult = (void *) nativeLong;
281272
}
282273
else if(strcmp(returnType, "Z") == 0) {
283-
auto nativeBool = curEnv->CallBooleanMethodA(object, method, argValues);
274+
auto nativeBool = gCurEnv->CallBooleanMethodA(object, method, argValues);
284275
nativeInvokeResult = (void *) nativeBool;
285276
}
286277
else if(strcmp(returnType, "V") == 0) {
287-
curEnv->CallVoidMethodA(object, method, argValues);
278+
gCurEnv->CallVoidMethodA(object, method, argValues);
288279
}
289280

290281
free(argValues);
282+
free(methodSignature);
291283
free(signature);
292-
if (bShouldDetach) {
293-
gJvm->DetachCurrentThread();
294-
}
295284
return nativeInvokeResult;
296285
}
297286

@@ -318,31 +307,21 @@ NativeMethodCallback getCallbackMethod(jlong targetAddr, char *functionName) {
318307
}
319308

320309
void registerNativeCallback(void *target, char* targetName, char *funName, void *callback) {
321-
JNIEnv *curEnv;
322-
bool bShouldDetach = false;
323-
auto error = gJvm->GetEnv((void **) &curEnv, JNI_VERSION_1_6);
324-
if (error < 0) {
325-
error = gJvm->AttachCurrentThread(&curEnv, nullptr);
326-
bShouldDetach = true;
327-
NSLog("AttachCurrentThread : %d", error);
328-
}
310+
attachThread();
329311

330-
jclass callbackManager = findClass(curEnv, "com/dartnative/dart_native/CallbackManager");
331-
jmethodID registerCallback = curEnv->GetStaticMethodID(callbackManager, "registerCallback", "(JLjava/lang/String;)Ljava/lang/Object;");
312+
jclass callbackManager = findClass(gCurEnv, "com/dartnative/dart_native/CallbackManager");
313+
jmethodID registerCallback = gCurEnv->GetStaticMethodID(callbackManager, "registerCallback", "(JLjava/lang/String;)Ljava/lang/Object;");
332314
jlong targetAddr = (jlong)target;
333315
jvalue *argValues = new jvalue[2];
334316
argValues[0].j = targetAddr;
335-
argValues[1].l = curEnv->NewStringUTF(targetName);
336-
jobject callbackOJ = curEnv->NewGlobalRef(curEnv->CallStaticObjectMethodA(callbackManager, registerCallback, argValues));
317+
argValues[1].l = gCurEnv->NewStringUTF(targetName);
318+
jobject callbackOJ = gCurEnv->NewGlobalRef(gCurEnv->CallStaticObjectMethodA(callbackManager, registerCallback, argValues));
337319
callbackObjCache[target] = callbackOJ;
338320
targetCache[targetAddr] = target;
339321

340322
registerCallbackManager(targetAddr, funName, callback);
341-
curEnv->DeleteLocalRef(callbackManager);
323+
gCurEnv->DeleteLocalRef(callbackManager);
342324
free(argValues);
343-
if (bShouldDetach) {
344-
gJvm->DetachCurrentThread();
345-
}
346325
}
347326

348327
// Dart extensions
@@ -407,10 +386,9 @@ JNIEXPORT jobject JNICALL Java_com_dartnative_dart_1native_CallbackInvocationHan
407386
char **argTypes = new char *[argTypeLength + 1];
408387
void **arguments = new void *[argTypeLength];
409388
for (int i = 0; i < argTypeLength; ++i) {
410-
jobject argType = env->GetObjectArrayElement(arg_types, i);
389+
jstring argTypeString = (jstring) env->GetObjectArrayElement(arg_types, i);
411390
jobject argument = env->GetObjectArrayElement(args, i);
412391

413-
jstring argTypeString = (jstring) argType;
414392
argTypes[i] = (char *) env->GetStringUTFChars(argTypeString, 0);
415393
env->DeleteLocalRef(argTypeString);
416394
//todo optimization
@@ -451,7 +429,7 @@ JNIEXPORT jobject JNICALL Java_com_dartnative_dart_1native_CallbackInvocationHan
451429
}
452430
char *returnType = (char *) env->GetStringUTFChars(return_type, 0);
453431
argTypes[argTypeLength] = returnType;
454-
const Work work = [dartObject, argTypes, arguments, arg_count, funName, &sem, isSemInitSuccess, return_type]() {
432+
const Work work = [dartObject, argTypes, arguments, arg_count, funName, &sem, isSemInitSuccess]() {
455433
NativeMethodCallback methodCallback = getCallbackMethod(dartObject, funName);
456434
void *target = targetCache[dartObject];
457435
if (methodCallback != NULL && target != nullptr) {
@@ -469,10 +447,12 @@ JNIEXPORT jobject JNICALL Java_com_dartnative_dart_1native_CallbackInvocationHan
469447
jobject callbackResult = NULL;
470448

471449
if (isSemInitSuccess) {
472-
NSLog("wait");
450+
NSLog("wait work execute");
473451
sem_wait(&sem);
474452
//todo optimization
475-
if (strcmp(returnType, "Ljava/lang/String;") == 0) {
453+
if (returnType == nullptr) {
454+
NSLog("void");
455+
} else if (strcmp(returnType, "Ljava/lang/String;") == 0) {
476456
callbackResult = env->NewStringUTF((char *) arguments[0]);
477457
} else if (strcmp(returnType, "Z") == 0) {
478458
jclass booleanClass = env->FindClass("java/lang/Boolean");
@@ -483,9 +463,10 @@ JNIEXPORT jobject JNICALL Java_com_dartnative_dart_1native_CallbackInvocationHan
483463
sem_destroy(&sem);
484464
}
485465

466+
free(returnType);
486467
free(funName);
487-
free(argTypes);
488468
free(arguments);
469+
free(argTypes);
489470

490471
return callbackResult;
491472
}

dart_native/example/lib/android/delegate_stub.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import 'package:dart_native_example/android/runtimestub.dart';
55

66
class DelegateStub extends JObject with SampleDelegate {
77
DelegateStub([Pointer ptr])
8-
: super("com/dartnative/dart_native_example/SampleDelegate", ptr) {
8+
: super("com/dartnative/dart_native_example/SampleDelegate", pointer: ptr) {
99
super.registerSampleDelegate();
1010
}
1111

dart_native/example/lib/android/entity.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'dart:ffi';
22

33
import 'package:dart_native/dart_native.dart';
44
class Entity extends JObject {
5-
Entity([Pointer ptr]) : super("com/dartnative/dart_native_example/Entity", ptr);
5+
Entity([Pointer ptr]) : super("com/dartnative/dart_native_example/Entity", pointer: ptr);
66

77
int getCurrentTime() {
88
return invoke('getCurrentTime', [], "I");

dart_native/example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ packages:
168168
path: ".."
169169
relative: true
170170
source: path
171-
version: "0.3.12"
171+
version: "0.3.13"
172172
dart_native_gen:
173173
dependency: transitive
174174
description:

dart_native/lib/src/android/foundation/collection/jarray.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class JArray extends JSubclass<List> {
2121

2222
JArray.fromPointer(Pointer<Void> ptr) : super.fromPointer(ptr, CLS_ARRAY_OBJECT) {
2323
JObject converter = JObject("com/dartnative/dart_native/ArrayListConverter");
24-
raw = JList.fromPointer(converter.invoke("arrayToList", [JObject("java/lang/Object", ptr)], "Ljava/util/List;")).raw;
24+
raw = JList.fromPointer(converter.invoke("arrayToList", [JObject("java/lang/Object", pointer: ptr)], "Ljava/util/List;")).raw;
2525
}
2626
}
2727

dart_native/lib/src/android/foundation/collection/jlist.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ Pointer<Void> _new(dynamic value, String clsName) {
5151
}
5252

5353
String _getItemClass(Pointer<Void> itemPtr) {
54-
JObject templeObject = JObject("java/lang/Object", itemPtr);
54+
JObject templeObject = JObject("java/lang/Object", pointer: itemPtr);
5555
templeObject = JObject("java/lang/Class",
56-
templeObject.invoke("getClass", null, "Ljava/lang/Class;"));
56+
pointer: templeObject.invoke("getClass", null, "Ljava/lang/Class;"));
5757

5858
return templeObject.invoke("getName", null, "Ljava/lang/String;");
5959
}

0 commit comments

Comments
 (0)