【问题标题】:onJNILoad not being called when calling System.loadLibrary调用 System.loadLibrary 时未调用 onJNILoad
【发布时间】:2013-12-13 06:54:57
【问题描述】:

我在 JNI_OnLoad 中添加了一条打印日志语句,但我发现它没有被调用。这是我的 JNI_OnLoad 方法。

extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    __android_log_print(ANDROID_LOG_INFO,  __FUNCTION__, "onLoad");
   // some init code
}

我是否需要在特定文件中声明 JNI_OnLoad 或在 Android.MK 中声明某事告诉系统在哪里可以找到 JNI_OnLoad 方法?现在我只是放入众多 .cpp 文件之一。

已附加已编译的 .so 库。我尝试转储 so 文件,我确定 JNI_OnLoad 方法已导出。 https://docs.google.com/file/d/0B089WeEZXTb3ZjZiQllaYThuUUk/edit

实际上,我正在尝试从 android 源 (libcorejava.so) 移植一个库。为了避免类路径冲突,我已经更改了类路径。

这里是声明 JNI_OnLoad 的文件: https://android.googlesource.com/platform/libcore/+/master/luni/src/main/native/Register.cpp 我已经将签名更改为上述签名以匹配标准签名

编辑: 我发现android源没有通过System.loadLibrary加载它!它说libcorejava是用来实现System.loadLibrary的,所以我们不能使用System.loadLibrary来加载它。但就我而言,这应该不是问题,因为我只需要部分功能(ICU 相关)。

https://android.googlesource.com/platform/dalvik/+/master/vm/Init.cpp

 // Most JNI libraries can just use System.loadLibrary, but you can't
    // if you're the library that implements System.loadLibrary!
    loadJniLibrary("javacore");
    loadJniLibrary("nativehelper");

编辑 2:
原来是因为库的名字冲突! 但似乎 libjavacore 需要其他库。有什么工具可以列出我缺少的依赖项吗?

java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1286]: XXX

编辑 3:
TextClock 是一个新的显示时间的 api。它只存在于 4.2+ api 上。我正在尝试向后移植它,以便旧的 sdk 可以使用它。它依赖于位于 libjavacore 中的 ICU 库。所以我修改了 Android.mk 文件,以确保 libjavacore 只包含 icu 相关的源文件,并且最终编译的 so 文件包含在我的 apk 中。

文本时钟:
http://developer.android.com/reference/android/widget/TextClock.html

它现在可以在最初支持 TextClock 的手机中使用,但不能在旧设备中使用。这是异常日志。我认为这是因为 libjavacore 是 ICU 库的包装器。除了包装器,我还需要移植 ICU 库。但我要放弃了,因为 ICU 图书馆的规模很大,而且似乎不值得……

    12-13 14:07:54.859: E/AndroidRuntime(2091): java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1306]:    36 cannot locate '_ZN6icu_516Locale14createFromNameEPKc'...
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at java.lang.Runtime.loadLibrary(Runtime.java:370)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at java.lang.System.loadLibrary(System.java:535)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at com.example.time.MainActivity.onCreate(MainActivity.java:20)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.Activity.performCreate(Activity.java:5008)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread.access$600(ActivityThread.java:130)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.os.Handler.dispatchMessage(Handler.java:99)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.os.Looper.loop(Looper.java:137)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at android.app.ActivityThread.main(ActivityThread.java:4745)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at java.lang.reflect.Method.invokeNative(Native Method)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at java.lang.reflect.Method.invoke(Method.java:511)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    12-13 14:07:54.859: E/AndroidRuntime(2091):     at dalvik.system.NativeStart.main(Native Method)

【问题讨论】:

  • 将其放入众多 cpp 文件之一即可。我不知道问题是什么,但我很确定这不是原因。我唯一想到的是你忘了在函数前加上 extern "C"。
  • 是的,我没有。为什么需要它?
  • 我尝试改成 extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {,但还是一样...
  • 当成为您的应用程序的进程从 zygote 分叉时,您可能正面临与继承的工厂版本的冲突已经加载 - 它可能什么都不做库已经加载。 Logcat 可能会澄清。或者获取一个 shell 作为应用程序 uid 并查看它的 /proc/pid##/maps 或从 Java 代码中转储它。您可能需要为您的库命名其他名称,但请注意函数名称冲突!

标签: android c android-ndk java-native-interface dalvik


【解决方案1】:

您可以使用工具链文件夹中的 readelf 转储 .so 文件中的符号。检查 JNI_OnLoad 是否已导出。 -s(符号)命令和你的 libs 文件夹中的 .so 文件的名称应该可以做到这一点。

【讨论】:

    【解决方案2】:

    在旧版本的 Android 中,库直接链接到 VM(libdvm.so 链接到 libnativehelper.so,而 libjavacore.a 链接)。在最近的版本中,this changed 总是在启动时加载库,使用内部的本地库加载机制,所以如果存在 JNI_OnLoad 将被调用。

    如果我运行adb shell dalvikvm Foo(其中“Foo”不存在),我会在 logcat 中看到:

    D dalvikvm: Trying to load lib libjavacore.so 0x0
    D dalvikvm: Added shared lib libjavacore.so 0x0
    D dalvikvm: Trying to load lib libnativehelper.so 0x0
    D dalvikvm: Added shared lib libnativehelper.so 0x0
    D dalvikvm: No JNI_OnLoad found in libnativehelper.so 0x0, skipping init
    

    所以它加载了libjavacore.so,显然找到并运行了JNI_OnLoad(没有消息就是好消息)。它加载了libnativehelper.so,但没有找到JNI_OnLoad,所以它记录了一条消息告诉你,以防你有其他期望。

    如果您将/system/lib 中的libjavacore.so 替换(在有根设备上),并运行dalvikvm 命令,您应该会在日志文件中看到您的消息,其中包含我上面显示的消息。如果您重新启动系统,您应该会在 zygote 启动期间看到您的消息,并且不会再次看到,除非某些东西运行基于 Dalvik 的命令(例如 am)。

    【讨论】:

    • 是的...如果您替换或库路径覆盖原始 before 任何对 exec() 函数的调用从头开始初始化事物。但我怀疑张贴者试图将自己的 .apk 打包并显式加载,这可能不起作用,因为在他们尝试将其加载到后代 apk 进程之前,系统版本已经加载到父 zygote 中。
    • 啊。如果 VM 认为它是同一个库,则 logcat 中应该有一条“已经加载”的消息;如果没有,那么我希望JNI_OnLoad 能够正常工作(以及替换旧方法的新方法,因为它们使用显式注册)。您之前重命名共享库的建议可能会解决问题。
    • @fadden 对不起,我打算移植库而不是替换。所以重命名有效。谢谢大家。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多