【问题标题】:How do I import shared object libraries at runtime in Android?如何在 Android 运行时导入共享对象库?
【发布时间】:2012-04-25 22:55:44
【问题描述】:

我正在开发一个开源模拟器项目,它有多个可定制的原生插件。这些插件构建为原生共享对象库(.so 文件),并通过 JNI 在原生和 Java 之间提供各种接口。与其将 APK 与每个创建的插件一起分发,而且为了允许人们构建自己的自定义插件,我需要一种在安装应用程序后随时导入这些 .so 文件的方法。

我发现我可以将文件复制到文件夹 /data/data/[package_name],但不能复制到 lib/ 子文件夹(因为它归“系统”组所有)。要在 Java 中使用 JNI 接口,我必须调用 System.loadLibrary( libname );但是,这似乎要求 .so 文件位于 lib/ 子文件夹中。我能想到的唯一方法是要求用户拥有一个根设备。有没有其他方法可以做到这一点?

【问题讨论】:

    标签: android android-ndk java-native-interface shared-libraries


    【解决方案1】:

    使用 System.load() 代替:

    static
    {
        final String[] libs = {
            "/data/data/com.foo.test/lib/liba.so",
            "/data/data/com.foo.test/lib/libb.so"
        };
    
        for (int i=0; i<libs.length; ++i)
        {
            Log.d(TAG, "Loading " + libs[i] + "...");
            System.load(libs[i]);
        }
    }
    

    $ adb logcat

    D/LibTest (1022):正在加载 /data/data/com.foo.test/lib/liba.so...
    D/dalvikvm(1022): 试图加载 lib /data/data/com.foo.test/lib/liba.so 0x40512640
    D/dalvikvm(1022):添加共享库 /data/data/com.foo.test/lib/liba.so 0x40512640
    D/dalvikvm(1022):在 /data/data/com.foo.test/lib/liba.so 0x40512640 中找不到 JNI_OnLoad,跳过初始化
    D/LibTest (1022):加载 /data/data/com.foo.test/lib/libb.so...
    D/dalvikvm(1022): 试图加载 lib /data/data/com.foo.test/lib/libb.so 0x40512640
    D/dalvikvm(1022):添加共享库 /data/data/com.foo.test/lib/libb.so 0x40512640
    D/dalvikvm(1022):在 /data/data/com.foo.test/lib/libb.so 0x40512640 中找不到 JNI_OnLoad,跳过初始化

    【讨论】:

    • 啊哈,我不知道 System.load 用于此。我想我应该在发布问题之前进行研究。感谢您的帮助!
    • 这是否仍然需要库位于 data/data/com.foo.test/lib/liba.so 中?如何将文件添加到该目录?
    【解决方案2】:

    将您的插件分发为 APK,并通过 IPC 机制从主机与插件通信:

    • 广播Intents
    • 服务(命令或绑定模式)
    • ContentProvider

    作为附带奖励,如果插件需要比主机更多/不同的权限,则支持此功能。

    诚然,这将需要 IPC,它会增加不小的开销,引导您使用粗粒度的插件通信协议。而且,它会消耗更多的 RAM(额外的 CPU 时间会消耗更多的电池寿命)。

    【讨论】:

    • 可以作为替代方案,但不是最佳解决方案。不过,有选择很好。谢谢!
    【解决方案3】:

    您可以使用 dlopen 通过 c++ 加载 so 文件。 我在 c++ 中使用此代码将 so 文件加载到任何文件夹中。

    // KGEmain function pointer
    typedef void (*KGEmain) ();
    
    std::string strPluginName = "/data/data/com.kge.android/lib/lib";
    strPluginName += appname;
    strPluginName += ".so";
    
    void* handle = dlopen(strPluginName.c_str(), RTLD_LAZY);
    
    const char* error;
    
    if (!handle)
    {
        LOGE("can't load the %s plugin. (%s)", strPluginName.c_str(), dlerror());
        return;
    }
    // Clear any exiting error
    dlerror();
    
    // Load the KGEmain function
    pFn = (KGEmain)dlsym(handle, "KGEmain");
    if ((error = dlerror()) != NULL || !pFn)
    {
        LOGE("KGEmain function dose not find in %s plugin.\n%s", strPluginName.c_str(), error);
        return;
    }
    
    pFn();
    

    【讨论】:

    • 谢谢!我实际上有这部分,但没有注意到它也可以在 Java 端完成。不过,很高兴在这里得到这个答案,以获得完整的画面。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    相关资源
    最近更新 更多