React Native JSI’a Derinlemesine Bakış

Marc Rousavy yapmış olduğu vision camera ve mkkv gibi kütüphanelerde edindiği deneyimleri React Native EU konferansında paylaştı.

Giriş

React Native’deki bridge yapısı JSON mesajlar aracılığıyla haberleşmeyi sağlar.
React Native uygulamasında butona tıklandığında kamera açma sürecinde geçen işlemler görseldeki gibi belirtilmiştir.

JSI’ın ortaya çıkışı

JSC (JavaScript Core), Hermes ve V8 engine’lerinde JSI desteği bulunmaktadır.

JavaScript ve JSI’da değişken tanımlamaları

number tipinde tanımlama

// Javascript
const number = 42
// JSI (C++)
jsi::Value number = jsi::Value(42);

String tipinde tanımlama

// JavaScript
const name = "Marc"
// JSI (C++)
jsi::Value name = jsi::String::createFromUtf8(runtime, "Marc")

Fonksiyon tanımlama

// JS
const add = (first, second) => {
return first + second
}
// JSI (C++)
auto add = jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "add"), // add fonksiyonu
2, // first, second değişkenleri 2 adet
[](
jsi::Runtime& runtime,
const jsi::Value& thisValue,
const jsi::Value* arguments, // fonksiyon argümanları
size_t count
) -> jsi::Value {
double result = arguments[0].asNumber() + arguments[1].asNumber();
return jsi::Value(result);
}
);
// JavaScript
const result = add(5, 8)
// JSI (C++)
auto result = add.call(runtime, 5, 8);
// Javascript
global.add = add;
// JSI (C++)
runtime.global().setProperty(runtime, "add", std::move(add));
const resul = await global.add(5, 2)
// JSI (C++)
auto getIpAddress = jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "getIpAddress"),
0, // Hiç parametre almıyor
[](
jsi::Runtime& runtime,
// thisValue, arguments ve count değişkenleri gerekli değil
const jsi::Value&,
const jsi::Value*,
size_t
) -> jsi::Value {
// ObjC veya Java tarafındaki metot çağrılır
auto ip = SomeIosApi.getIpAddress();
return jsi::String::createFromUtf8(runtime, ip.toString());
}
);
runtime.global().setProperty(runtime, "getIpAddress", std::move(getIpAddress));
// JavaScript
const ip = global.getIpAddress();

Bridge ve JSI farklılıkları

Bir JSI örneği olarak mmkv kütüphanesi

iPhone 8 cihazında storage’dan 1000 defa veri okuma işlemi (ms). MMKV 10ms civarında iken AsyncStorage 230 ms’yi buluyor. Yaklaşık 23 kat haz farkı görülmektedir.
public class MainApplication extends Application implements ReactApplication {  private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}

@Override
protected List<ReactPackage> getPackages() {
return new PackageList(this).getPackages();
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new MmkvModulePackage();
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}
public class MmkvModulePackage implements JSIModulePackage {
@Override
public List<JSIModuleSpec> getJSIModules(
ReactApplicationContext ctx,
JavaScriptContextHolder jsContext) {
MmkvModule.install(jsContext, ctx.getFilesDir().getAbsolutePath() + "/mmkv"); return Collections.emptyList();
}
}
public static void install(
JavaScriptContextHolder jsContext,
String storageDirectory) {

nativeInstall(jsContext.get(), storageDirectory);
}
private static native void nativeInstall(long jsiPtr, String path);
extern "C"
JNIEXPORT void JNICALL
Java_com_reactnativemmkv_MmkvModule_nativeInstall(
JNIEnv *env,
jobject clazz,
jlong jsiPtr,
jstring path) {

MMKV::initializeMMKV(jstringToStdString(env, path));
auto runtime = reinterpret_cast<jsi::Runtime*>(jsiPtr);
if (runtime) {
install(*runtime);
}
// if runtime was nullptr, MMKV will not be installed. This should only happen while Remote Debugging (Chrome), but will be weird either way.
}
nativeInstall(jsContext.get(), storageDirectory);

Vision Camera kütüphanesinde JSI kullanımı

const frameProcessor = useFrameProcessor((frame) => {
'worklet';

console.log(`A new ${frame.width} x ${frame.height} frame arrived!`);

const values = examplePlugin(frame);
console.log(`Return values ${JSON.stringify(values)}`);
},[]);
export interface Frame {
isValid: boolean;
width: number;
height: number;
bytesPerRow: number;
planesCount: number;
toString(): string;
close(): void;
}

Custom Host Object oluşturma

auto promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");auto promise = promiseCtor.callAsConstructor(runtime, resolve, reject);
auto nativeFunc = jsi::Function::createFromHostFunction(runtime,
jsi::PropNameID::forAsci(runtime, "someFunc"),
1, // a function
[](jsi::Runtime& runtime,
const jsi::Value& thisValue,
constjsi::Value* arguments,
size_t count) -> jsi::Value {
auto func = arguments[0].asObject().asFunction();
return func.call(runtime, jsi::Value(42));
});

Sonuç olarak

Kaynaklar

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store