From 3237d50f0ed6dfe714e79705d9430ac11e4621ce Mon Sep 17 00:00:00 2001 From: Lukasz Kozakiewicz Date: Mon, 29 Jan 2018 21:15:32 +0100 Subject: [PATCH] Android: fix crash when calling juce::JUCEApplicationBase::quit(). --- .../native/java/JuceAppActivity.java | 32 +++++-- .../native/juce_android_JNIHelpers.h | 96 ++++++++++--------- .../native/juce_android_SystemStats.cpp | 20 +++- 3 files changed, 92 insertions(+), 56 deletions(-) diff --git a/modules/juce_core/native/java/JuceAppActivity.java b/modules/juce_core/native/java/JuceAppActivity.java index 7d4f1008e2..2aeb1b35d2 100644 --- a/modules/juce_core/native/java/JuceAppActivity.java +++ b/modules/juce_core/native/java/JuceAppActivity.java @@ -955,15 +955,29 @@ public class JuceAppActivity extends Activity //============================================================================== public static class NativeInvocationHandler implements InvocationHandler { - public NativeInvocationHandler (long nativeContextRef) - { + public NativeInvocationHandler (Activity activityToUse, long nativeContextRef) + { + activity = activityToUse; nativeContext = nativeContextRef; } + public void nativeContextDeleted() + { + nativeContext = 0; + } + @Override public void finalize() { - dispatchFinalize (nativeContext); + activity.runOnUiThread (new Runnable() + { + @Override + public void run() + { + if (nativeContext != 0) + dispatchFinalize (nativeContext); + } + }); } @Override @@ -972,16 +986,22 @@ public class JuceAppActivity extends Activity return dispatchInvoke (nativeContext, proxy, method, args); } - //============================================================================== + //============================================================================== + Activity activity; private long nativeContext = 0; private native void dispatchFinalize (long nativeContextRef); private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); } - public static InvocationHandler createInvocationHandler (long nativeContextRef) + public InvocationHandler createInvocationHandler (long nativeContextRef) { - return new NativeInvocationHandler (nativeContextRef); + return new NativeInvocationHandler (this, nativeContextRef); + } + + public void invocationHandlerContextDeleted (InvocationHandler handler) + { + ((NativeInvocationHandler) handler).nativeContextDeleted(); } //============================================================================== diff --git a/modules/juce_core/native/juce_android_JNIHelpers.h b/modules/juce_core/native/juce_android_JNIHelpers.h index 2e4e541be9..a6800696a9 100644 --- a/modules/juce_core/native/juce_android_JNIHelpers.h +++ b/modules/juce_core/native/juce_android_JNIHelpers.h @@ -283,51 +283,52 @@ extern AndroidSystem android; //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ - METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ - METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ - METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \ - METHOD (finish, "finish", "()V") \ - METHOD (setRequestedOrientation, "setRequestedOrientation", "(I)V") \ - METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \ - METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ - METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ - METHOD (renderGlyph, "renderGlyph", "(CCLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ - STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ - METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ - METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ - METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V") \ - METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ - STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \ - STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \ - STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \ - STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \ - STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \ - STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \ - METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \ - METHOD (getTypeFaceFromByteArray, "getTypeFaceFromByteArray", "([B)Landroid/graphics/Typeface;") \ - METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \ - METHOD (getScreenSaver, "getScreenSaver", "()Z") \ - METHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager;") \ - METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \ - STATICMETHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \ - METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \ - METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \ - METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \ - METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \ - METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \ - METHOD (getAssets, "getAssets", "()Landroid/content/res/AssetManager;") \ - METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \ - METHOD (getPackageManager, "getPackageManager", "()Landroid/content/pm/PackageManager;") \ - METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \ - METHOD (getResources, "getResources", "()Landroid/content/res/Resources;") \ - STATICMETHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \ - METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \ - METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \ - METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \ - METHOD (moveTaskToBack, "moveTaskToBack", "(Z)Z") \ - METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \ - METHOD (startActivityForResult, "startActivityForResult", "(Landroid/content/Intent;I)V") \ - METHOD (getContentResolver, "getContentResolver", "()Landroid/content/ContentResolver;") \ + METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ + METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ + METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \ + METHOD (finish, "finish", "()V") \ + METHOD (setRequestedOrientation, "setRequestedOrientation", "(I)V") \ + METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \ + METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ + METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ + METHOD (renderGlyph, "renderGlyph", "(CCLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ + STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ + METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ + METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ + METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V") \ + METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ + STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \ + STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \ + STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \ + STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \ + STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \ + STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \ + METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \ + METHOD (getTypeFaceFromByteArray, "getTypeFaceFromByteArray", "([B)Landroid/graphics/Typeface;") \ + METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \ + METHOD (getScreenSaver, "getScreenSaver", "()Z") \ + METHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager;") \ + METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \ + STATICMETHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \ + METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \ + METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \ + METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \ + METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \ + METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \ + METHOD (getAssets, "getAssets", "()Landroid/content/res/AssetManager;") \ + METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \ + METHOD (getPackageManager, "getPackageManager", "()Landroid/content/pm/PackageManager;") \ + METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \ + METHOD (getResources, "getResources", "()Landroid/content/res/Resources;") \ + METHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \ + METHOD (invocationHandlerContextDeleted, "invocationHandlerContextDeleted", "(Ljava/lang/reflect/InvocationHandler;)V") \ + METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \ + METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \ + METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \ + METHOD (moveTaskToBack, "moveTaskToBack", "(Z)Z") \ + METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \ + METHOD (startActivityForResult, "startActivityForResult", "(Landroid/content/Intent;I)V") \ + METHOD (getContentResolver, "getContentResolver", "()Landroid/content/ContentResolver;") \ DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH); #undef JNI_CLASS_MEMBERS @@ -637,15 +638,16 @@ void juce_dispatchDelete (JNIEnv*, jlong); class AndroidInterfaceImplementer { protected: - virtual ~AndroidInterfaceImplementer() {} + virtual ~AndroidInterfaceImplementer(); virtual jobject invoke (jobject proxy, jobject method, jobjectArray args); //============================================================================== friend LocalRef CreateJavaInterface (AndroidInterfaceImplementer*, const StringArray&, LocalRef); friend jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray); - friend void juce_dispatchDelete (JNIEnv*, jlong); + friend void juce_dispatchDelete (JNIEnv*, jlong); private: GlobalRef javaSubClass; + GlobalRef invocationHandler; }; LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, diff --git a/modules/juce_core/native/juce_android_SystemStats.cpp b/modules/juce_core/native/juce_android_SystemStats.cpp index d64a56b85b..c43114e887 100644 --- a/modules/juce_core/native/juce_android_SystemStats.cpp +++ b/modules/juce_core/native/juce_android_SystemStats.cpp @@ -133,9 +133,14 @@ LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, } } - auto invocationHandler = LocalRef (env->CallStaticObjectMethod (JuceAppActivity, - JuceAppActivity.createInvocationHandler, - reinterpret_cast (implementer))); + auto invocationHandler = LocalRef (env->CallObjectMethod (android.activity, + JuceAppActivity.createInvocationHandler, + reinterpret_cast (implementer))); + + // CreateJavaInterface() is expected to be called just once for a given implementer + jassert (implementer->invocationHandler == nullptr); + + implementer->invocationHandler = GlobalRef (invocationHandler); return LocalRef (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance, classLoader.get(), classArray.get(), @@ -156,6 +161,15 @@ LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, return CreateJavaInterface (implementer, StringArray (interfaceName)); } +AndroidInterfaceImplementer::~AndroidInterfaceImplementer() + +{ + if (invocationHandler != nullptr) + getEnv()->CallVoidMethod (android.activity, + JuceAppActivity.invocationHandlerContextDeleted, + invocationHandler.get()); +} + jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args) { auto* env = getEnv();