1. 程式人生 > >Android動態載入—so檔案

Android動態載入—so檔案

簡介

前幾天做一個視訊播放的功能,用到了bilibili開源ijkplayer播放器的(整合ijkplayer),功能確實強大,但就是用到的ffmpeg解碼庫太大,不得已只能只能將so檔案拿出來,通過動態的方式來載入。

什麼是動態載入?

就是講so檔案不打包進apk,在安裝完應用開啟app的時候通過後臺下載so庫,將下載下來的so檔案再寫入到app裡面。
首先我們要知道,Android載入so檔案的方式有兩種:

  • System.loadLibrary
  • System.load

它們都可以用來裝載庫檔案,但是System.load引數必須為庫檔案的絕對路徑,可以是任意路徑;System.loadLibrary引數為庫檔名,不包含庫檔案的副檔名,必須是在JVM屬性java.library.path所指向的路徑中,路徑可以通過System.getProperty('java.library.path')

獲得。所有動態載入的時候我們不能用System.loadLibrary,只能用System.load來載入。

介紹

  1. 下載so檔案
  2. 安裝
  3. load載入

    其中下載沒什麼說的,不過推薦Netroid,下載一般下載到sdcard,前面說過System.load引數為絕對路徑,也就是說你可以直接載入sdcard上面的so檔案,但是在sdcard上可能會被使用者刪除或者修改,所以我建議將so檔案copy到我們app的目錄(data/data/包名/app_lib)下面

   public static void loadSoFile(Context context) {
        File dir = context.getDir("libs"
, Context.MODE_PRIVATE); if (!isLoadSoFile(dir)) { copy(formPath, dir.getAbsolutePath()); } } public static boolean isLoadSoFile(File dir) { File[] currentFiles; currentFiles = dir.listFiles(); boolean hasJkffmpeg = false; if (currentFiles == null
) { return false; } for (int i = 0; i < currentFiles.length; i++) { if (currentFiles[i].getName().contains(DuduUtil.SoFile.IJKFFMPEG)) { hasJkffmpeg = true; } } return hasJkffmpeg; }

先判斷app_lib下有沒有我們需要載入的so檔案,如果沒有的話複製到指定目錄,其中formPath是下載到sdcard上so檔案的路徑

ublic static int copy(String fromFile, String toFile) {
        //要複製的檔案目錄
        File[] currentFiles;
        File root = new File(fromFile);
        //如同判斷SD卡是否存在或者檔案是否存在
        //如果不存在則 return出去
        if (!root.exists()) {
            return -1;
        }
        //如果存在則獲取當前目錄下的全部檔案 填充陣列
        currentFiles = root.listFiles();

        //目標目錄
        File targetDir = new File(toFile);
        //建立目錄
        if (!targetDir.exists()) {
            targetDir.mkdirs();
        }
        //遍歷要複製該目錄下的全部檔案
        for (int i = 0; i < currentFiles.length; i++) {
            if (currentFiles[i].isDirectory()) {
                //如果當前項為子目錄 進行遞迴
                copy(currentFiles[i].getPath() + "/", toFile + currentFiles[i].getName() + "/");

            } else {
                //如果當前項為檔案則進行檔案拷貝
                Log.e(TAG, "path:" + currentFiles[i].getPath());
                Log.e(TAG, "name:" + currentFiles[i].getName());
                if (currentFiles[i].getName().contains(".so")) {
                    int id = copySdcardFile(currentFiles[i].getPath(), toFile + File.separator + currentFiles[i].getName());
                    Log.e(TAG, "id:" + id);
                }
            }
        }
        return 0;
    }


    //檔案拷貝
    //要複製的目錄下的所有非子目錄(資料夾)檔案拷貝
    public static int copySdcardFile(String fromFile, String toFile) {

        try {
            FileInputStream fosfrom = new FileInputStream(fromFile);
            FileOutputStream fosto = new FileOutputStream(toFile);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = fosfrom.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            // 從記憶體到寫入到具體檔案
            fosto.write(baos.toByteArray());
            // 關閉檔案流
            baos.close();
            fosto.close();
            fosfrom.close();
            return 0;

        } catch (Exception ex) {
            return -1;
        }
    }

最後一步,load

File dir = context.getDir("libs", Context.MODE_PRIVATE);
File[] currentFiles;
currentFiles = dir.listFiles();
for (int i = 0; i < currentFiles.length; i++) {
     Log.e(TAG, "#:" + currentFiles[i].getAbsolutePath());
     // System.load(currentFiles[i].getAbsolutePath());

 }

問題

  1. java.lang.UnsatisfiedLinkError: dlopen failed: “XXXX.so” is 32-bit instead of 64-bit
    大體原因是手機cpu為64位,並且在app中你配置了載入64位(arm64-v8a),所有系統會去載入64位路徑下的so庫,但是你load的so庫卻是32位,解決方法有兩種:1、將32為庫換成64位,2、只加載32位庫,在build.gradle中配置,armeabi,armeabi-v7a,x86位32位,arm64-v8a,x86_64是64位。
defaultConfig {
        applicationId "xxxx"
        minSdkVersion 10
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"

        ndk {
            abiFilters "armeabi","armeabi-v7a","x86"
        }
    }

2.java.lang.UnsatisfiedLinkError: Couldn’t load ijkffmpeg from loader dalvik.system.DexClassLoader[DexPathList[]]: findLibrary returned null

載入時注意載入so檔案的順序,遇到的問題是load ijkffmpeg.so檔案時需要先load另外一個so檔案,但是我先load了ffmpeg,所以報了 couldn’t load異常