react-native-fast-tflite icon indicating copy to clipboard operation
react-native-fast-tflite copied to clipboard

feat: Android litert version bump and 16KB page support

Open andrew-schenk opened this issue 5 months ago • 13 comments

This PR fixes an issue where the Google Play Store would complain that this library was bundling non 16KB page aligned .so files.

Specifically

  • base/lib/x86_64/libtensorflowlite_gpu_jni.so
  • base/lib/x86_64/libtensorflowlite_jni.so

v1.4.0 of the litert fixes that without requiring a major update to v2. https://github.com/google-ai-edge/LiteRT/issues/43

Simply updating the dependency version wasn't enough because the directory structure of litert changes from 1.0.1 to 1.4.0

You can see that by going to maven and downloading and unzipping the 1.0.1 AAR and 1.4.0 AAR.

1.0.1 directory structure

headers/ --tensorflow/ ----lite/

1.4.0 directory structure

headers/ --external/ ----org_tensorflow/ ----tensorflow/ --tflite/

This change in directory structure requires updated imports in this projects cpp files. from #include <tensorflow/lite to #include <tflite

There was also an issue with the internal litert cpp files not linking correctly because of the extra external/org_tensorflow/tensorflow path and the way this project copies over header files. I modified the gradle script to copy tensorflow up two levels

so this: turns into headers/ --external/ ----org_tensorflow/ ----tensorflow/ --tflite/

this headers/ --tensorflow/ --tflite/

andrew-schenk avatar Jul 03 '25 15:07 andrew-schenk

Is this written with AI? 😄

Did you test this change if everything runs on both iOS + Android?

mrousavy avatar Jul 07 '25 09:07 mrousavy

Parts of the gradle step were AI generated and then refined by myself. I tested on Android with the app I am working on and the ML model I am using was still working correctly.

andrew-schenk avatar Jul 07 '25 13:07 andrew-schenk

Not working for me.

ERROR TypeError: Cannot read property 'install' of undefined, js engine: hermes [Component Stack] ERROR Warning: TypeError: Cannot read property 'useTensorflowModel' of undefined

jslok avatar Aug 24 '25 09:08 jslok

Not working for me.

ERROR TypeError: Cannot read property 'install' of undefined, js engine: hermes [Component Stack] ERROR Warning: TypeError: Cannot read property 'useTensorflowModel' of undefined

@jslok were you building off this branch? Did you clean out node_modules, android/build, and android/.gradle folders. yarn install, then did you run a clean project, and a gradle sync?

andrew-schenk avatar Aug 25 '25 13:08 andrew-schenk

Yep I tried this PR. Just tried again with full reinstall and gradle clean and same error. Also tried with a new clean project and got the same error. Using react-native 0.75.5 no expo no new arch.

jslok avatar Aug 25 '25 21:08 jslok

Just FYI, Google Play Console complains about some additional libs at my end as well. Here is the full list:

        base/lib/arm64-v8a/libVisionCameraTflite.so
        base/lib/x86_64/libVisionCameraTflite.so
        base/lib/x86_64/libtensorflowlite_gpu_jni.so
        base/lib/x86_64/libtensorflowlite_jni.so

I'm not using vision camera lib in my project.

haidernawaz99 avatar Aug 31 '25 09:08 haidernawaz99

Not working for me.

ERROR TypeError: Cannot read property 'install' of undefined, js engine: hermes [Component Stack] ERROR Warning: TypeError: Cannot read property 'useTensorflowModel' of undefined

Tested on rn 0.81.4, rn cli, with new arch and getting the same errors

jslok avatar Sep 19 '25 06:09 jslok

Any update on this? Or when can this PR be merged?

coderof19clc7 avatar Oct 13 '25 04:10 coderof19clc7

@coderof19clc7 it's open source. I laid out all the thought process in this PR. It worked in my case and the project I was working on. You can examine it and adapt it to your use case. jslok was saying that there were issues, so maybe start by digging in and seeing if you can recreate those issues.

andrew-schenk avatar Oct 13 '25 14:10 andrew-schenk

@andrew-schenk , thanks for your reply, I was waiting for the merge but I will try to adapt your changes first then.

coderof19clc7 avatar Oct 14 '25 03:10 coderof19clc7

@andrew-schenk, Considering that the NDK version is 27 or lower, the following code seems necessary.

// CMakeLists.txt
if(CMAKE_ANDROID_NDK_VERSION VERSION_LESS "27")
    target_link_options(${PACKAGE_NAME} PRIVATE "-Wl,-z,max-page-size=16384")
endif()

fe-dudu avatar Oct 19 '25 12:10 fe-dudu

This patch worked for me, Play Console says it's now fine.

[email protected]

react-native-fast-tflite+1.6.1.patch

diff --git a/node_modules/react-native-fast-tflite/android/CMakeLists.txt b/node_modules/react-native-fast-tflite/android/CMakeLists.txt
index 100468c..e43cc1f 100644
--- a/node_modules/react-native-fast-tflite/android/CMakeLists.txt
+++ b/node_modules/react-native-fast-tflite/android/CMakeLists.txt
@@ -36,6 +36,12 @@ add_library(
   src/main/cpp/Tflite.cpp
 )
 
+target_link_options(${PACKAGE_NAME}
+    PRIVATE
+    "-Wl,-z,max-page-size=16384"
+    "-Wl,-z,common-page-size=16384"
+)
+
 # Specifies a path to native header files.
 target_include_directories(
   ${PACKAGE_NAME}
@@ -43,6 +49,7 @@ target_include_directories(
   "../cpp"
   "src/main/cpp"
   "src/main/cpp/lib/litert/headers"
+  "src/main/cpp/lib/litert/headers/tflite"
   "${NODE_MODULES_DIR}/react-native/ReactCommon"
   "${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker"
   "${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni/react/turbomodule" # <-- CallInvokerHolder JNI wrapper
diff --git a/node_modules/react-native-fast-tflite/android/build.gradle b/node_modules/react-native-fast-tflite/android/build.gradle
index 0083618..3aa1edb 100644
--- a/node_modules/react-native-fast-tflite/android/build.gradle
+++ b/node_modules/react-native-fast-tflite/android/build.gradle
@@ -1,4 +1,5 @@
 import java.nio.file.Paths
+import org.gradle.api.file.RelativePath
 
 buildscript {
   repositories {
@@ -126,25 +127,44 @@ dependencies {
   implementation "com.facebook.react:react-native:+"
 
   // Tensorflow Lite .aar (includes C API via prefabs)
-  implementation "com.google.ai.edge.litert:litert:1.0.1"
-  extractSO("com.google.ai.edge.litert:litert:1.0.1")
-  extractHeaders("com.google.ai.edge.litert:litert:1.0.1")
+  implementation "com.google.ai.edge.litert:litert:1.4.0"
+  extractSO("com.google.ai.edge.litert:litert:1.4.0")
+  extractHeaders("com.google.ai.edge.litert:litert:1.4.0")
 
   // Tensorflow Lite GPU delegate
-  implementation "com.google.ai.edge.litert:litert-gpu:1.0.1"
-  extractSO("com.google.ai.edge.litert:litert-gpu:1.0.1")
-  extractHeaders("com.google.ai.edge.litert:litert-gpu:1.0.1")
+  implementation "com.google.ai.edge.litert:litert-gpu:1.4.0"
+  extractSO("com.google.ai.edge.litert:litert-gpu:1.4.0")
+  extractHeaders("com.google.ai.edge.litert:litert-gpu:1.4.0")
+}
+
+task cleanEmptyDirectories(type: Delete) {
+  delete 'src/main/cpp/lib/headers'
+  delete 'src/main/cpp/lib/res'
 }
 
 task extractAARHeaders {
+  finalizedBy cleanEmptyDirectories
   doLast {
-    configurations.extractHeaders.files.each {
-      def file = it.absoluteFile
-      def packageName = file.name.tokenize('-')[0]
+    configurations.extractHeaders.files.each { aarFile ->
+      def packageName = aarFile.name.tokenize('-')[0]
       copy {
-        from zipTree(file)
-        into "src/main/cpp/lib/$packageName/"
+        from zipTree(aarFile)
+        into "src/main/cpp/lib/"
         include "**/*.h"
+        eachFile { fileCopyDetails ->
+          if (fileCopyDetails.name.endsWith(".h")) {
+            def originalRelativePath = fileCopyDetails.relativePath.toString()
+            if (originalRelativePath.startsWith("headers/external/org_tensorflow/tensorflow/")) {
+              def newRelativePath = packageName + "/headers/" + originalRelativePath.substring("headers/external/org_tensorflow/".length())
+              fileCopyDetails.relativePath = new RelativePath(true, newRelativePath.split('/'))
+            } else {
+              def newRelativePath = packageName + "/" + originalRelativePath
+              fileCopyDetails.relativePath = new RelativePath(true, newRelativePath.split('/'))
+            }
+          } else {
+            exclude fileCopyDetails.name
+          }
+        }
       }
     }
   }
diff --git a/node_modules/react-native-fast-tflite/android/gradle.properties b/node_modules/react-native-fast-tflite/android/gradle.properties
index 42a799a..46a02f8 100644
--- a/node_modules/react-native-fast-tflite/android/gradle.properties
+++ b/node_modules/react-native-fast-tflite/android/gradle.properties
@@ -1,5 +1,5 @@
-Tflite_kotlinVersion=1.7.0
-Tflite_minSdkVersion=21
-Tflite_targetSdkVersion=31
-Tflite_compileSdkVersion=31
-Tflite_ndkversion=21.4.7075529
+Tflite_kotlinVersion=2.1.10
+Tflite_minSdkVersion=26
+Tflite_targetSdkVersion=36
+Tflite_compileSdkVersion=36
+Tflite_ndkversion=27.1.12297006
diff --git a/node_modules/react-native-fast-tflite/cpp/TensorHelpers.cpp b/node_modules/react-native-fast-tflite/cpp/TensorHelpers.cpp
index 10446ca..6456e6b 100644
--- a/node_modules/react-native-fast-tflite/cpp/TensorHelpers.cpp
+++ b/node_modules/react-native-fast-tflite/cpp/TensorHelpers.cpp
@@ -9,7 +9,7 @@
 #include "TensorHelpers.h"
 
 #ifdef ANDROID
-#include <tensorflow/lite/c/c_api.h>
+#include <tflite/c/c_api.h>
 #else
 #include <TensorFlowLiteC/TensorFlowLiteC.h>
 #endif
diff --git a/node_modules/react-native-fast-tflite/cpp/TensorHelpers.h b/node_modules/react-native-fast-tflite/cpp/TensorHelpers.h
index fac1c11..22ba35b 100644
--- a/node_modules/react-native-fast-tflite/cpp/TensorHelpers.h
+++ b/node_modules/react-native-fast-tflite/cpp/TensorHelpers.h
@@ -12,7 +12,7 @@
 #include <jsi/jsi.h>
 
 #ifdef ANDROID
-#include <tensorflow/lite/c/c_api.h>
+#include <tflite/c/c_api.h>
 #else
 #include <TensorFlowLiteC/TensorFlowLiteC.h>
 #endif
diff --git a/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.cpp b/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.cpp
index 36e1887..4993607 100644
--- a/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.cpp
+++ b/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.cpp
@@ -18,9 +18,9 @@
 #include <thread>
 
 #ifdef ANDROID
-#include <tensorflow/lite/c/c_api.h>
-#include <tensorflow/lite/delegates/gpu/delegate.h>
-#include <tensorflow/lite/delegates/nnapi/nnapi_delegate_c_api.h>
+#include <tflite/c/c_api.h>
+#include <tflite/delegates/gpu/delegate.h>
+#include <tflite/delegates/nnapi/nnapi_delegate_c_api.h>
 #else
 #include <TensorFlowLiteC/TensorFlowLiteC.h>
 
diff --git a/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.h b/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.h
index 0da4822..9266d17 100644
--- a/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.h
+++ b/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.h
@@ -16,7 +16,7 @@
 
 #ifdef ANDROID
 #include <ReactCommon/CallInvoker.h>
-#include <tensorflow/lite/c/c_api.h>
+#include <tflite/c/c_api.h>
 #else
 #include <React-callinvoker/ReactCommon/CallInvoker.h>
 #include <TensorFlowLiteC/TensorFlowLiteC.h>

efstathiosntonas avatar Oct 23 '25 10:10 efstathiosntonas

The code from @efstathiosntonas works for me. Thanks!

jslok avatar Nov 15 '25 04:11 jslok

This patch worked for me, Play Console says it's now fine.

[email protected]

react-native-fast-tflite+1.6.1.patch

diff --git a/node_modules/react-native-fast-tflite/android/CMakeLists.txt b/node_modules/react-native-fast-tflite/android/CMakeLists.txt
index 100468c..e43cc1f 100644
--- a/node_modules/react-native-fast-tflite/android/CMakeLists.txt
+++ b/node_modules/react-native-fast-tflite/android/CMakeLists.txt
@@ -36,6 +36,12 @@ add_library(
   src/main/cpp/Tflite.cpp
 )
 
+target_link_options(${PACKAGE_NAME}
+    PRIVATE
+    "-Wl,-z,max-page-size=16384"
+    "-Wl,-z,common-page-size=16384"
+)
+
 # Specifies a path to native header files.
 target_include_directories(
   ${PACKAGE_NAME}
@@ -43,6 +49,7 @@ target_include_directories(
   "../cpp"
   "src/main/cpp"
   "src/main/cpp/lib/litert/headers"
+  "src/main/cpp/lib/litert/headers/tflite"
   "${NODE_MODULES_DIR}/react-native/ReactCommon"
   "${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker"
   "${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni/react/turbomodule" # <-- CallInvokerHolder JNI wrapper
diff --git a/node_modules/react-native-fast-tflite/android/build.gradle b/node_modules/react-native-fast-tflite/android/build.gradle
index 0083618..3aa1edb 100644
--- a/node_modules/react-native-fast-tflite/android/build.gradle
+++ b/node_modules/react-native-fast-tflite/android/build.gradle
@@ -1,4 +1,5 @@
 import java.nio.file.Paths
+import org.gradle.api.file.RelativePath
 
 buildscript {
   repositories {
@@ -126,25 +127,44 @@ dependencies {
   implementation "com.facebook.react:react-native:+"
 
   // Tensorflow Lite .aar (includes C API via prefabs)
-  implementation "com.google.ai.edge.litert:litert:1.0.1"
-  extractSO("com.google.ai.edge.litert:litert:1.0.1")
-  extractHeaders("com.google.ai.edge.litert:litert:1.0.1")
+  implementation "com.google.ai.edge.litert:litert:1.4.0"
+  extractSO("com.google.ai.edge.litert:litert:1.4.0")
+  extractHeaders("com.google.ai.edge.litert:litert:1.4.0")
 
   // Tensorflow Lite GPU delegate
-  implementation "com.google.ai.edge.litert:litert-gpu:1.0.1"
-  extractSO("com.google.ai.edge.litert:litert-gpu:1.0.1")
-  extractHeaders("com.google.ai.edge.litert:litert-gpu:1.0.1")
+  implementation "com.google.ai.edge.litert:litert-gpu:1.4.0"
+  extractSO("com.google.ai.edge.litert:litert-gpu:1.4.0")
+  extractHeaders("com.google.ai.edge.litert:litert-gpu:1.4.0")
+}
+
+task cleanEmptyDirectories(type: Delete) {
+  delete 'src/main/cpp/lib/headers'
+  delete 'src/main/cpp/lib/res'
 }
 
 task extractAARHeaders {
+  finalizedBy cleanEmptyDirectories
   doLast {
-    configurations.extractHeaders.files.each {
-      def file = it.absoluteFile
-      def packageName = file.name.tokenize('-')[0]
+    configurations.extractHeaders.files.each { aarFile ->
+      def packageName = aarFile.name.tokenize('-')[0]
       copy {
-        from zipTree(file)
-        into "src/main/cpp/lib/$packageName/"
+        from zipTree(aarFile)
+        into "src/main/cpp/lib/"
         include "**/*.h"
+        eachFile { fileCopyDetails ->
+          if (fileCopyDetails.name.endsWith(".h")) {
+            def originalRelativePath = fileCopyDetails.relativePath.toString()
+            if (originalRelativePath.startsWith("headers/external/org_tensorflow/tensorflow/")) {
+              def newRelativePath = packageName + "/headers/" + originalRelativePath.substring("headers/external/org_tensorflow/".length())
+              fileCopyDetails.relativePath = new RelativePath(true, newRelativePath.split('/'))
+            } else {
+              def newRelativePath = packageName + "/" + originalRelativePath
+              fileCopyDetails.relativePath = new RelativePath(true, newRelativePath.split('/'))
+            }
+          } else {
+            exclude fileCopyDetails.name
+          }
+        }
       }
     }
   }
diff --git a/node_modules/react-native-fast-tflite/android/gradle.properties b/node_modules/react-native-fast-tflite/android/gradle.properties
index 42a799a..46a02f8 100644
--- a/node_modules/react-native-fast-tflite/android/gradle.properties
+++ b/node_modules/react-native-fast-tflite/android/gradle.properties
@@ -1,5 +1,5 @@
-Tflite_kotlinVersion=1.7.0
-Tflite_minSdkVersion=21
-Tflite_targetSdkVersion=31
-Tflite_compileSdkVersion=31
-Tflite_ndkversion=21.4.7075529
+Tflite_kotlinVersion=2.1.10
+Tflite_minSdkVersion=26
+Tflite_targetSdkVersion=36
+Tflite_compileSdkVersion=36
+Tflite_ndkversion=27.1.12297006
diff --git a/node_modules/react-native-fast-tflite/cpp/TensorHelpers.cpp b/node_modules/react-native-fast-tflite/cpp/TensorHelpers.cpp
index 10446ca..6456e6b 100644
--- a/node_modules/react-native-fast-tflite/cpp/TensorHelpers.cpp
+++ b/node_modules/react-native-fast-tflite/cpp/TensorHelpers.cpp
@@ -9,7 +9,7 @@
 #include "TensorHelpers.h"
 
 #ifdef ANDROID
-#include <tensorflow/lite/c/c_api.h>
+#include <tflite/c/c_api.h>
 #else
 #include <TensorFlowLiteC/TensorFlowLiteC.h>
 #endif
diff --git a/node_modules/react-native-fast-tflite/cpp/TensorHelpers.h b/node_modules/react-native-fast-tflite/cpp/TensorHelpers.h
index fac1c11..22ba35b 100644
--- a/node_modules/react-native-fast-tflite/cpp/TensorHelpers.h
+++ b/node_modules/react-native-fast-tflite/cpp/TensorHelpers.h
@@ -12,7 +12,7 @@
 #include <jsi/jsi.h>
 
 #ifdef ANDROID
-#include <tensorflow/lite/c/c_api.h>
+#include <tflite/c/c_api.h>
 #else
 #include <TensorFlowLiteC/TensorFlowLiteC.h>
 #endif
diff --git a/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.cpp b/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.cpp
index 36e1887..4993607 100644
--- a/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.cpp
+++ b/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.cpp
@@ -18,9 +18,9 @@
 #include <thread>
 
 #ifdef ANDROID
-#include <tensorflow/lite/c/c_api.h>
-#include <tensorflow/lite/delegates/gpu/delegate.h>
-#include <tensorflow/lite/delegates/nnapi/nnapi_delegate_c_api.h>
+#include <tflite/c/c_api.h>
+#include <tflite/delegates/gpu/delegate.h>
+#include <tflite/delegates/nnapi/nnapi_delegate_c_api.h>
 #else
 #include <TensorFlowLiteC/TensorFlowLiteC.h>
 
diff --git a/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.h b/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.h
index 0da4822..9266d17 100644
--- a/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.h
+++ b/node_modules/react-native-fast-tflite/cpp/TensorflowPlugin.h
@@ -16,7 +16,7 @@
 
 #ifdef ANDROID
 #include <ReactCommon/CallInvoker.h>
-#include <tensorflow/lite/c/c_api.h>
+#include <tflite/c/c_api.h>
 #else
 #include <React-callinvoker/ReactCommon/CallInvoker.h>
 #include <TensorFlowLiteC/TensorFlowLiteC.h>

worked for me, thanks for the good stuff

AlshehriAli0 avatar Nov 16 '25 19:11 AlshehriAli0