如何在Android N上动态加载NativeActivity的本机共享库(.so)?

时间:2018-12-14 05:06:32

标签: android c++ linux android-ndk

我想为.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 :(得分:0)

您必须将共享库打包到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文件位于外部SD卡的/Download/,即/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 Emulator Nexus 6P API 23上验证。

答案 1 :(得分:0)

可以通过System.load()

在Android上动态加载SO库。

以下代码成功加载了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

您可能需要检查您的应用权限。