Android React-Native通信数据模型分析

无论是计算机领域还是日常生活中,我们所言的通信,其核心都是数据信息的交换,而数据模型的优劣对通信效率有着决定性的作用。

在React-Native项目中,Javascript语言与Native两种语言(Java或OC等)间存在着大量的数据交换,也就是所谓的通信。众所周知,移动APP对性能的要求无比苛刻,如果通信数据模型设计地不合理,很可能引起多线程下的数据安全问题,以及应用性能问题,比如内存泄漏,UI绘制缓慢等。

前面几篇博客我们详细分析过React-Native的通信机制,主要有两个方向: Java->Bridge->Javascript和Javascript->Bridge->Java。所以,真正的数据交换其实发生在Java与Bridge,Javascript与Bridge两个环节。

Javascript与Bridge间的数据通信是借助于Webkit使用Json完成,简单实用,水到渠成,不多分析。而Java与Bridge间的数据通信相比之下就复杂多了,作为真正运行在设备上的程序语言,这恰恰是决定整个通信过程效率高低最核心的一环,也是本篇博客研究的内容。

Java是Android应用程序的本地开发语言,而Bridge是使用C++开发的动态链接库,由Java语言通过JNI的方式调用。Java与Bridge间的数据通信,实质是Java和C++两种程序语言间的数据传输,而传递的方向又分为两个场景:Java传输数据给C++ 和C++ 传输数据给Java。

我们先来看第一种场景。

Java主动向Javascript通信,主要是通过ReactBridge.java类的callFunction方法,将需要调用的组件(moduleId)、功能(methodId)、数据(arguments)三者传递到Bridge。

package com.facebook.react.bridge;

public class ReactBridge extends Countable {

 static final String REACT_NATIVE_LIB = "reactnativejni";

 static {
  SoLoader.loadLibrary(REACT_NATIVE_LIB);
 }

 ...

 public native void callFunction(int moduleId, int methodId, NativeArray arguments);

 ...

}

我们可以看到,传输的数据类型是NativeArray,来瞧下具体的代码,位于com.facebook.react.bridge包下:

public abstract class NativeArray {
 static {
  SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB);
 }

 protected NativeArray(HybridData hybridData) {
  mHybridData = hybridData;
 }

 @Override
 public native String toString();

 @DoNotStrip
 private HybridData mHybridData;
}

NativeArray是一个抽象类,其中,只有一个HybridData类型成员变量,由其构造方法赋值初始化。

NativeArray还有一个名为ReadableNativeArray的直接子类,和一个名为WritableNativeArray的间接子类,后者是继承于前者。顾名思义,一个是用于读数据,一个是用于写数据。

Java向Bridge传输数据,自然就是写数据了,所以我们先来看WritableNativeArray。

package com.facebook.react.bridge;

public class WritableNativeArray extends ReadableNativeArray implements WritableArray {

 static {
  SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB);
 }

 public WritableNativeArray() {
  super(initHybrid());
 }

 @Override
 public native void pushNull();
 @Override
 public native void pushBoolean(boolean value);
 @Override
 public native void pushDouble(double value);
 @Override
 public native void pushInt(int value);
 @Override
 public native void pushString(String value);

 @Override
 public void pushArray(WritableArray array) {
  Assertions.assertCondition(
    array == null || array instanceof WritableNativeArray, "Illegal type provided");
  pushNativeArray((WritableNativeArray) array);
 }

 @Override
 public void pushMap(WritableMap map) {
  Assertions.assertCondition(
    map == null || map instanceof WritableNativeMap, "Illegal type provided");
  pushNativeMap((WritableNativeMap) map);
 }

 private native static HybridData initHybrid();
 private native void pushNativeArray(WritableNativeArray array);
 private native void pushNativeMap(WritableNativeMap map);
}

里面有7个写数据的native方法,涵盖了int、string,array,map等不同的数据类型和结构。

还有一个名为initHybrid()的native方法,用于创建HybridData类的实例,然后通过构造方法给其超父类NativeArray的mHybridData成员变量赋值,具体作用后面来分析,先略过。

接下来,我们来验证一下WritableNativeArray是否是真正传输给Bridge的数据类型。

Java调用Javascript组件,都是由名为JavaScriptModuleInvocationHandler的动态代理类统一拦截处理的吗?来回顾一下代码,位于com.facebook.react.bridge.JavaScriptModuleRegistry.java:

 private static class JavaScriptModuleInvocationHandler implements InvocationHandler {

  private final CatalystInstanceImpl mCatalystInstance;
  private final JavaScriptModuleRegistration mModuleRegistration;

  public JavaScriptModuleInvocationHandler(
    CatalystInstanceImpl catalystInstance,
    JavaScriptModuleRegistration moduleRegistration) {
   mCatalystInstance = catalystInstance;
   mModuleRegistration = moduleRegistration;
  }

  @Override
  public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   String tracingName = mModuleRegistration.getTracingName(method);
   mCatalystInstance.callFunction(
     mModuleRegistration.getModuleId(),
     mModuleRegistration.getMethodId(method),
     Arguments.fromJavaArgs(args),
     tracingName);
   return null;
  }
 }

callFunction方法传递的参数类型是Arguments.fromJavaArgs(args),具体代码又如下:

 public static WritableNativeArray fromJavaArgs(Object[] args) {
  WritableNativeArray arguments = new WritableNativeArray();
  for (int i = 0; i < args.length; i++) {
   Object argument = args[i];
   if (argument == null) {
    arguments.pushNull();
    continue;
   }

   Class argumentClass = argument.getClass();
   if (argumentClass == Boolean.class) {
    arguments.pushBoolean(((Boolean) argument).booleanValue());
   } else if (argumentClass == Integer.class) {
    arguments.pushDouble(((Integer) argument).doubleValue());
   } else if (argumentClass == Double.class) {
    arguments.pushDouble(((Double) argument).doubleValue());
   } else if (argumentClass == Float.class) {
    arguments.pushDouble(((Float) argument).doubleValue());
   } else if (argumentClass == String.class) {
    arguments.pushString(argument.toString());
   } else if (argumentClass == WritableNativeMap.class) {
    arguments.pushMap((WritableNativeMap) argument);
   } else if (argumentClass == WritableNativeArray.class) {
    arguments.pushArray((WritableNativeArray) argument);
   } else {
    throw new RuntimeException("Cannot convert argument of type " + argumentClass);
   }
  }
  return arguments;
 }

正如我们猜测的一般,fromJavaArgs静态方法返回的是一个新创建的WritableNativeArray对象实例,然后按照数据类型,调用相应的push方法。有些特殊的是,int型和float型都当成了double型来处理,这样做并不会造成数据的损害。

刚刚说到,WritableNativeArray的所有写入数据的方法都是native方法,即Java层面的通信数据全部是直接写入到Bridge层的,换言之,WritableNativeArray仅仅起到了数据传输管道的作用。这样做,有两个好处:

1、数据只在C++存有一份,这样避免了数据具有多个副本,节省了一部分的内存。

2、减小对WritableNativeArray对象的依赖,使其容易释放,可以由虚拟机GC自动回收内存。

那么,在Bridge层中,C++又是如何处理push过来的数据的呢?

先来看一下WritableNativeArray中native方法在JNI中动态注册的代码,位于react/jni/OnLoad.cpp中:

static void registerNatives() {
  jni::registerNatives("com/facebook/react/bridge/WritableNativeArray", {
    makeNativeMethod("initHybrid", WritableNativeArray::initHybrid),
    makeNativeMethod("pushNull", WritableNativeArray::pushNull),
    makeNativeMethod("pushBoolean", WritableNativeArray::pushBoolean),
    makeNativeMethod("pushDouble", WritableNativeArray::pushDouble),
    makeNativeMethod("pushInt", WritableNativeArray::pushInt),
    makeNativeMethod("pushString", WritableNativeArray::pushString),
    makeNativeMethod("pushNativeArray", WritableNativeArray::pushNativeArray),
    makeNativeMethod("pushNativeMap", "(Lcom/facebook/react/bridge/WritableNativeMap;)V",
             WritableNativeArray::pushNativeMap),
  });
 }

很明显,在C++中也存在着一个名为WritableNativeArray的类,具有与着native方法相对应的方法,巧的是,它也是继承于ReadableNativeArray类(注意HybridClass模板类的第二个泛型表示父类):

struct WritableNativeArray
  : public jni::HybridClass<WritableNativeArray, ReadableNativeArray> {
 static constexpr const char* kJavaDescriptor = "Lcom/facebook/react/bridge/WritableNativeArray;";

 WritableNativeArray()
   : HybridBase(folly::dynamic({})) {}

 static local_ref<jhybriddata> initHybrid(alias_ref<jclass>) {
  return makeCxxInstance();
 }

 void pushNull() {
  ...
  array.push_back(nullptr);
 }

 void pushBoolean(jboolean value) {
  ...
  array.push_back(value == JNI_TRUE);
 }

 void pushDouble(jdouble value) {
  ...
  array.push_back(value);
 }

 void pushInt(jint value) {
  ...
  array.push_back(value);
 }

 void pushString(jstring value) {
  ...
  array.push_back(wrap_alias(value)->toStdString());
 }

 void pushNativeArray(WritableNativeArray* otherArray) {
  ...
  array.push_back(std::move(otherArray->array));
  otherArray->isConsumed = true;
 }

 void pushNativeMap(jobject jmap) {
  ...
  array.push_back(std::move(map->map));
  map->isConsumed = true;
 }
 ...
}

看到这里,我们不禁会猜测,C++中的ReadableNativeArray类很可能也是继承于NativeArray。

当然,事实确实是这样的。在C++中存在着与Java中完全呼应的三个类:NativeArray、ReadableNativeArray、WritableNativeArray,命名和继承关系都是完全一致的!

而且可以看到,所有的数据都被存储到父类NativeArray的array变量中。

不过,问题来了!

C++中的WritableNativeArray对象和Java中的WritableNativeArray两个同名对象间是否存在着某种联系呢,比如一一映射的关系?

答案是肯定的! 因为每当一个Java层的WritableNativeArray对象被创建,在C++层都会有一个相应的WritableNativeArray对象被创建,用来接收Java层push过来的数据。

再来回顾下WritableNativeArray.java创建的过程。

public class WritableNativeArray extends ReadableNativeArray implements WritableArray {
  ...
  public WritableNativeArray() {
   super(initHybrid());
  }
  ...
  private native static HybridData initHybrid();
  ...
}

在构造WritableNativeArray的时候,会通过initHybrid方法创建一个HybridData对象,并保存到其超父类NativeArray的成员变量mHybridData中。

而HybridData对象又是什么呢?

public class HybridData {
  // Private C++ instance
  private long mNativePointer = 0;
  public HybridData() {
    Prerequisites.ensure();
  }
  public native void resetNative();

  protected void finalize() throws Throwable {
   resetNative();
   super.finalize();
  }
}
public class Prerequisites {
  ...
  public static void ensure() {
    SoLoader.loadLibrary("fbjni");
  }
  ...
}

构造函数中Prerequisites.ensure(),是用来加载fbjni动态链接库的。

在HybridData 类中,有一个long的私有成员变量,根据注释和名字可以猜测与C++指针相关,具体是不是这样呢?我们来看HybridData对象通过initHybrid()初始化的过程。

代码位于react/jni/OnLoad.cpp中:

struct WritableNativeArray
  : public jni::HybridClass<WritableNativeArray, ReadableNativeArray> {
  ...

 static local_ref<jhybriddata> initHybrid(alias_ref<jclass>) {
   return makeCxxInstance();
 }

  ...
}

这里的jhybriddata指的就是HybridData(Java)对象,其是通过typedef方式定义在jni/first-party/jni/fbjni/Hybrid.h中的。

...

struct HybridData : public JavaClass<HybridData> {
 constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
 void setNativePointer(std::unique_ptr<BaseHybridClass> new_value);
 BaseHybridClass* getNativePointer();
 static local_ref<HybridData> create();
};

...

typedef detail::HybridData::javaobject jhybriddata;

...

facebook在这里对在JNI中创建Java对象的过程做了非常高效的封装,即JavaClass对象。所有JavaClass的子类都通过一个名为kJavaDescriptor的字符串指针,来描述相对应的Java对象类名。

继续来看makeCxxInstance()是如何创建HybridData(Java) 对象的。代码同样在jni/first-party/jni/fbjni/Hybrid.h中。

class HybridClass : public detail::HybridTraits<Base>::CxxBase {
  ...

 static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
  auto hybridData = detail::HybridData::create();
  hybridData->setNativePointer(std::move(cxxPart));
  return hybridData;
 }

 template <typename... Args>
 static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
  return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
 }

  ...
}

结合下前面的WritableNativeArray(C++)来看

struct WritableNativeArray
  : public jni::HybridClass<WritableNativeArray, ReadableNativeArray> {
 static constexpr const char* kJavaDescriptor = "Lcom/facebook/react/bridge/WritableNativeArray;";

  ...

  static local_ref<jhybriddata> initHybrid(alias_ref<jclass>) {
   return makeCxxInstance();
  }

  ...
}

在创建HybridData(Java)的时候,模板类HybridClass的第一个泛型T,表示的是WritableNativeArray(C++)这个结构体。所以,makeHybridData中的new T(std::forward(args)…)新创建的T就是WritableNativeArray(C++)对象。

继续来看makeHybridData方法,参数cxxPart是刚刚创建的WritableNativeArray对象的指针。里面通过detail::HybridData::create()真正创建了HybridData(Java)和HybridData(C++)对象,并将WritableNativeArray(C++)对象的指针通过setNativePointer方法注入到了HybridData(Java)中。

接下来,看create和setNativePointer两个方法的细节,在Hybrid.cpp中:

local_ref<HybridData> HybridData::create() {
 return newInstance();
}
void HybridData::setNativePointer(std::unique_ptr<basehybridclass> new_value) {
 static auto pointerField = getClass()->getField<jlong>("mNativePointer");
 auto* old_value = reinterpret_cast<BaseHybridClass*>(getFieldValue(pointerField));

 if (new_value) {
  ...
 } else if (old_value == 0) {
  return;
 }
 delete old_value;
 ...
 setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));

}

create里面是通过newInstance方式创建了HybridData(Java) 和HybridData(C++)对象,具体细节不细说了,读者自行去研究facebook的封装。

HybridData(C++)的setNativePointer方法中的参数new_value,为WritableNativeArray(C++)对象的指针, 使用reinterpret_cast关键字将其转换成long型,设置到mNativePointer中。而这里的mNativePointer,就是我们前面谈到的HybridData(Java)类的成员变量了!

有一点需要注意的是,保存WritableNativeArray(C++)对象指针的时候,会先获取原先保存的指针并删除回收(如果存在的话),主要目的是回收WritableNativeArray(C++)对象的内存,调用的时机是HybridData(Java)的finalize,也就是WritableNativeArray(Java)和HybridData(Java)被虚拟机GC回收的时候,这说明了一点,就是WritableNativeArray(C++)对象实例和WritableNativeArray(Java)对象实例的内存释放是完全同步的,都是交由Java GC来触发!

到这里我们稍稍梳理一下。

当WritableNativeArray(Java)创建的时候,通过JNI调用会先创建WritableNativeArray(C++)对象,其后会创建HybridData(Java)和HybridData(C++),同时将WritableNativeArray(C++)的指针保存到HybridData(Java)的mNativePointer成员变量中,最后把HybridData(Java)保存到WritableNativeArray(Java)对象里面。

这样设计有一个好处。当WritableNativeArray(Java)通过JNI的方式传递到C++层时,可以通过保存在其内部的HybridData(Java)对象的mNativePointer的值,还原WritableNativeArray(C++)对象。

这个还原过程是通过内联函数cthis函数实现的,代码在jni/first-party/jni/fbjni/Hybrid.h中:

// Given a *_ref object which refers to a hybrid class, this will reach inside
// of it, find the mHybridData, extract the C++ instance pointer, cast it to
// the appropriate type, and return it.
template <typename T>
inline auto cthis(T jthis) -> decltype(jthis->cthis()) {
 return jthis->cthis();
}
inline T* HybridClass<T, B>::JavaPart::cthis() {
 static auto field =
  HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
 auto hybridData = this->getFieldValue(field);
 ...
 // I'd like to use dynamic_cast here, but -fno-rtti is the default.
 T* value = static_cast<T*>(hybridData->getNativePointer());
 ...
 return value;
};
BaseHybridClass* HybridData::getNativePointer() {
 static auto pointerField = getClass()->getField<jlong>("mNativePointer");
 auto* value = reinterpret_cast<BaseHybridClass*>(getFieldValue(pointerField));
 ...
 return value;
}

先提取出WritableNativeArray(Java)对象的mHybridData,再提取其mNativePointer,最后使用reinterpret_cast还原出WritableNativeArray(C++)对象。而在WritableNativeArray(C++)对象中存储着所有push的数据(定义在其父类NativeArray中),这样数据的提取工作就完成了。

到此,Java传输数据给C++的场景分析完成,下面我们来研究反向过程。

C++传输数据给Java的场景,主要是在callNativeModules里面,我们直接来看makeJavaCall方法,在jni\react\jni\OnLoad.cpp中

static void makeJavaCall(JNIEnv* env, ExecutorToken executorToken, jobject callback, const MethodCall& call) {
 if (call.arguments.isNull()) {
  return;
 }

 ...

 auto newArray = ReadableNativeArray::newObjectCxxArgs(std::move(call.arguments));
 env->CallVoidMethod(
   callback,
   gCallbackMethod,
   static_cast<JExecutorTokenHolder*>(executorToken.getPlatformExecutorToken().get())->getJobj(),
   call.moduleId,
   call.methodId,
   newArray.get());
}

call.arguments是一个封装好的folly::dynamic对象(详见folly开源库),通过newObjectCxxArgs方法转换成ReadableNativeArray(C++)对象,实现在jni/first-party/jni/fbjni/Hybrid.h中:

template <typename... Args>
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
  auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
  return JavaPart::newInstance(hybridData);
}
template <typename... Args>
static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
 return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
}
template<typename JC, typename... Args>
static local_ref<JC> newInstance(Args... args) {
 static auto cls = JC::javaClassStatic();
 static auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();
 return cls->newObject(constructor, args...);
}

创建ReadableNativeArray(C++)对象的过程和前面创建WritableNativeArray(C++)对象的过程一模一样。先创建HybridData(Java)和HybridData(C++),同时将ReadableNativeArray(C++)的指针保存到HybridData(Java)的mNativePointer成员变量中。最后ReadableNativeArray(Java)对象被封装在JavaPart中(再次用到facebook用JNI创建Java对象的封装库),通过get方法获取到真正的实例。

继续来看ReadableNativeArray(Java),位于包com.facebook.react.bridge中:

public class ReadableNativeArray extends NativeArray implements ReadableArray {
  static {
   SoLoader.loadLibrary(ReactBridge.REACT_NATIVE_LIB);
  }

  protected ReadableNativeArray(HybridData hybridData) {
   super(hybridData);
  }

  @Override
  public native int size();
  @Override
  public native boolean isNull(int index);
  @Override
  public native boolean getBoolean(int index);
  @Override
  public native double getDouble(int index);
  @Override
  public native int getInt(int index);
  @Override
  public native String getString(int index);
  @Override
  public native ReadableNativeArray getArray(int index);
  @Override
  public native ReadableNativeMap getMap(int index);
  @Override
  public native ReadableType getType(int index);
}

ReadableNativeArray(Java)同样也是一个管道,所有数据仍然是存在C++层,必须全部通过native本地方法来提取,依赖具有了前面说到的两个优点:减少内存和容易回收。

ReadableNativeArray::ReadableNativeArray(folly::dynamic array)
  : HybridBase(std::move(array)) {}

...

jint ReadableNativeArray::getSize() {
 return array.size();
}

jboolean ReadableNativeArray::isNull(jint index) {
 return array.at(index).isNull() ? JNI_TRUE : JNI_FALSE;
}

jboolean ReadableNativeArray::getBoolean(jint index) {
 return array.at(index).getBool() ? JNI_TRUE : JNI_FALSE;
}

jdouble ReadableNativeArray::getDouble(jint index) {
 const folly::dynamic& val = array.at(index);
 if (val.isInt()) {
  return val.getInt();
 }
 return val.getDouble();
}

jint ReadableNativeArray::getInt(jint index) {
 auto integer = array.at(index).getInt();
 ...
 jint javaint = static_cast<jint>(integer);
 ...
 return javaint;
}

const char* ReadableNativeArray::getString(jint index) {
 const folly::dynamic& dyn = array.at(index);
 if (dyn.isNull()) {
  return nullptr;
 }
 return dyn.getString().c_str();
}

ReadableNativeArray::getArray(jint index) {
 auto& elem = array.at(index);
 if (elem.isNull()) {
  return jni::local_ref<ReadableNativeArray::jhybridobject>(nullptr);
 } else {
  return ReadableNativeArray::newObjectCxxArgs(elem);
 }
}

jobject ReadableNativeArray::getMap(jint index) {
 return createReadableNativeMapWithContents(Environment::current(), array.at(index));
}

jobject ReadableNativeArray::getType(jint index) {
 return type::getType(array.at(index).type());
}

void ReadableNativeArray::registerNatives() {
 jni::registerNatives("com/facebook/react/bridge/ReadableNativeArray", {
  makeNativeMethod("size", ReadableNativeArray::getSize),
  makeNativeMethod("isNull", ReadableNativeArray::isNull),
  makeNativeMethod("getBoolean", ReadableNativeArray::getBoolean),
  makeNativeMethod("getDouble", ReadableNativeArray::getDouble),
  makeNativeMethod("getInt", ReadableNativeArray::getInt),
  makeNativeMethod("getString", ReadableNativeArray::getString),
  makeNativeMethod("getArray", ReadableNativeArray::getArray),
  makeNativeMethod("getMap", "(I)Lcom/facebook/react/bridge/ReadableNativeMap;",
           ReadableNativeArray::getMap),
  makeNativeMethod("getType", "(I)Lcom/facebook/react/bridge/ReadableType;",
           ReadableNativeArray::getType),
 });
}

对数据的提取,最后仍然是对array对象操作,其是定义在父类NativeArray.h中的,不在赘述。

整个数据模型的分析就到此结束了,总结一下有以下几个特点:

1、数据只有一份存储,即在C++中,无论是ReadableNativeArray(Java)还是WritableNativeArray(Java)都只是数据存取的管道。

2、ReadableNativeArray和WritableNativeArray在Java层和C++层又都有各自的实例,通过Java层实例的HybridData的mNativePointer作为纽带链接,其存储的是C++层实例的指针。

3、无论是Java层还是C++层的ReadableNativeArray和WritableNativeArray都是统一由Java GC进行回收管理。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Android Rreact Native 常见错误总结

    Android Rreact Native 常见错误总结 1.invariant violation:expected a component class,got[object object] 创建自定义组件首字母要大写,否则会报错. 2.Module 0 is not a registered callable module. 将gradle升级成最新版本(cd Android 进入android目录执行:sudo ./gradlew clean) 或者通过android studio工具升级

  • React Native第三方平台分享的实例(Android,IOS双平台)

    本文主要介绍了React Native第三方平台分享的实例(Android,IOS双平台),分享给大家,具体如下: 源码已开源到Github,地址请点击:react-native-share[一行代码,双平台分享] 目前支持分享的平台有[QQ][QQ空间][微信][朋友圈][微博][FaceBook]  欢迎大家star,fork..... [ Android平台配置 ] 1. app目录下创建 libs 文件夹,添加依赖文件[直接复制源码中 libs 目录即可] 2. app / src /

  • react-native 封装选择弹出框示例(试用ios&android)

    在开发 App 的时候,经常会使用到对话框(又叫消息框.提示框.告警框). 在web开发中经常会用得到.今天就来介绍了一下react-native 封装弹出框 之前看到react-native-image-picker中自带了一个选择器,可以选择拍照还是图库,但我们的项目中有多处用到这个选择弹出框,所以就自己写了一下,最最重要的是ios和Android通用.先上动态效果图~ 一.封装要点 1.使用动画实现弹框布局及显示隐藏效果 2.通过一个boolean值控制组件的显示隐藏 3.弹框选项数组通过

  • Android React Native原生模块与JS模块通信的方法总结

    Android React Native原生模块与JS模块通信的方法总结 前言: 在做React Native开发的时候避免不了的需要原生模块和JS之间进行数据传递,这篇文章将向大家分享原生模块向JS传递数据的几种方式. 方式一:通过Callbacks的方式 说起Callbacks大家都不陌生,它是最常用的设计模式之一.无论是Java,Object-c,C#,还是JavaScript等都会看到Callbacks的身影. 原生模块支持Callbacks类型的参数,该Callbacks对应JS中的f

  • Android原生嵌入React Native详解

    1.首先集成的项目目录 我使用的是直接按照react-native init Project 的格式来导入的,也就是说,我的Android项目目录是跟node_modules是在一个目录下的. 我们init完项目之后,项目初始化完成了,这时候我们可以用命令react-native run-android直接运行项目,至于怎么调试,之前已经说过. 说一下我们怎么开发和运行分开吧,我们开发一般会选择webstrom,开发后我们会Android和ios的编译分开. 启动npm 下面说一下android

  • Windows下React Native的Android环境部署及布局示例

    搭建基础环境 JDK(必须,不解释) SDK(建议使用Android Studio,集成SDK以及模拟器) genymotion(如果是使用真机或者Android Studio自带的模拟器,可以选择不装) NVM(node版本控制器,需要node4.0以上版本) 以上配置不是必须,可自行选择适合自己的环境,部分安装过程可能会涉及到翻墙,需要配置代理 配置踩坑记录 genymotion 这里选择genymotion模拟器来讲解,也会提一下Android Studio自带的模拟器的一些注意点,使用真

  • React-Native Android 与 IOS App使用一份代码实现方法

    React-Native  Android 与 IOS 共用代码 React-Native 开发的App, 所有组件iOS & Android 共用, 共享一份代码 包括一些自定义的组件, 如NavigationBar, TabBar, SegmentedControl, 使用字体图标, 具有一定的参考意义 主要专注于布局, 共享组件/代码, 以及一些React自带的组件, 如: ScrollView, TouchableOpacity, View, Text, ListView, Image,

  • React-Native实现ListView组件之上拉刷新实例(iOS和Android通用)

    在web应用中,上拉刷新加载更多,下拉刷新列表的操作非常常见,那么在React-Native中是如何实现呢,我们具体来看一下 ReactNative提供了RefreshControl下拉刷新组件,但是没有提供上拉刷新组件,上拉刷新在App中是很常用的. 今天我们来实现一个iOS和Android通用的上拉刷新功能. 下面简要介绍下我实现的思路. 思路: 1.常量定义: const moreText = "加载完毕"; //foot显示的文案 //页码 var pageNum = 1; /

  • 详解React Native监听Android回退按键与程序化退出应用

    详解React Native监听Android回退按键与程序化退出应用 前言 我们知道Android回退按键,会控制页面返回, 并且退出应用并非真正意义退出,仍在后台运行,所以在某些场景下需要监控android回退按键,那么在React Native中应该如何应用呢?我们具体来看看. BackAndroid 此模块用于监听硬件的back键操作. 看下具体代码: BackAndroid.addEventListener('hardwareBackPress', function() { if (!

  • Android React-Native通信数据模型分析

    无论是计算机领域还是日常生活中,我们所言的通信,其核心都是数据信息的交换,而数据模型的优劣对通信效率有着决定性的作用. 在React-Native项目中,Javascript语言与Native两种语言(Java或OC等)间存在着大量的数据交换,也就是所谓的通信.众所周知,移动APP对性能的要求无比苛刻,如果通信数据模型设计地不合理,很可能引起多线程下的数据安全问题,以及应用性能问题,比如内存泄漏,UI绘制缓慢等. 前面几篇博客我们详细分析过React-Native的通信机制,主要有两个方向: J

  • 如何在原有Android项目中快速集成React Native详解

    前言 RN经过一段时间发展,已经有充分数量的人尝试过了,就我身边就有几批,褒贬也不一: ① 做UI快 ② 还是有很多限制,不如原生Native ③ 入门简单,能让前端快速开发App ④ iOS&Android大部分代码通用 ⑤ code-push能做热更新,但是用不好依旧坑 ...... 在得到一些信息后,可以看出,要用RN高效率的做出比较不错的App是有可能的,单看投入度与最初设计是否合理,而且现在关于React Native的各种文档是相当丰富的,所以这个阶段想切入RN可能是一个不错的选择.

  • 深入理解React Native原生模块与JS模块通信的几种方式

    每种语言都有自己的设计理念.语法.运行环境,这也导致了不同语言间相互交流通信时必须要有中介来翻译,如JAVA与C/C++通过JNI来交流.OC与C/C++需要在.mm文件混编.而JAVA/OC与Lua通信时需要通过C/C++语言来做中介.那么在React-Native中JSX是如何与底层模块进行通信的呢?这里主要以iOS系统来做说明. 原理 通信本质上是信息的交流,具体到计算机语言则是数据的流动.应用中数据在React-Native与原生模块间的流动与共享,完成了与用户的交互,达成了应用的目标.

  • React Native与Android 原生通信的方法

    我们用React Native 做混合开发的时候免不了要原生和React Native 进行通信交互,这篇文章就是分享原生模块与JS传递数据的几种方式. 总的步骤可以分为如下几点: 在原生端定义Module类,继承ReactContextBaseJavaModule,在Module类里,定义交互的方法. 定义Package类,继承ReactPackage,将Module实例添加到集合. 在android继承的ReactApplication,回调实现getPackages方法,将Package实

  • React Native JSI实现RN与原生通信的示例代码

    目录 什么是JSI JSI有什么不同 在iOS中使用JSI iOS端配置 RN端配置 js调用带参数的原生方法 原生调用JS 原生调用带参数的JS方法 在原生端调用js的函数参数 总结 问题 参考资料 什么是JSI React Native JSI (JavaScript Interface) 可以使 JavaScript 和 原生模块 更快.更简单的通信.它也是React Native 新的架构体系中Fabric UI层 和 Turbo 模块的核心部分. JSI有什么不同 JSI 移除了原生代

  • react native与webview通信的示例代码

    WebView是ReactNative中的组件 , 它可以创建一个原生的WebView,可以用于访问一个网页. 有时候我们需要在RN与WebView之间进行通信,或者进行数据传递,或者发送消息通知.这时候就要用以下知识了. 一:WebView向RN端发送数据: 首先,我们构建一个webview: <WebView ref={'webview'} source={require('./index.html')} style={{width: 375, height: 220}} onMessage

随机推荐