2012-05-18 4 views
12

私のアプリケーションに2つのネイティブライブラリ(.so)を統合しました。ライブラリはうまくコンパイルされ、アプリケーションにロードすることもできます。ライブラリのネイティブメソッドを初めて呼び出すときは正常に動作しますが、アクティビティで同じメソッドを再度呼び出すと、アプリケーションがシャットダウンします。ここで述べたように、私が直面していますアクティビティで第三者ライブラリのネイティブメソッドを2回呼び出すと、Androidアプリケーションが終了します

問題はまったく同じです:
http://grokbase.com/t/gg/android-ndk/1226m68ydm/app-exit-on-second-native-call

機能するソリューションは、他の活動にネイティブメソッドを呼び出すとはSystem.exitを経由して強制的にそれをシャットダウンすることである

(0) 。記事の後、私は成功した操作の後に、呼び出されたメソッドのNULLにポインタを設定しようとしましたが、これも私を助けませんでした。また、System.loadLibrary()によって読み込まれたライブラリをアンロードすることもできません。

新しいアクティビティを作成せずにネイティブメソッドを複数回呼び出す必要があります。任意のアイデアはどのようにこの問題を解決するには?

(私は最終的に解決策を見つけた...ここにある)

さて、私はこの問題を解決する方法をついに発見しました。解決策は実際には非常に簡単です。別の独立したネイティブライブラリ(ユーティリティライブラリ)をビルドして、他のライブラリをロードおよびアンロードします。私たちがする必要があるのは、ユーティリティのネイティブメソッドで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); 

これは、他の人が同じ問題に直面するのに役立ちます。

+0

dlclose(ハンドル)はどこですか?アクティビティコードまたはJNIコードの意味ですか? –

+0

そのJNIコードです。これは、dlfcn.hヘッダーファイルで使用できる関数です。 – ZakiMak

+0

それを得ました、私はあなたがこのcクラスの他のライブラリをロードしてアンロードするのを見ます。しかし、どのように私はJavaコードにリンクする必要がありますか? –

答えて

5

私の解決策は、共有ライブラリコードを実行するサービスを開始していました。このサービスは異なるプロセス名(Androidマニフェストで設定することができます)です。それはどのような方法でアプリケーションに影響を与えずに、実行が終了するときProcess.killProcess(Process.myPidを())を使用する。

は私のために非常によく働い、それは他の誰かを助け願っています。

+0

これはきれいな解決策だと思います。それはより困難ですが、より良いです。 – ezefire

2

これがトップヒットとしてこの問題については、問題自体がまだ存在するため、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ファイルがあります丁寧にそれを置くために少し薄れた。 1つのAndroid.mkファイルに2つの別個のライブラリを構築しているので、これはもう少し複雑なので、他のNDKがファイルを作るので、いくつかの奇妙なエラーが発生した場合、たとえば、https://stackoverflow.com/a/6243727/334402
関連する問題