Binding for js events

Reviewed By: fkgozali

Differential Revision: D8181616

fbshipit-source-id: 5937c83f22ac09e3041fcb0f8d4e9e3026b2b397
This commit is contained in:
David Vacca 2018-05-30 21:49:20 -07:00 committed by Facebook Github Bot
parent 23fbd312aa
commit 0f10e03dd8
10 changed files with 92 additions and 44 deletions

View File

@ -261,7 +261,7 @@ public class ReactAppTestActivity extends FragmentActivity
List<ViewManager> viewManagers = List<ViewManager> viewManagers =
mReactInstanceManager.getOrCreateViewManagers(reactApplicationContext); mReactInstanceManager.getOrCreateViewManagers(reactApplicationContext);
FabricUIManager fabricUIManager = FabricUIManager fabricUIManager =
new FabricUIManager(reactApplicationContext, new ViewManagerRegistry(viewManagers)); new FabricUIManager(reactApplicationContext, new ViewManagerRegistry(viewManagers), jsContext);
new FabricJSCBinding().installFabric(jsContext, fabricUIManager); new FabricJSCBinding().installFabric(jsContext, fabricUIManager);
return fabricUIManager; return fabricUIManager;
} }

View File

@ -8,9 +8,24 @@
package com.facebook.react.fabric; package com.facebook.react.fabric;
import com.facebook.react.bridge.JavaScriptContextHolder; import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.NativeMap;
public interface FabricBinding { public interface FabricBinding {
void installFabric(JavaScriptContextHolder jsContext, FabricUIManager fabricModule); void installFabric(JavaScriptContextHolder jsContext, FabricUIManager fabricModule);
long createEventTarget(long jsContextNativePointer, long instanceHandlePointer);
void releaseEventTarget(long jsContextNativePointer, long eventTargetPointer);
void releaseEventHandler(long jsContextNativePointer, long eventHandlerPointer);
void dispatchEventToTarget(
long jsContextNativePointer,
long eventHandlerPointer,
long eventTargetPointer,
String type,
NativeMap payload
);
} }

View File

@ -14,15 +14,15 @@ import static android.view.View.MeasureSpec.UNSPECIFIED;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.GuardedRunnable;
import com.facebook.react.bridge.JavaScriptContextHolder; import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap; 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.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.common.ReactConstants;
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;
@ -39,10 +39,8 @@ import com.facebook.react.uimanager.common.MeasureSpecProvider;
import com.facebook.react.uimanager.common.SizeMonitoringFrameLayout; import com.facebook.react.uimanager.common.SizeMonitoringFrameLayout;
import com.facebook.yoga.YogaDirection; import com.facebook.yoga.YogaDirection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
@ -60,14 +58,16 @@ public class FabricUIManager implements UIManager, JSHandler {
private final ViewManagerRegistry mViewManagerRegistry; private final ViewManagerRegistry mViewManagerRegistry;
private final UIViewOperationQueue mUIViewOperationQueue; private final UIViewOperationQueue mUIViewOperationQueue;
private final NativeViewHierarchyManager mNativeViewHierarchyManager; private final NativeViewHierarchyManager mNativeViewHierarchyManager;
private final JavaScriptContextHolder mJSContext;
private volatile int mCurrentBatch = 0; private volatile int mCurrentBatch = 0;
private final FabricReconciler mFabricReconciler; private final FabricReconciler mFabricReconciler;
// TODO: Initialize new Binding (waiting for C++ implemenation to be landed)
private FabricBinding mBinding; private FabricBinding mBinding;
private JavaScriptContextHolder mContext; private long mEventHandlerPointer;
public FabricUIManager( public FabricUIManager(
ReactApplicationContext reactContext, ViewManagerRegistry viewManagerRegistry) { ReactApplicationContext reactContext,
ViewManagerRegistry viewManagerRegistry,
JavaScriptContextHolder jsContext) {
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext); DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext);
mReactApplicationContext = reactContext; mReactApplicationContext = reactContext;
mViewManagerRegistry = viewManagerRegistry; mViewManagerRegistry = viewManagerRegistry;
@ -76,10 +76,11 @@ public class FabricUIManager implements UIManager, JSHandler {
new UIViewOperationQueue( new UIViewOperationQueue(
reactContext, mNativeViewHierarchyManager, 0); reactContext, mNativeViewHierarchyManager, 0);
mFabricReconciler = new FabricReconciler(mUIViewOperationQueue); mFabricReconciler = new FabricReconciler(mUIViewOperationQueue);
mJSContext = jsContext;
} }
public void registerEventHandler(long eventHandlerPointer) { public void setBinding(FabricBinding binding) {
// TODO: Release this event handler at some point. mBinding = binding;
} }
/** Creates a new {@link ReactShadowNode} */ /** Creates a new {@link ReactShadowNode} */
@ -480,20 +481,39 @@ public class FabricUIManager implements UIManager, JSHandler {
} }
} }
@Override @Nullable
public void invoke(int instanceHandle, String name, WritableMap params) { public long createEventTarget(int reactTag) {
Log.e(TAG, "Invoking '" + name + "' on instanceHandle: '" + instanceHandle + "' from FabricUIManager.");
// -> call to C++
}
public long createEventTarget(int reactTag) throws IllegalStateException {
long instanceHandle = mNativeViewHierarchyManager.getInstanceHandle(reactTag); long instanceHandle = mNativeViewHierarchyManager.getInstanceHandle(reactTag);
if (instanceHandle == 0) { long context = mJSContext.get();
throw new IllegalStateException("View with reactTag " + reactTag + " does not exist."); long eventTarget = mBinding.createEventTarget(context, instanceHandle);
if (DEBUG) {
Log.e(
TAG,
"Created EventTarget: " + eventTarget + " for tag: " + reactTag + " with instanceHandle: " + instanceHandle);
} }
return eventTarget;
// TODO: uncomment after diff including Binding is landed
// return mBinding.createEventTarget(mContext.get(), instanceHandle);
return 0;
} }
public void registerEventHandler(long eventHandlerPointer) {
mEventHandlerPointer = eventHandlerPointer;
}
public void releaseEventTarget(long eventTargetPointer) {
mBinding.releaseEventTarget(mJSContext.get(), eventTargetPointer);
}
public void releaseEventHandler(long eventHandlerPointer) {
mBinding.releaseEventHandler(mJSContext.get(), eventHandlerPointer);
}
@Override
public void invoke(long eventTarget, String name, WritableMap params) {
if (DEBUG) {
Log.e(
TAG,
"Dispatching event for target: " + eventTarget);
}
mBinding.dispatchEventToTarget(mJSContext.get(), mEventHandlerPointer, eventTarget, name, (WritableNativeMap) params);
}
} }

View File

@ -10,6 +10,6 @@ import com.facebook.react.bridge.WritableMap;
public interface JSHandler { public interface JSHandler {
void invoke(int instanceHandle, String name, WritableMap params); void invoke(long instanceHandle, String name, WritableMap params);
} }

View File

@ -49,9 +49,8 @@ public class FabricEventEmitter implements RCTEventEmitter, Closeable {
@Override @Override
public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap params) { public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap params) {
//TODO get instanceHandle associated with targetTag. long eventTarget = mFabricUIManager.createEventTarget(targetTag);
int instanceHandle = targetTag; mScheduler.scheduleWork(new FabricUIManagerWork(eventTarget, eventName, params));
mScheduler.scheduleWork(new FabricUIManagerWork(instanceHandle, eventName, params) );
} }
@Override @Override
@ -60,19 +59,26 @@ public class FabricEventEmitter implements RCTEventEmitter, Closeable {
} }
private class FabricUIManagerWork implements Work { private class FabricUIManagerWork implements Work {
private final int mInstanceHandle; private final long mEventTarget;
private final String mEventName; private final String mEventName;
private final WritableMap mParams; private final WritableMap mParams;
public FabricUIManagerWork(int instanceHandle, String eventName, @Nullable WritableMap params) { public FabricUIManagerWork(long eventTarget, String eventName, @Nullable WritableMap params) {
mInstanceHandle = instanceHandle; mEventTarget = eventTarget;
mEventName = eventName; mEventName = eventName;
mParams = params; mParams = params;
} }
@Override @Override
public void run() { public void run() {
mFabricUIManager.invoke(mInstanceHandle, mEventName, mParams); try {
mFabricUIManager.invoke(mEventTarget, mEventName, mParams);
} catch (Throwable t) {
Log.e(TAG, "Error sending event " + mEventName, t);
//TODO: manage exception properly
} finally{
mFabricUIManager.releaseEventTarget(mEventTarget);
}
} }
} }

View File

@ -28,13 +28,17 @@ public class FabricJSCBinding implements FabricBinding {
private static native HybridData initHybrid(); private static native HybridData initHybrid();
private native long createEventTarget(long jsContextNativePointer, long instanceHandlePointer); @Override
public native long createEventTarget(long jsContextNativePointer, long instanceHandlePointer);
private native void releaseEventTarget(long jsContextNativePointer, long eventTargetPointer); @Override
public native void releaseEventTarget(long jsContextNativePointer, long eventTargetPointer);
private native void releaseEventHandler(long jsContextNativePointer, long eventHandlerPointer); @Override
public native void releaseEventHandler(long jsContextNativePointer, long eventHandlerPointer);
private native void dispatchEventToTarget( @Override
public native void dispatchEventToTarget(
long jsContextNativePointer, long jsContextNativePointer,
long eventHandlerPointer, long eventHandlerPointer,
long eventTargetPointer, long eventTargetPointer,
@ -50,6 +54,7 @@ public class FabricJSCBinding implements FabricBinding {
@Override @Override
public void installFabric(JavaScriptContextHolder jsContext, FabricUIManager fabricModule) { public void installFabric(JavaScriptContextHolder jsContext, FabricUIManager fabricModule) {
fabricModule.setBinding(this);
installFabric(jsContext.get(), fabricModule); installFabric(jsContext.get(), fabricModule);
} }
} }

View File

@ -222,17 +222,15 @@ public class NativeViewHierarchyManager {
@Nullable @Nullable
@TargetApi(Build.VERSION_CODES.DONUT) @TargetApi(Build.VERSION_CODES.DONUT)
public long getInstanceHandle(int reactTag) { public long getInstanceHandle(int reactTag) {
UiThreadUtil.assertOnUiThread();
View view = mTagsToViews.get(reactTag); View view = mTagsToViews.get(reactTag);
if (view == null) { if (view == null) {
throw new IllegalArgumentException("Unable to find view for tag: " + reactTag); throw new IllegalArgumentException("Unable to find view for tag: " + reactTag);
} }
Long tag = (Long) view.getTag(R.id.view_tag_instance_handle); Long instanceHandle = (Long) view.getTag(R.id.view_tag_instance_handle);
if (tag == null) { if (instanceHandle == null) {
throw new IllegalArgumentException("Unable to find instanceHandle for tag: " + reactTag); throw new IllegalArgumentException("Unable to find instanceHandle for tag: " + reactTag);
} }
return tag; return instanceHandle;
} }
private void updateLayout(View viewToUpdate, int x, int y, int width, int height) { private void updateLayout(View viewToUpdate, int x, int y, int width, int height) {

View File

@ -115,7 +115,7 @@ public class EventDispatcher implements LifecycleEventListener,
for (EventDispatcherListener listener : mListeners) { for (EventDispatcherListener listener : mListeners) {
listener.onEventDispatch(event); listener.onEventDispatch(event);
} }
synchronized (mEventsStagingLock) { synchronized (mEventsStagingLock) {
mEventStaging.add(event); mEventStaging.add(event);
Systrace.startAsyncFlow( Systrace.startAsyncFlow(

View File

@ -5,6 +5,7 @@ import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import com.facebook.react.bridge.CatalystInstance; import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactTestHelper; import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.uimanager.NativeViewHierarchyManager; import com.facebook.react.uimanager.NativeViewHierarchyManager;
@ -41,7 +42,8 @@ public class FabricReconcilerTest {
reactContext.initializeWithInstance(catalystInstance); reactContext.initializeWithInstance(catalystInstance);
List<ViewManager> viewManagers = new ArrayList<>(); List<ViewManager> viewManagers = new ArrayList<>();
ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagers); ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagers);
mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry); JavaScriptContextHolder jsContext = mock(JavaScriptContextHolder.class);
mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry, jsContext);
mMockUIViewOperationQueue = new MockUIViewOperationQueue(reactContext); mMockUIViewOperationQueue = new MockUIViewOperationQueue(reactContext);
mFabricReconciler = new FabricReconciler(mMockUIViewOperationQueue); mFabricReconciler = new FabricReconciler(mMockUIViewOperationQueue);
} }

View File

@ -3,8 +3,10 @@ package com.facebook.react.fabric;
import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.assertThat;
import static com.facebook.react.bridge.InstanceHandleHelper.randomInstanceHandle; import static com.facebook.react.bridge.InstanceHandleHelper.randomInstanceHandle;
import static org.mockito.Mockito.mock;
import com.facebook.react.ReactRootView; import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactTestHelper; import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.bridge.ReadableNativeMap;
@ -49,8 +51,8 @@ public class FabricUIManagerTest {
Arrays.<ViewManager>asList( Arrays.<ViewManager>asList(
new ReactViewManager(), new ReactTextViewManager(), new ReactRawTextManager()); new ReactViewManager(), new ReactTextViewManager(), new ReactRawTextManager());
ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagers); ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagers);
JavaScriptContextHolder jsContext = mock(JavaScriptContextHolder.class);
mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry); mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry, jsContext);
} }
@Test @Test