Introducing Scheduling of JS calls from native
Reviewed By: achen1 Differential Revision: D7729226 fbshipit-source-id: 9869e0a6a2b0c58b7538836ed2c13a4b28dd8887
This commit is contained in:
parent
4a9b2a7302
commit
e61341ba32
|
@ -2,7 +2,10 @@ load("//ReactNative:DEFS.bzl", "YOGA_TARGET", "react_native_dep", "react_native_
|
||||||
|
|
||||||
rn_android_library(
|
rn_android_library(
|
||||||
name = "fabric",
|
name = "fabric",
|
||||||
srcs = glob(["*.java"]),
|
srcs = glob([
|
||||||
|
"*.java",
|
||||||
|
"events/*.java",
|
||||||
|
]),
|
||||||
provided_deps = [
|
provided_deps = [
|
||||||
react_native_dep("third-party/android/support/v4:lib-support-v4"),
|
react_native_dep("third-party/android/support/v4:lib-support-v4"),
|
||||||
],
|
],
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.bridge.ReadableNativeMap;
|
import com.facebook.react.bridge.ReadableNativeMap;
|
||||||
import com.facebook.react.bridge.UIManager;
|
import com.facebook.react.bridge.UIManager;
|
||||||
import com.facebook.react.common.ReactConstants;
|
import com.facebook.react.common.ReactConstants;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||||
import com.facebook.react.modules.i18nmanager.I18nUtil;
|
import com.facebook.react.modules.i18nmanager.I18nUtil;
|
||||||
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||||
|
@ -48,7 +49,7 @@ import javax.annotation.Nullable;
|
||||||
* API.
|
* API.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused") // used from JNI
|
@SuppressWarnings("unused") // used from JNI
|
||||||
public class FabricUIManager implements UIManager {
|
public class FabricUIManager implements UIManager, JSHandler {
|
||||||
|
|
||||||
private static final String TAG = FabricUIManager.class.getSimpleName();
|
private static final String TAG = FabricUIManager.class.getSimpleName();
|
||||||
private static final boolean DEBUG = true;
|
private static final boolean DEBUG = true;
|
||||||
|
@ -475,4 +476,11 @@ public class FabricUIManager implements UIManager {
|
||||||
throw new RuntimeException(ex.getMessage(), t);
|
throw new RuntimeException(ex.getMessage(), t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(int instanceHandle, String name, WritableMap params) {
|
||||||
|
Log.e(TAG, "Invoking '" + name + "' on instanceHandle: '" + instanceHandle + "' from FabricUIManager.");
|
||||||
|
// -> call to C++
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2014-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
package com.facebook.react.fabric;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
|
|
||||||
|
public interface JSHandler {
|
||||||
|
|
||||||
|
void invoke(int instanceHandle, String name, WritableMap params);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.facebook.react.fabric;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class allows Native code to schedule work in JS.
|
||||||
|
* Work (following JS naming) represents a task that needs to be executed in JS.
|
||||||
|
*
|
||||||
|
* Four types of work are supported by this class:
|
||||||
|
*
|
||||||
|
* Synchronous:
|
||||||
|
* - Sync work -> flushSync
|
||||||
|
* - Work work -> flushSerial
|
||||||
|
*
|
||||||
|
* Asynchronous:
|
||||||
|
* - Interactive work (serial): -> scheduleSerial
|
||||||
|
* - Deferred work: -> scheduleWork
|
||||||
|
*/
|
||||||
|
public class Scheduler {
|
||||||
|
|
||||||
|
public Scheduler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleWork(Work work) {
|
||||||
|
// TODO T26717866 this method needs to be implemented. The current implementation is just for
|
||||||
|
// testing purpose.
|
||||||
|
work.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flushSync(Work work) {
|
||||||
|
// TODO T26717866 this method needs to be implemented. The current implementation is just for
|
||||||
|
// testing purpose.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flushSerial(Work work) {
|
||||||
|
// TODO T26717866 this method needs to be implemented. The current implementation is just for
|
||||||
|
// testing purpose.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleSerial(Work work) {
|
||||||
|
// TODO T26717866 this method needs to be implemented. The current implementation is just for
|
||||||
|
// testing purpose.
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2014-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.facebook.react.fabric;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that represents a task or piece of code that will be executed by {@link Scheduler}
|
||||||
|
* This follows React API naming for consistency.
|
||||||
|
*/
|
||||||
|
public interface Work {
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2014-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.facebook.react.fabric.events;
|
||||||
|
|
||||||
|
import static com.facebook.react.uimanager.events.TouchesHelper.CHANGED_TOUCHES_KEY;
|
||||||
|
import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_KEY;
|
||||||
|
import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_CANCEL_KEY;
|
||||||
|
import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_END_KEY;
|
||||||
|
import static com.facebook.react.uimanager.events.TouchesHelper.TOUCHES_KEY;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
import com.facebook.react.bridge.WritableArray;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
|
import com.facebook.react.bridge.WritableNativeArray;
|
||||||
|
import com.facebook.react.bridge.WritableNativeMap;
|
||||||
|
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.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ECLAIR)
|
||||||
|
public class FabricEventEmitter implements RCTEventEmitter {
|
||||||
|
|
||||||
|
private static final String TAG = FabricEventEmitter.class.getSimpleName();
|
||||||
|
|
||||||
|
private final FabricUIManager mFabricUIManager;
|
||||||
|
private final Scheduler mScheduler;
|
||||||
|
|
||||||
|
public FabricEventEmitter(Scheduler scheduler, FabricUIManager fabricUIManager) {
|
||||||
|
mScheduler = scheduler;
|
||||||
|
mFabricUIManager = fabricUIManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap params) {
|
||||||
|
//TODO get instanceHandle associated with targetTag.
|
||||||
|
int instanceHandle = targetTag;
|
||||||
|
mScheduler.scheduleWork(new FabricUIManagerWork(instanceHandle, eventName, params) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FabricUIManagerWork implements Work {
|
||||||
|
private final int mInstanceHandle;
|
||||||
|
private final String mEventName;
|
||||||
|
private final WritableMap mParams;
|
||||||
|
|
||||||
|
public FabricUIManagerWork(int instanceHandle, String eventName, @Nullable WritableMap params) {
|
||||||
|
mInstanceHandle = instanceHandle;
|
||||||
|
mEventName = eventName;
|
||||||
|
mParams = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mFabricUIManager.invoke(mInstanceHandle, mEventName, mParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receiveTouches(String eventTopLevelType, WritableArray touches,
|
||||||
|
WritableArray changedIndices) {
|
||||||
|
Pair<WritableArray, WritableArray> result =
|
||||||
|
TOP_TOUCH_END_KEY.equalsIgnoreCase(eventTopLevelType) ||
|
||||||
|
TOP_TOUCH_CANCEL_KEY.equalsIgnoreCase(eventTopLevelType)
|
||||||
|
? removeTouchesAtIndices(touches, changedIndices)
|
||||||
|
: touchSubsequence(touches, changedIndices);
|
||||||
|
|
||||||
|
WritableArray changedTouches = result.first;
|
||||||
|
touches = result.second;
|
||||||
|
|
||||||
|
for (int jj = 0; jj < changedTouches.size(); jj++) {
|
||||||
|
WritableMap touch = getWritableMap(changedTouches.getMap(jj));
|
||||||
|
// Touch objects can fulfill the role of `DOM` `Event` objects if we set
|
||||||
|
// the `changedTouches`/`touches`. This saves allocations.
|
||||||
|
touch.putArray(CHANGED_TOUCHES_KEY, changedTouches);
|
||||||
|
touch.putArray(TOUCHES_KEY, touches);
|
||||||
|
WritableMap nativeEvent = touch;
|
||||||
|
int rootNodeID = 0;
|
||||||
|
int target = nativeEvent.getInt(TARGET_KEY);
|
||||||
|
if (target < 1) {
|
||||||
|
Log.e(TAG,"A view is reporting that a touch occurred on tag zero.");
|
||||||
|
} else {
|
||||||
|
rootNodeID = target;
|
||||||
|
}
|
||||||
|
receiveEvent(rootNodeID, eventTopLevelType, touch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys `touches` by removing touch objects at indices `indices`. This is
|
||||||
|
* to maintain compatibility with W3C touch "end" events, where the active
|
||||||
|
* touches don't include the set that has just been "ended".
|
||||||
|
*
|
||||||
|
* This method was originally in ReactNativeRenderer.js
|
||||||
|
*
|
||||||
|
* TODO: this method is a copy from ReactNativeRenderer.removeTouchesAtIndices and it needs
|
||||||
|
* to be rewritten in a more efficient way,
|
||||||
|
*
|
||||||
|
* @param touches {@link WritableArray} Deserialized touch objects.
|
||||||
|
* @param indices {WritableArray} Indices to remove from `touches`.
|
||||||
|
* @return {Array<Touch>} Subsequence of removed touch objects.
|
||||||
|
*/
|
||||||
|
private Pair<WritableArray, WritableArray> removeTouchesAtIndices(WritableArray touches, WritableArray indices) {
|
||||||
|
WritableArray rippedOut = new WritableNativeArray();
|
||||||
|
// use an unsafe downcast to alias to nullable elements,
|
||||||
|
// so we can delete and then compact.
|
||||||
|
WritableArray tempTouches = new WritableNativeArray();
|
||||||
|
Set<Integer> rippedOutIndices = new HashSet<>();
|
||||||
|
for (int i = 0; i < indices.size(); i++) {
|
||||||
|
int index = indices.getInt(i);
|
||||||
|
rippedOut.pushMap(getWritableMap(touches.getMap(index)));
|
||||||
|
rippedOutIndices.add(index);
|
||||||
|
}
|
||||||
|
for (int j = 0 ; j < touches.size() ; j++) {
|
||||||
|
if (!rippedOutIndices.contains(j)) {
|
||||||
|
tempTouches.pushMap(getWritableMap(touches.getMap(j)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair<>(rippedOut, tempTouches);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects a subsequence of `Touch`es, without destroying `touches`.
|
||||||
|
*
|
||||||
|
* This method was originally in ReactNativeRenderer.js
|
||||||
|
*
|
||||||
|
* @param touches {@link WritableArray} Deserialized touch objects.
|
||||||
|
* @param changedIndices {@link WritableArray} Indices by which to pull subsequence.
|
||||||
|
* @return {Array<Touch>} Subsequence of touch objects.
|
||||||
|
*/
|
||||||
|
private Pair<WritableArray, WritableArray> touchSubsequence(WritableArray touches, WritableArray changedIndices) {
|
||||||
|
WritableArray result = new WritableNativeArray();
|
||||||
|
for (int i = 0; i < changedIndices.size(); i++) {
|
||||||
|
result.pushMap(getWritableMap(touches.getMap(i)));
|
||||||
|
}
|
||||||
|
return new Pair<>(result, touches);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: this is required because the WritableNativeArray.getMap() returns a ReadableMap instead
|
||||||
|
* of the original writableMap. this will change in the near future.
|
||||||
|
*
|
||||||
|
* @param readableMap {@link ReadableMap} source map
|
||||||
|
*/
|
||||||
|
private WritableMap getWritableMap(ReadableMap readableMap) {
|
||||||
|
WritableNativeMap map = new WritableNativeMap();
|
||||||
|
map.merge(readableMap);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,11 +17,15 @@ import com.facebook.react.uimanager.PixelUtil;
|
||||||
/**
|
/**
|
||||||
* Class responsible for generating catalyst touch events based on android {@link MotionEvent}.
|
* Class responsible for generating catalyst touch events based on android {@link MotionEvent}.
|
||||||
*/
|
*/
|
||||||
/*package*/ class TouchesHelper {
|
public class TouchesHelper {
|
||||||
|
|
||||||
|
public static final String TARGET_KEY = "target";
|
||||||
|
public static final String CHANGED_TOUCHES_KEY = "changedTouches";
|
||||||
|
public static final String TOUCHES_KEY = "touches";
|
||||||
|
public static final String TOP_TOUCH_END_KEY = "topTouchEnd";
|
||||||
|
public static final String TOP_TOUCH_CANCEL_KEY = "topTouchCancel";
|
||||||
private static final String PAGE_X_KEY = "pageX";
|
private static final String PAGE_X_KEY = "pageX";
|
||||||
private static final String PAGE_Y_KEY = "pageY";
|
private static final String PAGE_Y_KEY = "pageY";
|
||||||
private static final String TARGET_KEY = "target";
|
|
||||||
private static final String TIMESTAMP_KEY = "timestamp";
|
private static final String TIMESTAMP_KEY = "timestamp";
|
||||||
private static final String POINTER_IDENTIFIER_KEY = "identifier";
|
private static final String POINTER_IDENTIFIER_KEY = "identifier";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue