Refactor so that mBridge can not be null and can be final
Reviewed By: astreet Differential Revision: D2717983 fb-gh-sync-id: 969499eb062d54e0271f910c06c4539e2cb20030
This commit is contained in:
Родитель
f2d84456e5
Коммит
8de172e92d
|
@ -14,6 +14,7 @@ import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -63,7 +64,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||||
private boolean mInitialized = false;
|
private boolean mInitialized = false;
|
||||||
|
|
||||||
// Access from JS thread
|
// Access from JS thread
|
||||||
private @Nullable ReactBridge mBridge;
|
private final ReactBridge mBridge;
|
||||||
private boolean mJSBundleHasLoaded;
|
private boolean mJSBundleHasLoaded;
|
||||||
|
|
||||||
private CatalystInstanceImpl(
|
private CatalystInstanceImpl(
|
||||||
|
@ -83,39 +84,34 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||||
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
|
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
|
||||||
mTraceListener = new JSProfilerTraceListener();
|
mTraceListener = new JSProfilerTraceListener();
|
||||||
|
|
||||||
final CountDownLatch initLatch = new CountDownLatch(1);
|
|
||||||
mCatalystQueueConfiguration.getJSQueueThread().runOnQueue(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
|
|
||||||
try {
|
|
||||||
initializeBridge(jsExecutor, jsModulesConfig);
|
|
||||||
initLatch.countDown();
|
|
||||||
} finally {
|
|
||||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Assertions.assertCondition(
|
mBridge = mCatalystQueueConfiguration.getJSQueueThread().callOnQueue(
|
||||||
initLatch.await(BRIDGE_SETUP_TIMEOUT_MS, TimeUnit.MILLISECONDS),
|
new Callable<ReactBridge>() {
|
||||||
"Timed out waiting for bridge to initialize!");
|
@Override
|
||||||
} catch (InterruptedException e) {
|
public ReactBridge call() throws Exception {
|
||||||
throw new RuntimeException(e);
|
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
|
||||||
|
try {
|
||||||
|
return initializeBridge(jsExecutor, jsModulesConfig);
|
||||||
|
} finally {
|
||||||
|
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).get(BRIDGE_SETUP_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (Exception t) {
|
||||||
|
throw new RuntimeException("Failed to initialize bridge", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeBridge(
|
private ReactBridge initializeBridge(
|
||||||
JavaScriptExecutor jsExecutor,
|
JavaScriptExecutor jsExecutor,
|
||||||
JavaScriptModulesConfig jsModulesConfig) {
|
JavaScriptModulesConfig jsModulesConfig) {
|
||||||
mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
|
mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
|
||||||
Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
|
Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
|
||||||
|
|
||||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
|
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
|
||||||
|
ReactBridge bridge;
|
||||||
try {
|
try {
|
||||||
mBridge = new ReactBridge(
|
bridge = new ReactBridge(
|
||||||
jsExecutor,
|
jsExecutor,
|
||||||
new NativeModulesReactCallback(),
|
new NativeModulesReactCallback(),
|
||||||
mCatalystQueueConfiguration.getNativeModulesQueueThread());
|
mCatalystQueueConfiguration.getNativeModulesQueueThread());
|
||||||
|
@ -125,15 +121,17 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||||
|
|
||||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
|
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
|
||||||
try {
|
try {
|
||||||
mBridge.setGlobalVariable(
|
bridge.setGlobalVariable(
|
||||||
"__fbBatchedBridgeConfig",
|
"__fbBatchedBridgeConfig",
|
||||||
buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
|
buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
|
||||||
mBridge.setGlobalVariable(
|
bridge.setGlobalVariable(
|
||||||
"__RCTProfileIsProfiling",
|
"__RCTProfileIsProfiling",
|
||||||
Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
|
Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
|
||||||
} finally {
|
} finally {
|
||||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -260,13 +258,11 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mBridge != null) {
|
Systrace.unregisterListener(mTraceListener);
|
||||||
Systrace.unregisterListener(mTraceListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can access the Bridge from any thread now because we know either we are on the JS thread
|
// We can access the Bridge from any thread now because we know either we are on the JS thread
|
||||||
// or the JS thread has finished via CatalystQueueConfiguration#destroy()
|
// or the JS thread has finished via CatalystQueueConfiguration#destroy()
|
||||||
Assertions.assertNotNull(mBridge).dispose();
|
mBridge.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -294,8 +290,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public @Nullable
|
public ReactBridge getBridge() {
|
||||||
ReactBridge getBridge() {
|
|
||||||
return mBridge;
|
return mBridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,25 +336,16 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsProfiling() {
|
public boolean supportsProfiling() {
|
||||||
if (mBridge == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mBridge.supportsProfiling();
|
return mBridge.supportsProfiling();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startProfiler(String title) {
|
public void startProfiler(String title) {
|
||||||
if (mBridge == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mBridge.startProfiler(title);
|
mBridge.startProfiler(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopProfiler(String title, String filename) {
|
public void stopProfiler(String title, String filename) {
|
||||||
if (mBridge == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mBridge.stopProfiler(title, filename);
|
mBridge.stopProfiler(title, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
|
|
||||||
package com.facebook.react.bridge.queue;
|
package com.facebook.react.bridge.queue;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,6 +26,13 @@ public interface MessageQueueThread {
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
void runOnQueue(Runnable runnable);
|
void runOnQueue(Runnable runnable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given Callable on this Thread. It will be submitted to the end of the event queue even
|
||||||
|
* if it is being submitted from the same queue Thread.
|
||||||
|
*/
|
||||||
|
@DoNotStrip
|
||||||
|
<T> Future<T> callOnQueue(final Callable<T> callable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whether the current Thread is also the Thread associated with this MessageQueueThread.
|
* @return whether the current Thread is also the Thread associated with this MessageQueueThread.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
|
|
||||||
package com.facebook.react.bridge.queue;
|
package com.facebook.react.bridge.queue;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import com.facebook.common.logging.FLog;
|
import com.facebook.common.logging.FLog;
|
||||||
|
@ -45,6 +49,7 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
|
||||||
* if it is being submitted from the same queue Thread.
|
* if it is being submitted from the same queue Thread.
|
||||||
*/
|
*/
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
|
@Override
|
||||||
public void runOnQueue(Runnable runnable) {
|
public void runOnQueue(Runnable runnable) {
|
||||||
if (mIsFinished) {
|
if (mIsFinished) {
|
||||||
FLog.w(
|
FLog.w(
|
||||||
|
@ -55,9 +60,29 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
|
||||||
mHandler.post(runnable);
|
mHandler.post(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
@Override
|
||||||
|
public <T> Future<T> callOnQueue(final Callable<T> callable) {
|
||||||
|
final SimpleSettableFuture<T> future = new SimpleSettableFuture<>();
|
||||||
|
runOnQueue(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
future.set(callable.call());
|
||||||
|
} catch (Exception e) {
|
||||||
|
future.setException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whether the current Thread is also the Thread associated with this MessageQueueThread.
|
* @return whether the current Thread is also the Thread associated with this MessageQueueThread.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean isOnThread() {
|
public boolean isOnThread() {
|
||||||
return mLooper.getThread() == Thread.currentThread();
|
return mLooper.getThread() == Thread.currentThread();
|
||||||
}
|
}
|
||||||
|
@ -66,6 +91,7 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
|
||||||
* Asserts {@link #isOnThread()}, throwing a {@link AssertionException} (NOT an
|
* Asserts {@link #isOnThread()}, throwing a {@link AssertionException} (NOT an
|
||||||
* {@link AssertionError}) if the assertion fails.
|
* {@link AssertionError}) if the assertion fails.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void assertIsOnThread() {
|
public void assertIsOnThread() {
|
||||||
SoftAssertions.assertCondition(isOnThread(), mAssertionErrorMessage);
|
SoftAssertions.assertCondition(isOnThread(), mAssertionErrorMessage);
|
||||||
}
|
}
|
||||||
|
@ -139,6 +165,13 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
|
||||||
}, "mqt_" + name);
|
}, "mqt_" + name);
|
||||||
bgThread.start();
|
bgThread.start();
|
||||||
|
|
||||||
return new MessageQueueThreadImpl(name, simpleSettableFuture.get(5000), exceptionHandler);
|
try {
|
||||||
|
return new MessageQueueThreadImpl(
|
||||||
|
name,
|
||||||
|
simpleSettableFuture.get(5000, TimeUnit.MILLISECONDS),
|
||||||
|
exceptionHandler);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new RuntimeException(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,29 +12,61 @@ package com.facebook.react.common.futures;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A super simple Future-like class that can safely notify another Thread when a value is ready.
|
* A super simple Future-like class that can safely notify another Thread when a value is ready.
|
||||||
* Does not support setting errors or canceling.
|
* Does not support canceling.
|
||||||
*/
|
*/
|
||||||
public class SimpleSettableFuture<T> {
|
public class SimpleSettableFuture<T> implements Future<T> {
|
||||||
|
|
||||||
private final CountDownLatch mReadyLatch = new CountDownLatch(1);
|
private final CountDownLatch mReadyLatch = new CountDownLatch(1);
|
||||||
private volatile @Nullable T mResult;
|
private @Nullable T mResult;
|
||||||
|
private @Nullable Exception mException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the result. If another thread has called {@link #get}, they will immediately receive the
|
* Sets the result. If another thread has called {@link #get}, they will immediately receive the
|
||||||
* value. Must only be called once.
|
* value. set or setException must only be called once.
|
||||||
*/
|
*/
|
||||||
public void set(T result) {
|
public void set(T result) {
|
||||||
if (mReadyLatch.getCount() == 0) {
|
checkNotSet();
|
||||||
throw new RuntimeException("Result has already been set!");
|
|
||||||
}
|
|
||||||
mResult = result;
|
mResult = result;
|
||||||
mReadyLatch.countDown();
|
mReadyLatch.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the eception. If another thread has called {@link #get}, they will immediately receive the
|
||||||
|
* exception. set or setException must only be called once.
|
||||||
|
*/
|
||||||
|
public void setException(Exception exception) {
|
||||||
|
checkNotSet();
|
||||||
|
mException = exception;
|
||||||
|
mReadyLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone() {
|
||||||
|
return mReadyLatch.getCount() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
public T get() throws InterruptedException, ExecutionException {
|
||||||
|
throw new UnsupportedOperationException("Must use a timeout");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait up to the timeout time for another Thread to set a value on this future. If a value has
|
* Wait up to the timeout time for another Thread to set a value on this future. If a value has
|
||||||
* already been set, this method will return immediately.
|
* already been set, this method will return immediately.
|
||||||
|
@ -42,21 +74,26 @@ public class SimpleSettableFuture<T> {
|
||||||
* NB: For simplicity, we catch and wrap InterruptedException. Do NOT use this class if you
|
* NB: For simplicity, we catch and wrap InterruptedException. Do NOT use this class if you
|
||||||
* are in the 1% of cases where you actually want to handle that.
|
* are in the 1% of cases where you actually want to handle that.
|
||||||
*/
|
*/
|
||||||
public @Nullable T get(long timeoutMS) {
|
@Override
|
||||||
|
public @Nullable T get(long timeout, TimeUnit unit) throws
|
||||||
|
InterruptedException, ExecutionException, TimeoutException {
|
||||||
try {
|
try {
|
||||||
if (!mReadyLatch.await(timeoutMS, TimeUnit.MILLISECONDS)) {
|
if (!mReadyLatch.await(timeout, unit)) {
|
||||||
throw new TimeoutException();
|
throw new TimeoutException("Timed out waiting for result");
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
if (mException != null) {
|
||||||
|
throw new ExecutionException(mException);
|
||||||
|
}
|
||||||
|
|
||||||
return mResult;
|
return mResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TimeoutException extends RuntimeException {
|
private void checkNotSet() {
|
||||||
|
if (mReadyLatch.getCount() == 0) {
|
||||||
public TimeoutException() {
|
throw new RuntimeException("Result has already been set!");
|
||||||
super("Timed out waiting for future");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче