diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java index 18291a66f..40618ac95 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java @@ -235,6 +235,7 @@ public class CatalystInstanceImpl implements CatalystInstance { new Runnable() { @Override public void run() { + mBridge.destroy(); mBridge.dispose(); bridgeDisposeFuture.set(null); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactBridge.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactBridge.java index 89b6870be..1e9fe6748 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactBridge.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactBridge.java @@ -87,6 +87,7 @@ public class ReactBridge extends Countable { public native void stopProfiler(String title, String filename); private native void handleMemoryPressureModerate(); private native void handleMemoryPressureCritical(); + public native void destroy(); /** * This method will return a long representing the underlying JSGlobalContextRef pointer or diff --git a/ReactAndroid/src/main/jni/react/Bridge.cpp b/ReactAndroid/src/main/jni/react/Bridge.cpp index 1c1a2bf34..ed758d1f0 100644 --- a/ReactAndroid/src/main/jni/react/Bridge.cpp +++ b/ReactAndroid/src/main/jni/react/Bridge.cpp @@ -22,8 +22,7 @@ Bridge::Bridge(JSExecutorFactory* jsExecutorFactory, Callback callback) : // This must be called on the same thread on which the constructor was called. Bridge::~Bridge() { - *m_destroyed = true; - m_mainExecutor.reset(); + CHECK(*m_destroyed) << "Bridge::destroy() must be called before deallocating the Bridge!"; } void Bridge::loadApplicationScript(const std::string& script, const std::string& sourceURL) { @@ -132,4 +131,10 @@ void Bridge::callNativeModules(const std::string& callJSON, bool isEndOfBatch) { m_callback(parseMethodCalls(callJSON), isEndOfBatch); } +void Bridge::destroy() { + *m_destroyed = true; + m_mainExecutor->destroy(); + m_mainExecutor.reset(); +} + } } diff --git a/ReactAndroid/src/main/jni/react/Bridge.h b/ReactAndroid/src/main/jni/react/Bridge.h index 3868d507f..16edc62ac 100644 --- a/ReactAndroid/src/main/jni/react/Bridge.h +++ b/ReactAndroid/src/main/jni/react/Bridge.h @@ -74,6 +74,11 @@ public: * TODO: get rid of isEndOfBatch */ void callNativeModules(const std::string& callJSON, bool isEndOfBatch); + + /** + * Synchronously tears down the bridge and the main executor. + */ + void destroy(); private: Callback m_callback; // This is used to avoid a race condition where a proxyCallback gets queued after ~Bridge(), diff --git a/ReactAndroid/src/main/jni/react/Executor.h b/ReactAndroid/src/main/jni/react/Executor.h index 036b6414e..6f392a3cd 100644 --- a/ReactAndroid/src/main/jni/react/Executor.h +++ b/ReactAndroid/src/main/jni/react/Executor.h @@ -72,6 +72,7 @@ public: virtual void handleMemoryPressureCritical() { handleMemoryPressureModerate(); }; + virtual void destroy() {}; virtual ~JSExecutor() {}; }; diff --git a/ReactAndroid/src/main/jni/react/JSCExecutor.cpp b/ReactAndroid/src/main/jni/react/JSCExecutor.cpp index 56af90b9b..01ccc8188 100644 --- a/ReactAndroid/src/main/jni/react/JSCExecutor.cpp +++ b/ReactAndroid/src/main/jni/react/JSCExecutor.cpp @@ -126,17 +126,17 @@ JSCExecutor::JSCExecutor( } JSCExecutor::~JSCExecutor() { - try { - *m_isDestroyed = true; - if (m_messageQueueThread->isOnThread()) { + CHECK(*m_isDestroyed) << "JSCExecutor::destroy() must be called before its destructor!"; +} + +void JSCExecutor::destroy() { + *m_isDestroyed = true; + if (m_messageQueueThread->isOnThread()) { + terminateOnJSVMThread(); + } else { + m_messageQueueThread->runOnQueueSync([this] () { terminateOnJSVMThread(); - } else { - m_messageQueueThread->runOnQueueSync([this] () { - terminateOnJSVMThread(); - }); - } - } catch (...) { - Exceptions::handleUncaughtException(); + }); } } @@ -392,6 +392,7 @@ void JSCExecutor::receiveMessageFromOwner(const std::string& msgString) { void JSCExecutor::terminateOwnedWebWorker(int workerId) { auto worker = m_ownedWorkers.at(workerId).getExecutor(); std::shared_ptr workerMQT = worker->m_messageQueueThread; + worker->destroy(); m_ownedWorkers.erase(workerId); workerMQT->quitSynchronous(); } diff --git a/ReactAndroid/src/main/jni/react/JSCExecutor.h b/ReactAndroid/src/main/jni/react/JSCExecutor.h index 336220144..a10907ccf 100644 --- a/ReactAndroid/src/main/jni/react/JSCExecutor.h +++ b/ReactAndroid/src/main/jni/react/JSCExecutor.h @@ -75,6 +75,7 @@ public: virtual void stopProfiler(const std::string &titleString, const std::string &filename) override; virtual void handleMemoryPressureModerate() override; virtual void handleMemoryPressureCritical() override; + virtual void destroy() override; void installNativeHook(const char *name, JSObjectCallAsFunctionCallback callback); diff --git a/ReactAndroid/src/main/jni/react/Platform.cpp b/ReactAndroid/src/main/jni/react/Platform.cpp index 3e1c700b7..ace85bd92 100644 --- a/ReactAndroid/src/main/jni/react/Platform.cpp +++ b/ReactAndroid/src/main/jni/react/Platform.cpp @@ -26,8 +26,4 @@ namespace JSLogging { JSCNativeHook nativeHook = nullptr; }; -namespace Exceptions { -HandleUncaughtException handleUncaughtException; -}; - } } diff --git a/ReactAndroid/src/main/jni/react/Platform.h b/ReactAndroid/src/main/jni/react/Platform.h index 5ad664dbf..608fbc1a8 100644 --- a/ReactAndroid/src/main/jni/react/Platform.h +++ b/ReactAndroid/src/main/jni/react/Platform.h @@ -46,9 +46,4 @@ namespace JSLogging { extern JSCNativeHook nativeHook; }; -namespace Exceptions { - using HandleUncaughtException = std::function; - extern HandleUncaughtException handleUncaughtException; -}; - } } diff --git a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp index fad41e7cc..d684b5823 100644 --- a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp @@ -651,6 +651,15 @@ static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback, setCountableForJava(env, obj, std::move(bridge)); } +static void destroy(JNIEnv* env, jobject jbridge) { + auto bridge = extractRefPtr(env, jbridge); + try { + bridge->destroy(); + } catch (...) { + translatePendingCppExceptionToJavaException(); + } +} + static void loadApplicationScript( const RefPtr& bridge, const std::string& script, @@ -865,9 +874,6 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { return std::unique_ptr( JMessageQueueThread::currentMessageQueueThread().release()); }; - Exceptions::handleUncaughtException = [] () { - translatePendingCppExceptionToJavaException(); - }; PerfLogging::installNativeHooks = addNativePerfLoggingHooks; JSLogging::nativeHook = nativeLoggingHook; @@ -950,6 +956,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { registerNatives("com/facebook/react/bridge/ReactBridge", { makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/queue/MessageQueueThread;)V", bridge::create), + makeNativeMethod("destroy", bridge::destroy), makeNativeMethod( "loadScriptFromAssets", "(Landroid/content/res/AssetManager;Ljava/lang/String;)V", bridge::loadScriptFromAssets),