From 82b8a9221ace6e13340d9e0dba27f69dca4edd99 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Thu, 3 May 2018 08:38:03 -0700 Subject: [PATCH] Add hybrid class to hold native delta client Summary: Adds hybrid JNI wrapper class that can hold a native delta client. This class allows Java code in the `devsupport` package to hold onto a native delta client across reloads. It also takes care of passing `okhttp` response objects to native code, where we build a `std::string` to hold JSON messages coming from Metro, and parse them with `folly::parseJson`. In a follow-up, we will switch to a streaming-friendly binary format that will make allocating memory for the whole message unnecessary. Reviewed By: fromcelticpark Differential Revision: D7845138 fbshipit-source-id: 7a29b30645331addf843097dd0d05c03b3143991 --- .../react/bridge/NativeDeltaClient.java | 25 +++++++++ .../src/main/jni/react/jni/Android.mk | 1 + .../main/jni/react/jni/NativeDeltaClient.cpp | 54 +++++++++++++++++++ .../main/jni/react/jni/NativeDeltaClient.h | 38 +++++++++++++ .../src/main/jni/react/jni/OnLoad.cpp | 2 + 5 files changed, 120 insertions(+) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/bridge/NativeDeltaClient.java create mode 100644 ReactAndroid/src/main/jni/react/jni/NativeDeltaClient.cpp create mode 100644 ReactAndroid/src/main/jni/react/jni/NativeDeltaClient.h diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeDeltaClient.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeDeltaClient.java new file mode 100644 index 000000000..6b87c972c --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeDeltaClient.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2018-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.bridge; + +import java.nio.channels.ReadableByteChannel; + +import com.facebook.jni.HybridData; + +public class NativeDeltaClient { + static { + ReactBridge.staticInit(); + } + + // C++ parts + private final HybridData mHybridData = initHybrid(); + private native static HybridData initHybrid(); + + public native void reset(); + public native void processDelta(ReadableByteChannel deltaMessage); +} diff --git a/ReactAndroid/src/main/jni/react/jni/Android.mk b/ReactAndroid/src/main/jni/react/jni/Android.mk index 8d8b992d5..313950674 100644 --- a/ReactAndroid/src/main/jni/react/jni/Android.mk +++ b/ReactAndroid/src/main/jni/react/jni/Android.mk @@ -18,6 +18,7 @@ LOCAL_SRC_FILES := \ ModuleRegistryBuilder.cpp \ NativeArray.cpp \ NativeCommon.cpp \ + NativeDeltaClient.cpp \ NativeMap.cpp \ OnLoad.cpp \ ProxyExecutor.cpp \ diff --git a/ReactAndroid/src/main/jni/react/jni/NativeDeltaClient.cpp b/ReactAndroid/src/main/jni/react/jni/NativeDeltaClient.cpp new file mode 100644 index 000000000..2c35306ca --- /dev/null +++ b/ReactAndroid/src/main/jni/react/jni/NativeDeltaClient.cpp @@ -0,0 +1,54 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include "NativeDeltaClient.h" + +#include +#include +#include + +namespace facebook { +namespace react { + +jni::local_ref NativeDeltaClient::initHybrid( + jni::alias_ref) { + return makeCxxInstance(); +} + +void NativeDeltaClient::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", NativeDeltaClient::initHybrid), + makeNativeMethod("processDelta", NativeDeltaClient::jniProcessDelta), + makeNativeMethod("reset", NativeDeltaClient::jniReset), + }); +} + +void NativeDeltaClient::jniProcessDelta( + jni::alias_ref delta) { + + std::ostringstream deltaMessage; + std::vector buffer(8192); + auto byteBuffer = jni::JByteBuffer::wrapBytes(buffer.data(), buffer.size()); + + size_t pos = 0; + int read = 0; + do { + read = delta->read(byteBuffer); + if (read < 1) { + deltaMessage.write(reinterpret_cast(buffer.data()), pos); + byteBuffer->rewind(); + pos = 0; + } else { + pos += read; + } + } while (read != -1); + + + deltaClient_->patch(folly::parseJson(deltaMessage.str())); +} + +void NativeDeltaClient::jniReset() { + deltaClient_->clear(); +} + +} // namespace react +} // namespace facebook diff --git a/ReactAndroid/src/main/jni/react/jni/NativeDeltaClient.h b/ReactAndroid/src/main/jni/react/jni/NativeDeltaClient.h new file mode 100644 index 000000000..5e2eea14a --- /dev/null +++ b/ReactAndroid/src/main/jni/react/jni/NativeDeltaClient.h @@ -0,0 +1,38 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class NativeDeltaClient : public jni::HybridClass { + +public: + static constexpr auto kJavaDescriptor = + "Lcom/facebook/react/bridge/NativeDeltaClient;"; + static jni::local_ref initHybrid(jni::alias_ref); + static void registerNatives(); + + ~NativeDeltaClient() override = default; + + std::shared_ptr getDeltaClient() { + return deltaClient_; + } + +private: + friend HybridBase; + void jniProcessDelta(jni::alias_ref delta); + void jniReset(); + const std::shared_ptr deltaClient_ = + std::make_shared(); +}; + +} // namespace react +} // namespace facebook diff --git a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp index ec97e56e5..81cc8f334 100644 --- a/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp +++ b/ReactAndroid/src/main/jni/react/jni/OnLoad.cpp @@ -16,6 +16,7 @@ #include "CxxModuleWrapper.h" #include "JavaScriptExecutorHolder.h" #include "JCallback.h" +#include "NativeDeltaClient.h" #include "ProxyExecutor.h" #include "WritableNativeArray.h" #include "WritableNativeMap.h" @@ -91,6 +92,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { CxxModuleWrapper::registerNatives(); JCxxCallbackImpl::registerNatives(); NativeArray::registerNatives(); + NativeDeltaClient::registerNatives(); ReadableNativeArray::registerNatives(); WritableNativeArray::registerNatives(); NativeMap::registerNatives();