First implementation of scheduleWork method

Reviewed By: shergin

Differential Revision: D7799412

fbshipit-source-id: b78a0bc0e80868f6877a31f862d7e6104fd4a049
This commit is contained in:
David Vacca 2018-05-30 21:48:57 -07:00 committed by Facebook Github Bot
parent 58ea20b5e8
commit a04ad8d8fb
5 changed files with 97 additions and 24 deletions

View File

@ -1,5 +1,13 @@
package com.facebook.react.fabric;
import android.util.Log;
import com.facebook.react.bridge.ReactContext;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* This class allows Native code to schedule work in JS.
* Work (following JS naming) represents a task that needs to be executed in JS.
@ -7,23 +15,59 @@ package com.facebook.react.fabric;
* Four types of work are supported by this class:
*
* Synchronous:
* - Sync work -> flushSync
* - Work work -> flushSerial
* - Sync work -> flushSync()
* - Work work -> flushSerial()
*
* Asynchronous:
* - Interactive work (serial): -> scheduleSerial
* - Deferred work: -> scheduleWork
* - Interactive work (serial): -> scheduleSerial()
* - Deferred work: -> scheduleWork()
*
*/
public class Scheduler {
public Scheduler() {
private static final String TAG = Scheduler.class.getSimpleName();
// The usage of this executor might change in the near future.
private final ExecutorService mExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>());
private final ReactContext mReactContext;
public Scheduler(ReactContext reactContext) {
mReactContext = reactContext;
}
public void scheduleWork(Work work) {
// TODO T26717866 this method needs to be implemented. The current implementation is just for
// testing purpose.
work.run();
/**
* This method schedules work to be executed with the lowest priority in the JS Thread.
*
* The current implementation queues "work"s in an unbounded queue tight to a SingleThreadExecutor.
* Work objects are going to be submitted one-by-one at the end of the JS Queue Thread.
*
* Notice that the current implementation might experience some delays in JS work execution,
* depending on the size of the JS Queue and the time it takes to execute each work in JS.
*
* TODO: This implementation is very likely to change in the near future.
*/
public void scheduleWork(final Work work) {
try {
mExecutor.execute(new Runnable() {
@Override
public void run() {
mReactContext.runOnJSQueueThread(new Runnable() {
@Override
public void run() {
try {
work.run();
} catch (Exception ex) {
Log.w(TAG, "Exception running work in JS.", ex);
throw ex;
}
}
});
}
});
} catch (RejectedExecutionException ex) {
// This can happen if a Work is scheduled when the Scheduler is being shutdown.
// For now, we log and do not take any action.
Log.i(TAG, "Unable to schedule task.");
}
}
public void flushSync(Work work) {
@ -40,4 +84,12 @@ public class Scheduler {
// TODO T26717866 this method needs to be implemented. The current implementation is just for
// testing purpose.
}
/**
* Shutdowns the {@link Scheduler}. this operation will attempt to stop all "active executing"
* Works items and it will "halts:" all the Works items waiting to be executed.
*/
public void shutdown() {
mExecutor.shutdownNow();
}
}

View File

@ -17,6 +17,7 @@ import android.annotation.TargetApi;
import android.os.Build;
import android.util.Log;
import android.util.Pair;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
@ -26,21 +27,23 @@ import com.facebook.react.fabric.FabricUIManager;
import com.facebook.react.fabric.Scheduler;
import com.facebook.react.fabric.Work;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
@TargetApi(Build.VERSION_CODES.ECLAIR)
public class FabricEventEmitter implements RCTEventEmitter {
public class FabricEventEmitter implements RCTEventEmitter, Closeable {
private static final String TAG = FabricEventEmitter.class.getSimpleName();
private final FabricUIManager mFabricUIManager;
private final Scheduler mScheduler;
public FabricEventEmitter(Scheduler scheduler, FabricUIManager fabricUIManager) {
mScheduler = scheduler;
public FabricEventEmitter(ReactApplicationContext context, FabricUIManager fabricUIManager) {
mScheduler = new Scheduler(context);
mFabricUIManager = fabricUIManager;
}
@ -51,6 +54,11 @@ public class FabricEventEmitter implements RCTEventEmitter {
mScheduler.scheduleWork(new FabricUIManagerWork(instanceHandle, eventName, params) );
}
@Override
public void close() {
mScheduler.shutdown();
}
private class FabricUIManagerWork implements Work {
private final int mInstanceHandle;
private final String mEventName;

View File

@ -97,7 +97,7 @@ public class EventDispatcher implements LifecycleEventListener,
private Event[] mEventsToDispatch = new Event[16];
private int mEventsToDispatchSize = 0;
private volatile @Nullable ReactEventEmitter mRCTEventEmitter = new ReactEventEmitter();
private volatile @Nullable ReactEventEmitter mReactEventEmitter = new ReactEventEmitter();
private short mNextEventTypeId = 0;
private volatile boolean mHasDispatchScheduled = false;
@ -123,7 +123,7 @@ public class EventDispatcher implements LifecycleEventListener,
event.getEventName(),
event.getUniqueID());
}
if (mRCTEventEmitter != null) {
if (mReactEventEmitter != null) {
// If the host activity is paused, the frame callback may not be currently
// posted. Ensure that it is so that this event gets delivered promptly.
mCurrentFrameCallback.maybePostFromNonUI();
@ -162,6 +162,7 @@ public class EventDispatcher implements LifecycleEventListener,
@Override
public void onHostDestroy() {
stopFrameCallback();
mReactEventEmitter.stop();
}
public void onCatalystInstanceDestroyed() {
@ -251,7 +252,7 @@ public class EventDispatcher implements LifecycleEventListener,
}
public void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter) {
mRCTEventEmitter.register(uiManagerType, eventEmitter);
mReactEventEmitter.register(uiManagerType, eventEmitter);
}
private class ScheduleDispatchFrameCallback extends ChoreographerCompat.FrameCallback {
@ -331,7 +332,7 @@ public class EventDispatcher implements LifecycleEventListener,
"ScheduleDispatchFrameCallback",
mHasDispatchScheduledCount.getAndIncrement());
mHasDispatchScheduled = false;
Assertions.assertNotNull(mRCTEventEmitter);
Assertions.assertNotNull(mReactEventEmitter);
synchronized (mEventsToDispatchLock) {
// We avoid allocating an array and iterator, and "sorting" if we don't need to.
// This occurs when the size of mEventsToDispatch is zero or one.
@ -348,7 +349,7 @@ public class EventDispatcher implements LifecycleEventListener,
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
event.getEventName(),
event.getUniqueID());
event.dispatch(mRCTEventEmitter);
event.dispatch(mReactEventEmitter);
event.dispose();
}
clearEventsToDispatch();

View File

@ -9,20 +9,23 @@ package com.facebook.react.uimanager.events;
import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_KEY;
import android.util.Log;
import android.util.SparseArray;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.common.UIManagerType;
import com.facebook.react.uimanager.common.ViewUtil;
import java.io.Closeable;
import java.io.IOException;
import javax.annotation.Nullable;
public class ReactEventEmitter implements RCTEventEmitter {
private static final String TAG = ReactEventEmitter.class.getSimpleName();
private final SparseArray<RCTEventEmitter> mEventEmitters = new SparseArray<>();
public ReactEventEmitter() {
}
public ReactEventEmitter() { }
public void register(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter) {
mEventEmitters.put(uiManagerType, eventEmitter);
@ -50,4 +53,16 @@ public class ReactEventEmitter implements RCTEventEmitter {
return mEventEmitters.get(type);
}
public void stop() {
for (int i = 0 ; i < mEventEmitters.size() ; i++) {
RCTEventEmitter eventEmitter = mEventEmitters.valueAt(i);
if (eventEmitter instanceof Closeable) {
try {
((Closeable) eventEmitter).close();
} catch (IOException e) {
Log.i(TAG, "Exception when closing EventEmitter: " + eventEmitter, e);
}
}
}
}
}

View File

@ -7,17 +7,14 @@
package com.facebook.react.views.image;
import javax.annotation.Nullable;
import android.support.annotation.IntDef;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nullable;
public class ImageLoadEvent extends Event<ImageLoadEvent> {
@IntDef({ON_ERROR, ON_LOAD, ON_LOAD_END, ON_LOAD_START, ON_PROGRESS})