Как динамически загрузить собственные разделяемые библиотеки (.so) для NativeActivity на Android N? - PullRequest
0 голосов
/ 14 декабря 2018

Я хочу загрузить пользовательский .so динамический для NaticityActivity, но получаю ошибку, когда NativeActivity.onCreate() вызов classLoader.findLibrary("UE4");

это партия NativeActivity.onCreate()

    BaseDexClassLoader classLoader = (BaseDexClassLoader) getClassLoader();
    String path = classLoader.findLibrary(libname);

    if (path == null) {
        throw new IllegalArgumentException("Unable to find native library " + libname +
                                           " using classloader: " + classLoader.toString());
    }

    byte[] nativeSavedState = savedInstanceState != null
            ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;

    mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(),
            getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()),
            getAbsolutePath(getExternalFilesDir(null)),
            Build.VERSION.SDK_INT, getAssets(), nativeSavedState,
            classLoader, classLoader.getLdLibraryPath());

    if (mNativeHandle == 0) {
        throw new UnsatisfiedLinkError(
                "Unable to load native library \"" + path + "\": " + getDlError());
    }
    super.onCreate(savedInstanceState);


    //Hack classLoader nativeLibraryDirectories, add my .so file path


    UnrealHelper.RequestPermission(this);

    UnrealHelper.CopyFile(Environment.getExternalStorageDirectory().getPath() + "/libUE4.so", getFilesDir() + "/libUE4.so");

    String TestA = System.mapLibraryName("gnustl_shared");
    //libUE4.so
    String fileName = System.mapLibraryName("UE4");

    String TmpVal = "";
    BaseDexClassLoader classLoader = (BaseDexClassLoader) getClassLoader();
    try
    {
        Field pathListField = classLoader.getClass().getSuperclass().getDeclaredField("pathList");
        pathListField.setAccessible(true);
        Object pathListVal  = pathListField.get(classLoader);
        Field nativeLibPathField = pathListVal.getClass().getDeclaredField("nativeLibraryDirectories");
        nativeLibPathField.setAccessible(true);
        Object nativeLibPathVal = nativeLibPathField.get(pathListVal);
        ArrayList nativeLibraryDirectories = (ArrayList)nativeLibPathVal;
        //add my .so path to classLoader
        nativeLibraryDirectories.add(getFilesDir());
        //nativeLibPathField.set(pathListVal, nativeLibraryDirectories);
        //pathListField.set(classLoader, pathListVal);

        //ref: https://android.googlesource.com/platform/libcore-snapshot/+/ics-mr1/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
        //ref: https://android.googlesource.com/platform/libcore-snapshot/+/ics-mr1/dalvik/src/main/java/dalvik/system/DexPathList.java
        for (Object directory : nativeLibraryDirectories) {
            File file = new File((File)directory, fileName);
            if (file.exists() && file.isFile() && file.canRead()) {
                //is valid
                TmpVal = file.getPath();
            }
        }
    }
    catch(Exception Exp)
    {
        String ErrorMsg = Exp.toString();
        System.out.print(ErrorMsg);
    }

    //test the path added, but got null
    String path = classLoader.findLibrary("UE4");

enter image description here

Ответы [ 2 ]

0 голосов
/ 15 декабря 2018

Библиотека SO может быть динамически загружена на Android через System.load()

Следующий код успешно загружает библиотеку OpenCV и вызывает функцию библиотеки SO, чтобы получить номер версии

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //Copy "libopencv_java3.so" to App directory and return the full path of the SO file
    String pathOpenCV = FileUtil.loadAssetFile(this, "libopencv_java3.so");
    try {
        System.load(pathOpenCV);
    } catch (Exception e) {
        android.util.Log.e("System.Load", e.toString());
    }

    //All version number returns correctly
    int vMajor = Core.getVersionMajor_0();
    int vMinor = Core.getVersionMinor_0();
    int vRev = Core.getVersionRevision_0();
}

См. Результат:

enter image description here

Возможно, вам потребуется проверить разрешения вашего приложения.

0 голосов
/ 14 декабря 2018

Вы должны упаковать ваши общие библиотеки в ваш apk, чтобы System.loadLibrary("your-lib-name") смог найти его.Обратите внимание, что System.loadLibrary будет принимать только имя библиотеки, а НЕ полный путь.


Для System.load(), я пробовал ниже шаги, это работает хорошо.Вы можете примерить свой проект, чтобы увидеть, как он идет.

Шаг 1:

Убедитесь, что разрешение вашего приложения для внешнего хранилища сконфигурировано в файле manifest.xml, см. Ниже:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Иубедитесь, что вы предоставили эти разрешения.

enter image description here

Шаг 2:

Предположим, загруженный вами файл .soнаходится на /Download/ вашей внешней SD-карты, т.е. /Download/libnative-lib.so.Ниже приведен фрагмент кода, который скопирует libnative-lib.so в /data/data/<your-app-id>/files/libnative-lib2.so и загрузка этого libnative-lib2.so будет выполнена успешно.

    String path_sd_card = Environment.getExternalStorageDirectory().getAbsolutePath();

    FileOutputStream outputStream;
    FileInputStream inputStream;

    // 1. This path works.
    //System.load("/data/data/com.arophix.jniexample/files/libnative-lib.so");
    String filesDir = getFilesDir().getAbsolutePath();

    try {
        inputStream = new FileInputStream(new File(path_sd_card + "/Download/libnative-lib.so"));
        outputStream = new FileOutputStream(new File(filesDir + "/libnative-lib2.so"));//openFileOutput("libnative-lib2.so", Context.MODE_PRIVATE);

        FileChannel inChannel = inputStream.getChannel();
        FileChannel outChannel = outputStream.getChannel();
        inChannel.transferTo(0, inChannel.size(), outChannel);
        inputStream.close();
        outputStream.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    // This path works
    System.load(filesDir + "/libnative-lib2.so"); 

Примечание: проверено на эмуляторе Android Nexus 6P API 23.

...