【问题标题】:Calling native method twice of third party library in an Activity causes the Android application to close downActivity中调用第三方库的native方法两次导致Android应用关闭
【发布时间】:2012-05-25 19:41:55
【问题描述】:

我在我的应用程序中集成了两个本机库 (.so)。这些库编译得很好,我也可以将它们加载到我的应用程序中。第一次调用库的本机方法时它工作正常,但如果我在 Activity 中再次调用相同的方法,应用程序将关闭。

我面临的问题与这里提到的完全相同:
http://grokbase.com/t/gg/android-ndk/1226m68ydm/app-exit-on-second-native-call

可行的解决方案是在另一个 Activity 中调用本机方法并通过 System.exit(0) 将其强制关闭。在文章之后,我尝试在成功操作后将被调用方法的指针设置为 NULL ,但这也对我没有帮助。一旦库被 System.loadLibrary() 加载,也无法卸载它。

我想在不创建新 Activity 的情况下多次调用本机方法。任何想法如何解决这个问题?

(我终于找到了解决方案……在这里)

好的,我终于找到了解决这个问题的方法。解决方案实际上非常简单。构建另一个独立的本机库(实用程序库)来加载和卸载其他库。我们需要做的是在实用程序的本机方法中使用 dlopen() 和 dlclose()。我们可以像以前一样通过 System.loadLibrary() 加载实用程序库。

所以在实用程序库的原生方法中我们需要做的是:

使用#include <dlfcn.h> // 这是调用 dlopen() 和 dlclose() 函数所必需的。

提供处理程序和函数原型:

void *handle;
typedef int (*func)(int); // define function prototype
func myFunctionName; // some name for the function

通过 dlopen() 打开库:

handle = dlopen("/data/data/my.package.com/lib/somelibrary.so", RTLD_LAZY);

获取和调用库函数:

myFunctionName = (func)dlsym(handle, "actualFunctionNameInLibrary");
myFunctionName(1); // passing parameters if needed in the call

现在通话已完成。通过 dlclose() 关闭它:

dlclose(handle);

希望这能帮助其他面临同样问题的人。

【问题讨论】:

  • 你在哪里做 dlclose(handle)?我的意思是在 Activity 或 JNI 代码中?
  • 它在 JNI 代码中。这是一个可通过 dlfcn.h 头文件获得的函数。
  • 好的,我看到你在这个 c 类中加载和卸载了其他库。但是我应该如何将它链接到我的 Java 代码?
  • 您需要了解 NDK。 link - 本教程将向您展示如何逐步完成所有操作。简而言之,一旦您的 NDK 环境准备就绪: 1. 您需要编写 C 文件。 2. 编写您的 Android.mk 文件以生成 .so - 共享库。 3. 使用 ndk 构建 .so 文件。 4. 编写 Java 类,您将在其中公开本地方法,并通过 System.loadLibrary 在静态块中加载库。
  • 我想知道你是如何让 dlclose 工作的,在 ndk 文档中它清楚地指出“目前从不调用静态析构函数,无论是在程序退出时,还是在调用 dlclose() 时。”请参考 NDK 文件夹(ndk 在您的系统上解压缩并参考 SYSTEM-ISSUES.html)

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


【解决方案1】:

由于这是该问题的热门话题,并且问题本身仍然存在,因此 ZakiMak 与我们分享的方法似乎仍然是最受欢迎的解决方案。

对于可能想要实现它并希望了解最新 Android 版本的更多详细信息的其他人,以下是我在偶然发现时做的一些笔记:

  • 首先,现在在 GitHub 上有一个实现这种方法的解决方案。我没有亲自尝试过,但我已将其用作参考。了解 Android.mk 文件的结构以及库的打开方式和方法调用方式非常有用。链接在这里:https://github.com/jhotovy/android-ffmpeg
  • 本机库文件夹的路径随 Android 版本的变化而变化,并且每次运行应用程序时它似乎也会发生变化(尽管这可能只是处于调试模式)。无论哪种方式,如果可能,最好从调用 Java 方法传入路径。例如:

在Java包装类中:

import android.content.Context;
import android.util.Log;

public class FfmpegJNIWrapper {

    //This class provides a Java wrapper around the exposed JNI ffmpeg functions.

    static {
        //Load the 'first' or 'outer' JNI library so this activity can use it
        System.loadLibrary("ffmpeg_wraper_multi_invoke_jni");
    }

    public static int call_ffmpegWrapper(Context appContext, String[] ffmpegArgs) {
        //Get the native libary path
        String nativeLibPath = appContext.getApplicationInfo().nativeLibraryDir;

        //Call the method in the first or 'outer' library, passing it the
        //native library past as well as the original args
        return ffmpegWrapper(nativeLibPath, ffmpegArgs);
    }


    // Native methods for ffmpeg functions
    public static native int ffmpegWrapper(String nativeLibPath, String[] argv);

}

在“第一”或“外部”原生库中:

JNIEXPORT jint JNICALL Java_com_yourpackage_androidffmpegwrapper_FfmpegJNIWrapper_ffmpegWrapper(JNIEnv *pEnv, jobject pObj, jstring nativeLibPath, jobjectArray javaArgv) {

    //Get the second or 'inner' native library path
    char* nativePathPassedIn = (char *)(*pEnv)->GetStringUTFChars(pEnv, nativeLibPath, NULL);
    char ourNativeLibraryPath[256];
    snprintf(ourNativeLibraryPath, sizeof (ourNativeLibraryPath), "%s%s", nativePathPassedIn, "/libffmpeg_wraper_jni.so"); //the name of your ffmpeg library

    //Open the so library
    void *handle;
    typedef int (*func)(JNIEnv*, jobject, jobjectArray);
    handle = dlopen(ourNativeLibraryPath, RTLD_LAZY);
    if (handle == NULL) {
        __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "could not open library: %s", dlerror());
        printf("Could not dlopen(\"libbar.so\"): %s\n", dlerror());
        return(-1);
    }

    //Call the ffmpeg wrapper functon in the second or 'inner' library
    func reenterable_ffmpegWrapperFunction;
    reenterable_ffmpegWrapperFunction = (func)dlsym(handle, "Java_com_yourpackage_androidffmpegwrapper_FfmpegJNIWrapper_ffmpegWrapper");
    reenterable_ffmpegWrapperFunction(pEnv, pObj, javaArgv); //the original arguments

    //Close the library
    dlclose(handle);

    // return
    return(1);
}
  • 礼貌地说,Android.mk 文件有点“不稳定”。因为您在一个 Android.mk 文件中构建了两个独立的库,所以这可能比其他 NDK 生成的文件更复杂一些,因此如果您遇到一些奇怪的错误,请在开始拆分项目之前进行一些搜索。例如:https://stackoverflow.com/a/6243727/334402

【讨论】:

    【解决方案2】:

    所以...我的解决方案是启动一个运行共享库代码的服务,该服务具有不同的进程名称(您可以在 Android Manifest 中设置它),因为它是一个不同的进程,您可以杀死它(使用Process.killProcess(Process.myPid()) 完成运行后,不会以任何方式影响您的应用程序。

    对我来说效果很好,希望对其他人有帮助。

    【讨论】:

    • 我认为这是一个干净的解决方案。它更难做,但更好。
    猜你喜欢
    • 2018-11-07
    • 1970-01-01
    • 1970-01-01
    • 2019-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多