【问题标题】:Can't load native shared library with dependencies in a native activity app无法在本机活动应用程序中加载具有依赖项的本机共享库
【发布时间】:2012-09-13 12:43:14
【问题描述】:

在我的 Android 应用中,我有 4 个库:

libTemplate.so
   depends on libPorkholt.so
libPorkholt.so
   depends on libpng15.so
   depends on liblua.so
   depends on libopenal.so
libpng15.so
liblua.so
libopenal.so

如果我编写一个与 libTemplate 链接并手动调用 ANativeActivity_onCreate 的小型命令行可执行文件,它会链接并运行得很好(如果我将 LD_LIBRARY_PATH 指向 /data/data/com.mycompany.Template/lib)

如果我运行我的应用程序,我会收到这个非常有用的错误消息:

E/AndroidRuntime(13214): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mycompany.Template/android.app.NativeActivity}: java.lang.IllegalArgumentException: Unable to load native library: /data/data/com.mycompany.Template/lib/libTemplate.so

它甚至没有进入ANativeActivity_onCreate,所以我唯一的猜测是它与链接有关

我应该提到我正在使用 CMake 和这个脚本:http://code.google.com/p/android-cmake/ 自己构建库(没有 ndk-build)。我设法用它编译了本机活动示例,所以我知道它可以工作。

另外,我确保没有库在其 soname 中包含版本号

我的清单:

<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.mycompany.Template"
        android:versionCode="1"
        android:versionName="1.0">

    <!-- This is the platform API where NativeActivity was introduced. -->
    <uses-sdk android:minSdkVersion="9" />

    <!-- This .apk has no Java code itself, so set hasCode to false. -->
    <application android:label="Template Porkholt project" android:hasCode="false">

        <!-- Our activity is the built-in NativeActivity framework class.
             This will take care of integrating with our NDK code. -->
        <activity android:name="android.app.NativeActivity"
                android:label="Template Porkholt project"
                android:configChanges="orientation|keyboardHidden">
            <!-- Tell NativeActivity the name of or .so -->
            <meta-data android:name="android.app.lib_name"
                    android:value="Template" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest> 
<!-- END_INCLUDE(manifest) -->

【问题讨论】:

  • 您能告诉我您是如何“手动调用 ANativeActivity_onCreate”的吗?你是用 C 代码做的吗?
  • 如何从 soname 中删除版本号?您是否必须重建所有依赖项以删除版本后缀? (我意识到这真的很老了,但我正在尝试做同样的事情并且对此感到疑惑)。

标签: android linker android-ndk native-activity


【解决方案1】:

您的 Activity 可能有一个调用 System.Load("libTemplate.so") 的静态构造函数。它应该根据依赖顺序加载其他库。

【讨论】:

    【解决方案2】:

    我认为 Android 不会自动加载清单中指定的库以外的库,因此您应该创建一个“虚拟”Java 类来加载外部依赖项,它应该包含:

    static {
        System.loadLibrary("openal");
        System.loadLibrary("lua");
        System.loadLibrary("png15");
        System.loadLibrary("Porkholt");
        System.loadLibrary("Template");
    }
    

    由于这个部分是静态的,所以它会在类加载时执行,即使它的方法没有被调用。

    【讨论】:

    • 是的,这就是问题所在,但我设法在没有 Java 的情况下解决了它。我要发布我的解决方案
    【解决方案3】:

    由于显然 Android 不够聪明,无法正确设置 LD_LIBRARY_PATH,因此我设法通过创建一个手动加载实际活动的小型引导程序库来解决我的问题。代码如下:

    #include <android/native_activity.h>
    #include <android/log.h>
    #include <dlfcn.h>
    #include <errno.h>
    #include <stdlib.h>
    
    #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "Porkholt", __VA_ARGS__))
    #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "Porkholt", __VA_ARGS__))
    
    #define LIB_PATH "/data/data/@PH_BUNDLE_ID@/lib/"
    
    void * load_lib(const char * l)
    {
        void * handle = dlopen(l, RTLD_NOW | RTLD_GLOBAL);
        if (!handle)
        {
            LOGE("dlopen(\"%s\"): %s", l, strerror(errno));
            exit(1);
        }
        return handle;
    }
    
    void ANativeActivity_onCreate(ANativeActivity * app, void * ud, size_t udsize)
    {
        LOGI("Loaded bootstrap");
        load_lib(LIB_PATH "libpng15.so");
        load_lib(LIB_PATH "liblua.so");
        load_lib(LIB_PATH "libopenal.so");
        load_lib(LIB_PATH "libPorkholt.so");
        void (*main)(ANativeActivity*, void*, size_t) = dlsym(load_lib(LIB_PATH "lib@PH_APP_TARGET@.so"), "ANativeActivity_onCreate");
        if (!main)
        {
            LOGE("undefined symbol ANativeActivity_onCreate");
            exit(1);
        }
        main(app, ud, udsize);
    }
    

    【讨论】:

    • 感谢 da_petcu21。使用本机代码在 JVM 中加载库是一项宝贵的贡献;) ... +1
    • 你是说让原生活动加载 .so 库的唯一方法是让第二个原生活动手动加载库并启动第一个原生活动?如果没有,您能否详细说明您的解决方案是如何工作的?已经在这个问题上工作了很长一段时间,但运气不佳。
    【解决方案4】:

    这不再影响 API 24+(请参阅框架修复 here)。但是,如果您需要支持旧版本,请扩展 NativeActivity,改为在清单文件中引用您的扩展,并添加 Sdra's answer 中提到的 static 块作为解决方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-24
      • 1970-01-01
      • 2011-11-07
      • 2021-01-06
      • 2018-12-02
      • 1970-01-01
      相关资源
      最近更新 更多