2012-05-18 4 views
12

Nella mia applicazione sono integrate due librerie native (.so). Le librerie si compongono bene e posso caricarle anche nella mia applicazione. La prima volta che invoco un metodo nativo di una libreria funziona correttamente, ma se richiamo di nuovo lo stesso metodo nell'attività l'applicazione si arresta.Chiamando il metodo nativo due volte di una libreria di terze parti in un'attività, l'applicazione Android viene chiusa

Il problema che sto affrontando è esattamente lo stesso di cui al qui:
http://grokbase.com/t/gg/android-ndk/1226m68ydm/app-exit-on-second-native-call

La soluzione che funziona è quella di invocare il metodo nativo in un'altra attività e spegnerlo con forza tramite System.exit (0) . Seguendo l'articolo ho provato a impostare i puntatori su NULL del metodo chiamato dopo un'operazione riuscita, ma anche questo non mi ha aiutato. Inoltre non è possibile scaricare una libreria una volta caricata da System.loadLibrary().

Voglio chiamare i metodi nativi più di una volta senza creare una nuova attività. Qualche idea su come risolvere questo problema?

(ho finalmente trovato una soluzione ... ECCO)

Va bene, ho finalmente trovato un modo per risolvere questo problema. La soluzione è in realtà piuttosto semplice. Costruisci un'altra libreria nativa indipendente (libreria di utilità) per caricare e scaricare le altre librerie. Quello che dobbiamo fare è usare dlopen() e dlclose() nel metodo nativo dell'utilità. Possiamo caricare la libreria di utilità come prima tramite System.loadLibrary().

Così, nel metodo nativo della libreria di utilità quello che dobbiamo fare è: funzioni

Usa #include <dlfcn.h> // questo è necessario per chiamare dlopen() e dlclose().

Fornire conduttore e prototipo di funzione:

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

Aprire la libreria tramite dlopen():

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

Prendi e chiamare la funzione della biblioteca:

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

Ora che il la chiamata è fatta. Chiudilo via dlclose():

dlclose(handle); 

Spero che questo aiuti gli altri ad affrontare lo stesso problema.

+0

dove si fa dlclose (handle)? Intendo nel codice Attività o JNI? –

+0

È in codice JNI. È una funzione disponibile tramite il file di intestazione dlfcn.h. – ZakiMak

+0

ok capito, vedo che carichi e scarica altre librerie in questa classe c. Ma come dovrei collegarlo al mio codice Java? –

risposta

5

Quindi ... la mia soluzione stava avviando un servizio che esegue il codice della libreria condivisa, questo servizio ha un nome di processo diverso (puoi impostarlo nel Manifest Android), poiché è un processo diverso che puoi uccidere (utilizzando Process.killProcess (Process.myPid()) quando finisce l'esecuzione, senza influenzare l'applicazione in alcun modo.

funzionato molto bene per me, spero che aiuta a qualcun altro.

+0

Penso che questa sia una soluzione pulita. È più difficile da fare ma migliore. – ezefire

2

quanto questo è il top hit per questo problema e poiché il problema è ancora esistente, sembra che l'approccio che ZakiMak ha condiviso con noi sia ancora la soluzione più popolare.

Per gli altri che potrebbero voler attuarlo e vorrebbero un po 'più in dettaglio per le ultime versioni di Android, qui ci sono alcune note che ho fatto come mi sono imbattuto attraverso questa:

  • In primo luogo, v'è una soluzione che implementa questo approccio su GitHub ora. Non l'ho provato personalmente, ma l'ho usato come riferimento. È molto utile vedere come è strutturato il file Android.mk e come viene aperta la libreria e richiamati i metodi. Link è qui: https://github.com/jhotovy/android-ffmpeg
  • Il percorso della cartella della libreria nativa cambia rispetto alle versioni Android e sembra anche cambiare ogni volta che si esegue l'app (anche se questo potrebbe essere solo in modalità di debug). In ogni caso, è meglio passare il percorso dal metodo Java chiamante se possibile. Per esempio:

Nella classe involucro 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); 

} 

Nella 'prima' o libreria nativa 'esterno':

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); 
} 
  • Il file è un Android.mk piccolo 'traballante' per dirla educatamente. Poiché stai creando due librerie separate in un unico file Android.mk, questo potrebbe essere un po 'più complesso di altri NDK per creare file, quindi se ottieni degli strani errori esegui delle ricerche prima di iniziare a far sparire il tuo progetto. Ad esempio: https://stackoverflow.com/a/6243727/334402
Problemi correlati