Recently, we found that many users often encounter problems related to dynamic library loading failure when accessing hongsoft ArcFace face recognition SDK. This paper introduces in detail the whole process from compiling dynamic library (so) to program calling so, simulating some problems often encountered when loading hongsoft face recognition SO file, to help you understand the causes of these problems and solutions.

1, ArcFace library loading common errors

1.1 The Dynamic library Cannot be Found

java.lang.UnsatisfiedLinkError: couldn't find "libarcsoft_face_engine.so"
Copy the code

Reason: when installing applications, APK specified ABI directory is not found in the specified dynamic library, looking for APK see developer. The rules of the dynamic library android. Google. Cn/the NDK/guides /…

There are many indirect causes for this problem, such as:

  • There is no specified dynamic library in the Android project
  • Error placing dynamic inventory in Android project
  • The highest ABI supported by the device is Armeabi-V7A, while APK only has the arm64-V8A dynamic library

Solution: Ensure that the target device included in the installation program supports the ABI’s dynamic library. You can unpack the APK to check if the dynamic library exists.

1.2 The loaded dynamic library ABI is incorrect

java.lang.UnsatisfiedLinkError: "libarcsoft_face_engine.so" is 32-bit instead of 64-bit
Copy the code

Cause: The dynamic library files stored in the 64-bit library directory are 32 bits. For example, placing the dynamic inventory of Armeabi-V7A in the ARM64-V8A directory and installing it on an ARM64-V8A-enabled device would result in such an error.

Solution: Make sure the dynamic library ABI is correct, usually just copy the ABI folder when copying files.

1.3 The length of the dynamic library file is 0

java.lang.UnsatisfiedLinkError: dlopen failed: file offset for the library "... /libarcsoft_face_engine.so" >= file size: 0 >= 0
Copy the code

Cause: The dynamic inventory is in, but the file is empty.

Solution: Reintroduce dynamic libraries into the project.

1.4 The XXXX function cannot be found during function execution

java.lang.UnsatisfiedLinkError: No implementation found for int b.a.a.b.b(android.content.Context, java.lang.String, java.lang.String) (tried Java_b_a_a_b_b and Java_b_a_a_b_b__Landroid_content_Context_2Ljava_lang_String_2Ljava_lang_String_2)
        at b.a.a.b.b(Native Method)
        at b.a.a.b.a(:182)
Copy the code

Reason: After Java functions are determined, native functions cannot be found according to fixed rules. This is usually the result of Java code confusion.

Solution: Modify the obfuscation profile to ensure that the relevant Java code is not obfuscated.

1.5 Crash Occurs when loading the dynamic library

JNI DETECTED ERROR IN APPLICATION: JNI RegisterNatives called with pending exception java.lang.ClassNotFoundException: Didn't find class "com.arcsoft.face.FaceEngine"
Copy the code

Cause: The Java class, function, or variable with the specified Java signature cannot be found in the dynamic library.

Solution: Modify the obfuscation profile to ensure that the relevant Java code is not obfuscated.

The above is the introduction of common crash and basic causes and solutions. Next, we will compile and use the dynamic library by ourselves to understand how these problems occur.

Build and use the dynamic library yourself

2.1. Build dynamic libraries

2.1.1 CMakeLists.txt

The content in cmakelists.txt is simple: compile hello. CPP into a dynamic library called libhello-sdk.so

add_library(
          hello-sdk
          SHARED
          hello.cpp
)
Copy the code

2.1.2 hello.cpp

In this file, two functions are defined using JNI static registration and dynamic registration, and the functions that need dynamic registration are registered in JNI_Onload:

  • Java_com_arcsoft_functionregisterdemo_MainActivity_hello Is a function that needs to be statically registered. When a Native function defined in Java is called for the first time, the JVM will find and register the native function according to fixed rules. The general rule is: Java_ package name _ class name _ function name. JniShortName() and JniLongName() : androidxref.com/9.0.0_r3/xr…

  • DynamicRegisterFunction A function that needs to be registered dynamically, usually in JNI_OnLoad.

  • JNI_OnLoad is the function that is executed when the dynamic library is loaded, where dynamicRegisterFunction is registered. For details of how the JNI_OnLoad function is called, please refer to androidxref.com/9.0.0_r3/xr… Lines 1009 to 1024 of

  #include <jni.h>
  #include <string>
  
  // Statically registered function, corresponding to the Hello function in the MainActivity class
  extern "C" JNIEXPORT jstring JNICALL
  Java_com_arcsoft_functionregisterdemo_MainActivity_hello(JNIEnv *env, jobject thiz) {
      return env->NewStringUTF("hello world");
  }
  
  // Dynamically registered functions
  jstring dynamicRegisterFunction(JNIEnv *env, jobject thiz) {
      return env->NewStringUTF("hello, I'm from dynamicRegisterFunction");
  }
  
  // Register functions when the dynamic library is loaded
  int JNI_OnLoad(JavaVM *javaVM, void *reserved) {
      JNIEnv *jniEnv;
  
      if (javaVM->GetEnv((void**) (&jniEnv), JNI_VERSION_1_4) ! = JNI_OK) {return JNI_ERR;
      }
      jclass registerClass = jniEnv->FindClass("com/arcsoft/functionregisterdemo/MainActivity");
      JNINativeMethod jniNativeMethods[] = {
              // name signature nativeFunction
              {"dynamicRegisterFunction"."()Ljava/lang/String;",
                      (void *) (dynamicRegisterFunction)}
      };
      if (jniEnv->RegisterNatives(registerClass, jniNativeMethods,
                                  sizeof(jniNativeMethods) / sizeof((jniNativeMethods)[0]))"0) {
          return JNI_ERR;
      } else {
          returnJNI_VERSION_1_4; }}Copy the code

2.1.3 build.gradle

Configure the path of cmakelists. TXT and configure the current compiled ABI to be armeabi-v7a and arm64-V8a only

  apply plugin: 'com.android.application'

  android {
      ...
      defaultConfig {
          ...
          ndk.abiFilters 'armeabi-v7a'.'arm64-v8a'}... externalNativeBuild { cmake { path"src/main/cpp/CMakeLists.txt"
              version "3.10.2"
        }
      }
  }

  dependencies {
      ...
  }

Copy the code

2.1.4 compilation

We can choose to package the APK directly and run it, but in order to simulate calling the SDK, we can choose to manually package the dynamic library and use it.

Perform externalNativebuild (Release | Debug) (terminal executable gradlew externalNativebuildRelease or click on the Android The option in the Studio on the right side of Gradle) compiler release or the debug version of the dynamic library, choose externalNativeBuildRelease here, compile the results are as follows:

At this point, libhello-sdk.so is compiled, then remove the Native build configuration of the project and use the two dynamic libraries as if they were connected to the SDK.

2.2 Use compiled dynamic libraries correctly

2.2.1 Put the required dynamic inventory insrc/main/jniLibsdirectory

2.2.2 Remove the Native build configuration in Gradle

• More than one file was found with OS independent path ‘XXXX’. • More than one file was found with OS independent path ‘XXXX

    // externalNativeBuild {
    // cmake {
    // path "src/main/cpp/CMakeLists.txt"
    / / version "3.10.2"
    / /}
    / /}
Copy the code

2.2.3 inMainActivityThe use of

  package com.arcsoft.functionregisterdemo;
  
  import androidx.appcompat.app.AppCompatActivity;
  
  import android.os.Bundle;
  import android.widget.TextView;
  
  public class MainActivity extends AppCompatActivity {
      
      // Load the dynamic library
      static {
          System.loadLibrary("hello-sdk");
      }
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
  
          TextView tv = findViewById(R.id.sample_text);
          tv.setText(hello());
  
          tv.append("\n\n");
          tv.append(dynamicRegisterFunction());
      }
      // Statically registered functions
      public native String hello(a);
      
      // Dynamically registered functions
      public native String dynamicRegisterFunction(a);
  }
Copy the code

2.2.4 Normal operation effect

2.3 The above problem is repeated by using the compiled dynamic library incorrectly

2.3.1 Failed to Find the dynamic library

How to operate: No dynamic libraries are reserved in the jniLibs directory

UnsatisfiedLinkError error error error error couldn’t find “libhello-sdk.so”

The 2020-03-26 15:55:09. 448, 26336-26336 / com. Arcsoft. Functionregisterdemo E/AndroidRuntime: FATAL EXCEPTION: the main Process: com.arcsoft.functionregisterdemo, PID: 26336 java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file"/data/app/com.arcsoft.functionregisterdemo-6C3PyVyDJypXOtLP_dDykA==/base.apk"],nativeLibraryDirectories=[/data/app/com.arcsoft.functionregisterdemo-6C3PyVyDJypXOtLP_dDykA==/lib/arm64, /system/lib64, /system/vendor/lib64]]] couldn't find "libhello-sdk.so" at java.lang.Runtime.loadLibrary0(Runtime.java:1012) ....Copy the code

2.3.2 The loaded dynamic library ABI is incorrect

Operation mode: willarmeabi-v7aDynamic libraryarm64-v8adirectory

The UnsatisfiedLinkError error “XXXX” is 32-bit instead of 64-bit was reported when the dynamic library was loaded but the ABI was incorrect

The 2020-03-26 15:56:25. 747, 26517-26517 / com. Arcsoft. Functionregisterdemo E/AndroidRuntime: FATAL EXCEPTION: the main Process: com.arcsoft.functionregisterdemo, PID: 26517 java.lang.UnsatisfiedLinkError: dlopen failed:"/data/app/com.arcsoft.functionregisterdemo-EWDPPRqzg8u7sv1Dq30ZJA==/lib/arm64/libhello-sdk.so" is 32-bit instead of 64-bit
          at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
          ....
Copy the code

2.3.3 The length of dynamic library file is 0

Operation mode: Delete the dynamic library file and then cancel the deletion

This can be a minor issue with Android Studio, sometimes deleting files and then undeleting them will reappear, but with a size of 0.

The UnsatisfiedLinkError error dlopen failed was reported when the dynamic library was loaded. file offset for the library “XXXX” >= file size: 0 >= 0

The 2020-03-26 15:56:58. 114, 26669-26669 / com. Arcsoft. Functionregisterdemo E/AndroidRuntime: FATAL EXCEPTION: the main Process: com.arcsoft.functionregisterdemo, PID: 26669 java.lang.UnsatisfiedLinkError: dlopen failed: file offsetfor the library "/data/app/com.arcsoft.functionregisterdemo-PITl9rCd6FztSupEwwvjQA==/lib/arm64/libhello-sdk.so" >= file size: 0 >= 0
        at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
        at java.lang.System.loadLibrary(System.java:1669)
        ....
Copy the code

2.3.4 The XXXX function Cannot be Found during function execution

Operation mode: Confusing Java code is also the most common scenario that causes crash. In general, code confusion is not carried out when compiling the Debug version of APK, but when compiling the Release version of APK, confusion is carried out, which will cause the program to run normally when debugging, but crash when running the release version. In the code, we used static registration and dynamic registration to register functions.

For the JNI static registration, the JVM will according to the name and signature of the Java function to find the corresponding native function, if can’t find, it is Java. Lang. UnsatiesFiedLinkError error. Because our dynamic library contains functions that are statically registered and dynamically registered, directly confusing all functions may lead to crash when loading the dynamic library. Therefore, we manually modify the statically registered function to simulate the effect of statically registered functions being confused, change hello() function to a(), and run it. The error log is as follows:

java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.arcsoft.functionregisterdemo.MainActivity.a() (tried Java_com_arcsoft_functionregisterdemo_MainActivity_a and Java_com_arcsoft_functionregisterdemo_MainActivity_a__)
              at com.arcsoft.functionregisterdemo.MainActivity.a(Native Method)
Copy the code

The function name is changed to the original one.

2.3.5 Crash Occurs when loading the dynamic library

Obfuscating Java code can also cause crash when loading dynamic libraries.

For JNI dynamic registration, functions are generally registered in JNI_OnLoad. At this time, native functions are determined by function Pointers. JVM searches for corresponding Java functions according to the specified Java function name and function signature.

JNI DETECTED ERROR IN APPLICATION: JNI NewGlobalRef called with pending exception java.lang.NoSuchMethodError: no static or non-static method "classSignature + . + functionName + FunctionSignature"
Copy the code

Modify build.gradle file, configuration code confusion:

buildTypes {
     debug {
         minifyEnabled true
         proguardFiles  'proguard-rules.pro'}}Copy the code

Proguard-rules. pro does not have any configuration at present. Therefore, after running crash, part of the log is as follows. It can be seen from the log that Java functions cannot be found according to the specified rules.

. The 2020-03-26 15:58:39. 046, 26947-26947 / com. Arcsoft. Functionregisterdemo A/ionregisterdem: java_vm_ext.cc:542] JNI DETECTED ERROR IN APPLICATION: JNI NewGlobalRef called with pending exception java.lang.NoSuchMethodError: no static or non-static method"Lcom/arcsoft/functionregisterdemo/MainActivity; .dynamicRegisterFunction()Ljava/lang/String;".Copy the code

Modify ProGuard-rules. pro to add obfuscation rules and keep native functions in MainActivity:

-keepclasseswithmembers class com.arcsoft.functionregisterdemo.MainActivity{
   native <methods>;
}
Copy the code

At this time, the running effect is normal. It should be noted that if you write native functions by yourself, you need to modify the fields in Java in Native reflection, and you also need to ensure that the fields to be reflected are not confused.

Third, summary

The dynamic library cannot be called successfully if one of the following is not met:

  • The dynamic library and its dependent inventory are in and loaded successfully
  • Java function and native function association success (static registration or dynamic registration)

When encountering an error, logs usually contain some key information. You can see the specific cause of the error, analyze the log, and learn how to troubleshoot the error.