2016-01-15 8 views
33

Ich führe Komponententests in Android Studio. Ich habe eine Java-Klasse, die mit dem folgenden CodeKomponententest Java-Klasse, die native Bibliothek lädt

static 
    { 
     System.loadLibrary("mylibrary"); 
    } 

Aber wenn ich teste diese Klasse in meinem src/test Verzeichnis eine native Bibliothek lädt I

java.lang.UnsatisfiedLinkError: no mylibrary in java.library.path 
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1864) 
    at java.lang.Runtime.loadLibrary0(Runtime.java:870) 
    at java.lang.System.loadLibrary(System.java:1122) 

bekommen Wie kann ich es den Weg der nativen finden machen .so Bibliotheken, die bei src/main/libs befindet, um Test ohne Fehler zu testen?

Hinweis: innerhalb src/main/libs Verzeichnis I 3 weitere Unterverzeichnisse: armeabi, mips und x86. Jede dieser Dateien enthält die richtige .so-Datei. Ich verwende die nicht experimentelle Version für den Aufbau von NDK-Bibliotheken.

Ich möchte nicht andere Drittanbieter-Testbibliotheken verwenden, da alle meine anderen "reinen" Java-Klassen in der Einheit getestet werden können. Aber wenn das nicht möglich ist, bin ich offen für Alternativen.

Hier mein Testcode ist, die den Fehler wirft

@Test 
    public void testNativeClass() throws Exception 
    { 
     MyNativeJavaClass test = new MyNativeJavaClass("lalalal") 
     List<String> results = test.getResultsFromNativeMethodAndPutThemInArrayList(); 
     assertEquals("There should be only three result", 3, results.size()); 
    } 
+0

Ich bin ein wenig verwirrt - wie kann der obige Code diesen Fehler auslösen? – PhilLab

+0

@PhilLab Dieser Code ist Teil einer Java-Klasse, die eine native Methode lädt, wie Sie sehen können. Es gibt kein Problem beim Ausführen meiner App. Dieser Fehler tritt jedoch beim Testen dieser Klasse im Android Studio – ThanosFisherman

+0

auf, aber warum wird ein Fehler ausgelöst, wenn Sie ihn fangen? Sind Sie sicher, dass dies die Bibliothek ist, die nicht geladen werden kann oder haben Sie eine andere in Ihrem Projekt? – PhilLab

Antwort

7

Die einzige Lösung, die ich gefunden, die ohne Hacks funktioniert, ist JUnit durch Instrumentierung Prüfung (androidTest Verzeichnis) zu verwenden. Meine Klasse kann jetzt gut getestet werden, aber mit Hilfe des Android-Geräts oder Emulators.

+0

Es scheint, dass [Suresh Joshi] (https://stackoverflow.com/users/992509/sjoshi) aufgelöst hat Das größte Hindernis [hier] (https://Stackoverflow.com/a/47325408/192373). Trotzdem müssen einige Vorbehalte gelöst werden. –

0

Nur sicher machen, das Verzeichnis der Bibliothek enthält, wird in der java.library.path Systemeigenschaft enthalten.

Aus dem Test, den Sie es, bevor Sie die Bibliothek laden einstellen könnte:

System.setProperty("java.library.path", "... path to the library .../libs/x86"); 

Sie den Pfad codierten Fest angeben können, aber das wird das Projekt weniger tragbar auf andere Umgebungen machen. Also schlage ich vor, dass Sie es programmatisch aufbauen.

+0

Noch funktioniert nicht. Ich habe den absoluten Pfad zu der x86-Lib innerhalb meiner Testmethode eingegeben, aber immer noch denselben Fehler erhalten. – ThanosFisherman

+0

Versuchen Sie, die '.so'-Erweiterung im' loadLibrary'-Aufruf hinzuzufügen. – Henry

+0

Kein Erfolg. Gleicher Fehler – ThanosFisherman

0

Die .so-Dateien unter sind zu

src/main/jniLibs

Nicht

unter src/main/libs platziert

(Getestet mit Android Studio 1.2.2)

Als Referenz überprüfen Sie die Seite - http://ph0b.com/android-studio-gradle-and-ndk-integration/, obwohl einige Teile möglicherweise veraltet sind.

+1

Ich habe einfach meine .so-Dateien in 'src/main/jniLibs' kopiert, aber keinen Unterschied. Gleicher Fehler mit Android Studio 1.5.1 (Ich benutze die nicht experimentelle Version für den Aufbau von NDK-Bibliotheken) – ThanosFisherman

+0

Wenn Sie 1.5.1 verwenden, geben Sie bitte auch die build.gradle-Version und die Gradle-Version ein – prabindh

0

Versuchen Sie, Ihre Test-Code mit Java -XshowSettings ausgeführt wird: Eigenschaften Option und sicher, dass Ihre Zielpfad für Systembibliotheken und in der Ausgabe dieses Befehls, Bibliothekspfad Werte machen die gleichen

+0

Was meinen Sie mit "Speicherort kopieren"? und "Bibliothekspfadwerte"? check out mein Stacktrace http://pastebin.com/sNjnbxgP und lassen Sie mich wissen, wo sind die Wege, die Sie sprechen – ThanosFisherman

+0

Entschuldigung Ich habe nur meine Antwort bearbeitet, um klarer zu sein, hier, was mein Verständnis ist Ihre Bibliotheken sind in src/main/libs/xxx ordner auch dein testcode wahrscheinlich in src/test/xxx, meiner meinung nach kannst du Bibliotheken unter dein testverzeichnis kopieren oder einen der im stacktrace gezeigten systempfad – daemonThread

+0

deine annahmen sind richtig. Ich werde versuchen, die nativen Bibliotheken in mein Testverzeichnis zu kopieren und zu sehen, was passiert, danke – ThanosFisherman

2

ich, ob diese löst nicht sicher bin, Ihr Problem oder nicht, aber bis jetzt hat noch niemand etwas über das Strategie-Muster für den Umgang mit Klassen gesagt, die Bibliotheken während ihrer Erstellung vorladen.

Nehmen wir das Beispiel sehen:

Wir wollen Fibonacci-Löser Klasse implementieren.Unter der Annahme, dass wir die Umsetzung in den nativen Code zur Verfügung gestellt und verwaltet die native Bibliothek zu erzeugen, können wir implementieren die folgenden:

public interface Fibonacci { 
    long calculate(int steps); 
} 

Zum einen bieten wir unseren native Implementierung:

public final class FibonacciNative implements Fibonacci { 
    static { 
     System.loadLibrary("myfibonacci"); 
    } 

    public native long calculate(int steps); 
} 

Zweitens bieten wir Java Implementierung für Fibonacci-Löser:

public final class FibonacciJava implements Fibonacci { 

    @Override 
    public long calculate(int steps) { 
     if(steps > 1) { 
      return calculate(steps-2) + calculate(steps-1); 
     } 
     return steps; 
    } 
} 

Drittens haben wir die Löser mit elterlicher Klasse wickeln ihre eigene Implementierung während seiner Instantiierung Wahl :

public class FibonnaciSolver implements Fibonacci { 

    private static final Fibonacci STRATEGY; 

    static { 
     Fibonacci implementation; 
     try { 
     implementation = new FibonnaciNative(); 
     } catch(Throwable e) { 
     implementation = new FibonnaciJava(); 
     } 

     STRATEGY = implementation; 
    } 

    @Override 
    public long calculate(int steps) { 
     return STRATEGY.calculate(steps); 
    } 

} 

So ist das Problem mit der Suche nach Pfad zur Bibliothek mit Strategie. Dieser Fall löst das Problem jedoch nicht, wenn die native Bibliothek wirklich während des Tests einbezogen werden muss. Es löst das Problem nicht, wenn die systemeigene Bibliothek eine Bibliothek von Dritt-Anbieter ist.

Im Grunde geht dies um die native Bibliothek laden Problem durch Spotten der nativen Code für Java-Code.

hoffe, das hilft irgendwie :)

+0

Das ist sehr interessant.Ich werde ein bisschen experimentieren müssen. Aber wird die native Methode immer in meiner Testklasse aufgerufen? Ich möchte kein Mock-Objekt erstellen, das ich nur testen möchte. Ein Wert, der von der nativen Methode berechnet und dann an ArrayList übergeben wird. Also ja, ich brauche native lib, um während des Tests aufgenommen zu werden. Sehen Sie meinen überprüften Code oben auf, wie ich erwarte, dass es geprüft wird. – ThanosFisherman

+0

"Aber wird die native Methode in meiner Testklasse immer aufgerufen?" Ich nahm an, dass Sie in Ihrem Projekt nativen C-Code haben, auf dem die native Bibliothek generiert wird. Wenn die Architektur des Geräts das Laden der Bibliothek zulässt und Sie native Methoden ordnungsgemäß mit Java-Schnittstellen verknüpft haben, dann wird ja die native Methode aufgerufen. Aber die Idee, die ich mir ausgedacht habe, ist eine alternative Java-Implementierung, die genau dasselbe macht, falls die Bibliothek nicht vorgeladen werden kann. –

+0

Yeap all Ihre Annahmen sind richtig. Leider hat es nicht funktioniert. Es fällt tatsächlich zurück auf den Catch-Block und "FibonnaciJava" wird stattdessen getestet :(Ich habe dieses Problem an Google Devs hier weitergeleitet https://code.google.com/p/android/issues/detail?id=199979 Sie sagten mir, dass Schalten Sie das experimentelle Plugin ein und verwenden Sie instrumentation-Tests, damit Sie nicht sehen, was passiert: P – ThanosFisherman

1

Es sind eine Möglichkeit, Bibliothekspfad von Gradle-run VM für lokale Unit-Tests zu konfigurieren, und ich werde es unten beschreiben, aber Spoiler: in meinem expericence, @ThanosFisherman hat Recht: lokale Unit-Tests für Sachen, die das Android NDK benutzen, scheinen im Moment ein Idiot zu sein.

Also, für alle anderen nach einem Weg suchen, um die Last, geteilt (dh .so) Bibliotheken in Unit-Tests mit gradle, hier ist die etwas längere Zusammenfassung:

Das Ziel ist die gemeinsame Bibliothek Lookup-Pfad für die JVM zu setzen Ausführen der Komponententests.

Obwohl viele Leute empfehlen, die Lib-Pfad in java.library.path setzen, fand ich, dass es nicht funktioniert, zumindest nicht auf meiner Linux-Maschine. (Auch, same results in this CodeRanch thread)

Was ist arbeiten obwohl setzt die LD_LIBRARY_PATH os Umgebungsvariable (oder PATH ist das nächstgelegene Synonym in Windows)

Gradle Verwendung:

// module-level build.gradle 
apply plugin: 'com.android.library' // or application 

android { 
    ... 

    testOptions { 
     unitTests { 
      all { 
       // This is where we have access to the properties of gradle's Test class, 
       // look it up if you want to customize more test parameters 

       // next we take our cmake output dir for whatever architecture 
       // you can also put some 3rd party libs here, or override 
       // the implicitly linked stuff (libc, libm and others) 

       def libpath = '' + projectDir + '/build/intermediates/cmake/debug/obj/x86_64/' 
        +':/home/developer/my-project/some-sdk/lib' 

       environment 'LD_LIBRARY_PATH', libpath 
      } 
     } 
    } 
} 

Damit Sie können z ./gradlew :mymodule:testDebugUnitTest und die nativen Bibliotheken werden in den von Ihnen angegebenen Pfaden gesucht.

Mit Android Studio JUnit-Plugin für das JUnit-Plug-Android-Studio, können Sie die VM-Optionen und die Umgebungsvariablen in der Testkonfiguration Einstellungen angeben, so führen Sie einfach einen JUnit-Test (Rechtsklick auf einem Testverfahren oder was auch immer) und dann die Run-Konfiguration bearbeiten: enter image description here enter image description here

Obwohl es wie „Mission erfüllt“ klingt, fand ich, dass, wenn von meinem os /usr/lib gibt mir Version Fehler (wahrscheinlich, weil meine eigene Bibliothek libc.so, libm.so und andere mit kompiliert von cmake mit dem android ndk toolkit gegen es ist eigene Plattform-Bibliotheken). Und mit Hilfe der Plattform Libs vom NDK Paket gebracht, die JVM wih eines SIGSEGV Fehler (aufgrund der Inkompatibilität des NDK Plattform Libs mit der Host-OS-Umgebung)

aktualisiert Wie @AlexCohn wies eindringlich in den Kommentaren aus, ein muss gegen die Host-Umgebung libs bauen, damit dies funktioniert; obwohl Ihre Maschine höchstwahrscheinlich x86_64 ist, werden die x86_64 Binärdateien, die gegen die NDK-Umgebung gebaut werden, nicht tun.

Es könnte natürlich etwas übersehen werden, und ich freue mich über jede Rückmeldung, aber im Moment verwarne ich die ganze Idee zugunsten instrumentierter Tests.

+3

Sie müssen die native Bibliothek für Ihren Host erstellen. Sie können die Android-Version von 'build/intermediate/cmake/debug/obj/x86_64/', auch wenn Ihr PC eine ** x86_64 ** - kompatible CPU verwendet –

+1

@AlexCohn Ja, absolut, da habe ich aufgehört, weil diese ganze Erfahrung Teil einer Aufgabe mit einem war Deadline, und ich hatte keine Lust mehr auf die "Finde heraus, wie man einen Grapple-Cmake-Combo-Build gegen Host-Environment-Bibliotheken erstellt", habe keinen intuitiv guten Pfad zum Fortfahren gesehen. Ich werde gerne nachlesen, wenn Sie etwas zu diesem Thema zu teilen haben. –

+0

Es scheint, dass [Suresh Joshi] (https://Stackoverflow.com/users/992509/sjoshi) das größte Hindernis [hier] gelöst hat (https://Stackoverflow.com/a/47325408/192373). Dennoch müssen einige Vorbehalte gelöst werden. –

Verwandte Themen