1. 程式人生 > >React Native 4 for Android原始碼分析 一《JNI智慧指標之介紹篇》

React Native 4 for Android原始碼分析 一《JNI智慧指標之介紹篇》

30cbbf8e6fb595562adf872258914222.jpg

導讀

React Native 釋出以來將近一年多了,也被抄的火爆到不行,包括RN的中文網和各種資料也很多,加之SE5,Se6語法升級,學習成本並不在RN環境搭建和入門,關鍵還是對JS的掌握入門,不管你是用Native開發,h5開發,還是React Native的開發,都應該明白上層語言和底層互動,都離不開c, 今天就介紹下native中Jni過程中的指標運用。文章適合入門了RN的朋友看,入門資料:Android 開發者轉 React Native 必看教程彙總

JNI指標

通常的app中, JNI提供的native函式主要充當Java類的擴充套件,邏輯層在Java端,JNI端較少使用OOP的設計思想。 而對於native端功能較重的模組,例如開源的閱讀器FBReader,native端與Java端有較多互動,即native會主動建立Java物件並呼叫它們的方法以實現功能,這時就需要考慮將native至Java的操作與訪問框架化,形成更高層次的封裝,以避免直接使用原始的JNI反射API集去操作Java物件。
對於ReactNative For Android而言,這套訪問框架尤其重要,其核心就是JNI智慧指標這個基本資料型別。它的實現基於C11標準,將先用幾篇對這套native至Java的操作框架進行介紹,為後續分析打下良好基礎。

Native引用

首先回顧一下Java Object(jobject)在native端的三種引用型別:

全域性引用

類似於C語言中的全域性變數。使用NewGlobalRef建立,支援跨執行緒訪問 ,在呼叫釋放DeleteGlobalRef銷燬前,GC無法回收該引用對應的java object。

區域性引用

概念上與C語言中的區域性變數有相似點,但不等同。使用NewLocalRef建立, 只能在本執行緒內安全訪問,當建立該引用的native呼叫鏈返回至JVM時,未銷燬的區域性引用會被JVM自動GC回收。但由於區域性引用表容量有限,在返回至JVM前,可以呼叫DeleteLocalRef先行銷燬,避免區域性引用表超限引起崩潰。

弱全域性引用

與全域性引用一樣具有全域性作用域,但不會影響GC回收, GC可以隨時回收該引用對應的java object。使用NewWeakGlobalRef建立,當需要使用時,需要將其升級為全域性引用或者區域性引用,若已被回收,會返回null,使用DeleteWeakGlobalRef銷燬。該引用型別使用場景較少。

由上可見,JNI智慧指標的第一個需求,就是要自動管理jobject的生命週期,當進入與離開對應作用域時,需要自動呼叫對應生命週期的建立與銷燬函式。這在C++中,通常會結合構造與解構函式來進行配對呼叫。若功能僅限於此,就與普通的智慧指標和mutext鎖管理機制類似了,更重要的需求是在C++層提供與被管理的Java物件映象結構的C++物件,形成高層次封裝。這樣,對jobject的訪問與操作就會被封裝在對應的映象C++物件中,相關JNI反射呼叫的細節被隱藏,對於其他native模組而言,與Java層的互動被轉化成了與這些映象C++物件的互動,整個實現風格OOP化了。這些映象C++物件被稱為wrapper物件,其定義程式碼位於ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h

檔案中。

先看一個使用範例:

 struct MyClass : public JavaClass<MyClass> {
   constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";

   void foo() {
 static auto method = javaClassStatic()->getMethod<void()>("foo");
 method(self());
   }

   static local_ref<javaobject> create(int i) {
 return newInstance(i);
   }
 };

auto obj = MyClass::create(10);
obj->foo();

Native的需求是在native端建立com.example.package.MyClass這個自定義的Java類的物件,並訪問它的foo方法。

實現步驟

例子中實現的步驟是:

定義java的MyClass的wrapper C++類MyClass,所有wrapper均需要繼承於JavaClass的一個模板例項,並將自身型別做為JavaClass的第一個模板型別引數,以供JavaClass獲取具體wrapper的型別。

給static成員變數kJavaDescriptor賦值為對應Java類的全類名。
在wrapper類實現映象方法foo(), 其會獲取jclass的包裝類JClass物件,並獲取jmethod的包裝類JMethod進行呼叫。

create工廠方法中使用newInstance構建映象物件的例項,並將其存至區域性智慧指標local_ref。這樣就可以通過智慧指標訪問wrapper class提供的foo方法,實現了native至Java的映象對映。

除了實現對一個java類的的對映,還需要支援對java繼承關係的對映。若java的MyClass有一子類MyChildClass,native層為其建立的wrapper class可如下:

struct MyChildClass : public JavaClass<MyChildClass, MyClass> {
constexpr static auto kJavaDescriptor = "Lcom/example/package/MyChildClass;";
};

這裡需要用到JavaClass的第二個模板引數,設為MyClass,它是JavaClass

疑問

這就帶來幾個問題:

javaObject與jobject的關係是什麼?

為什麼智慧指標的模板引數能夠接受多種型別?

模板引數起到的作用是什麼?

結尾

這些問題將在下一篇智慧指標的具體實現篇中解答。
總結一下,在ReactNative for Android中,為了簡化native層對Java層的呼叫,提供了映象結構的wrapper class,結合智慧指標,將jobject的生命週期管理、java method的反射呼叫等“樣板”程式碼封裝起來,是比較優雅的JNI呼叫框架。

這裡寫圖片描述