当调用System.loadLibrary时,onJNILoad未被调用

3
我在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可以使用它。它依赖于一个ICU库,该库位于libjavacore中。因此,我修改了Android.mk文件,以确保libjavacore只包含与ICU相关的源文件,并且最终编译的so文件被包含在我的apk中。

TextClock:
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"了。 - eozgonul
我还没有。为什么需要它? - Bear
1
当您的进程从zygote分叉出来成为应用程序时,您可能会遇到与已继承的工厂版本冲突的问题,因为该版本已经被预先加载,它可能认为库已经加载完毕而无所作为。Logcat可能会澄清这一点。或者以应用程序uid获取一个shell,并查看其/proc/pid##/maps或从Java代码中转储该信息。您可能需要将库命名为其他名称,但要注意函数名称冲突! - Chris Stratton
@ChrisStratton 您是正确的。我没有遇到其他错误消息。看起来libjavacore依赖于其他so文件。请查看我的更新。或者实际上我应该开一个新问题,您可以在下面回答,这样我就可以接受您刚才提到的答案。 - Bear
请详细解释你正在做什么,你是通过apk提供这个库还是通过修改系统来实现的? - Chris Stratton
显示剩余10条评论
2个回答

1
在旧版本的Android中,该库直接链接到VM(libdvm.so链接到libnativehelper.so,后者链接到libjavacore.a)。在最近的版本中,这已经改变了,始终使用内部本地库加载机制在启动时加载库,因此如果存在,则会调用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,因此记录了一条消息告诉您,以防您期望不同。
如果您在已root的设备上替换/system/lib中的libjavacore.so,并运行dalvikvm命令,则应在日志文件中看到您的消息,与我上面显示的消息混合在一起。如果您重新启动系统,则应在zygote启动期间看到您的消息,并且除非运行基于Dalvik的命令(如am),否则不会再次出现。

是的...如果您在任何对exec()函数的调用初始化之前替换或覆盖库路径,则会覆盖原始库。但我怀疑发布者实际上是试图将它们自己打包到.apk文件中并显式加载它,由于系统版本已被加载到父Zygote中,因此在他们尝试将其加载到子APK进程中之前,可能无法正常工作。 - Chris Stratton
啊,如果虚拟机认为它是同一个库,那么logcat中应该会有“已加载”的消息;如果不是,则我期望JNI_OnLoad可以正常工作(并且新方法将替换旧方法,因为它们使用显式注册)。你之前建议重命名共享库的想法可能会解决问题。 - fadden
1
@fadden 很抱歉,我将进行库的移植而不是替换。因此更名是有效的。感谢大家。 - Bear

1
你可以使用工具链文件夹中的readelf来转储.so文件中的符号。检查是否导出了JNI_OnLoad。使用-s(符号)命令和libs文件夹中.so文件的名称即可实现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接