1. 程式人生 > >Android 靜態和動態的呼叫so庫(JNI)

Android 靜態和動態的呼叫so庫(JNI)

優點

為什麼我們需要動態載入?因為靜態載入中CPU的資料夾我們可能需要相容的話需要放在不同arm資料夾下,那麼就會導致apk 包體過大,還有安卓Android SDK系統版本導致的差異,所以我們採用動態載入 so 庫檔案的話最主要的好處就是可以減小包體,其次,我們去修改so庫時也比較方便,因為是動態的,我們可以動態的同步更新。

還有另外一個原因就是,我們常常會用到第三方的 so 庫,如果單個庫可能沒問題,如果多個第三方 so 庫檔案,同時載入可能會出現衝突,比如說騰訊的YSDK和BUGLY,而動態載入就能夠很好的解決這個問題。

實現步驟

基本步驟如下:

  • 第一步,從網路下載 so庫檔案到指定目錄。
  • 然後,從指定的目錄中 複製 copy so庫檔案到自身包名目錄下,比如:/data/data/packagename/…
    ,當然我們需要配置 gradle ,這一點很多人會不記得操作,記得需要指定 cpu 的架構和可動態載入的檔案。如(/data/data/immqy/miqiyun/...)。
  • 最後load 載入。

    那我們如何操作?
    第一步:網路下載so庫,其實就是下載檔案,比較簡單,這裡不再過多的闡述,可檢視檔案下載的《Android 非同步檔案下載》
    第二步:我們假設指定目錄為SdCard中的IMMQY資料夾,那麼路徑為:/mnt/sdcard/IMMQY/,把此路徑複製到包下的可執行路徑,程式碼如下:

/**
 *  
 * Author:Karl-Dujinyang
 * 
 * 2016/11/10
 */
public class SoFile {

   /**
     * 載入 so 檔案
     * @param context
     * @param fromPath 下載到得sdcard目錄
     */
    public static void loadSoFile(Context context, String fromPath) {
        File dirs = context.getDir("libs", Context.MODE_PRIVATE);
        if
(!isLoadSoFiles(dirs)) { copyFile(fromPath, dir.getAbsolutePath()); } } /** * 判斷immqy so 檔案是否存在 * @param dir * @param name "libimmqy" so庫 * @return boolean */ public static boolean isLoadSoFiles(String name,File dirs) { boolean getSoLib = false; File[] currentFiles; currentFiles = dirs.listFiles(); if (currentFiles == null) { return false; } for (int i = 0; i < currentFiles.length; i++) { if (currentFiles[i].getName().contains(name)) { getSoLib = true; } } return getSoLib; } /** * 要複製的目錄下的所有非子目錄(資料夾)檔案拷貝 * @param fromFile 指定的下載目錄 * @param toFile 應用的包路徑 * @return int */ public static int copySdcardFile(String fromFiles, String toFile) { try { FileInputStream fileInput = new FileInputStream(fromFiles); FileOutputStream fileOutput = new FileOutputStream(toFile); ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); byte[] buffer = new byte[1024*1]; int len = -1; while ((len = fileInput.read(buffer)) != -1) { byteOut.write(buffer, 0, len); } // 從記憶體到寫入到具體檔案 fileOutput.write(baos.toByteArray()); // 關閉檔案流 byteOut.close(); fileOutput.close(); fileInput.close(); return 0; } catch (Exception ex) { return -1; } } /** * * @param fromFile 指定的下載目錄 * @param toFile 應用的包路徑 * @return int */ public static int copyFile(String fromFiles, String toFile) { //要複製的檔案目錄 File[] currentFiles; File root = new File(fromFiles); //如同判斷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()) { //如果當前項為子目錄 進行遞迴 copyFile(currentFiles[i].getPath() + "/", toFile + currentFiles[i].getName() + "/"); } else { //如果當前項為檔案則進行檔案拷貝 if (currentFiles[i].getName().contains(".so")) { int id = copySdcardFile(currentFiles[i].getPath(), toFile + File.separator + currentFiles[i].getName()); } } } return 0; } }

程式碼寫入到目錄後,我們就需要配置 grade 了。
配置方法如下:

defaultConfig {
        applicationId "immqy"
        minSdkVersion 16
        targetSdkVersion 26
        versionCode 2
        versionName "2.2" 
        ndk {
            abiFilters "armeabi","armeabi-v7a","x86"
        }
    }

提示:CPU架構有如下幾種型別:ARMv5,ARMv7,ARMV7A,x86,MIPS,ARMv8,MIPS64 和 x86_64,so 庫型別和 CPU 架構型別要一致,否則是會報錯的。

配置完成後,就剩下最後一步了。
第三步:load程式碼載入進行呼叫,很簡單,程式碼如下:

File dir = getApplicationContext().getDir("libs", Context.MODE_PRIVATE); 
File[] currFiles = null; 
currFiles = dir.listFiles(); 
for (int i = 0; i < currFiles.length; i++) {
     System.load(currFiles[i].getAbsolutePath()); 
} 

需要注意的是,getApplicationContext引用不能為空,所以最好還是先判斷下上下文物件是否為空。

這樣就可以使用 load API 呼叫動態載入 so 檔案了。

踩坑

有人會想,檔案下載過來是否安全,其實是否定的,大家也可以把檔案加密,或者把檔案路徑丟深一點。
我們在 Android 中載入 so 檔案,提供的 API 如下:

  1. 第一種,pathName 庫檔案的絕對路徑 void System.load(String pathName)
  2. 第二種,引數為庫檔名,不包含庫檔案的副檔名,必須是在JVM屬性 Java.library.path 所指向的路徑中,路徑可以通過 System.getProperty('java.library.path'), 獲得 void loadLibrary(String libname)

注意:而這裡載入的檔案路徑只能載入兩個目錄下的 so 檔案,那就是:/system/lib和/data/data/packagename/…(應用程式安裝包下的路徑)
所以,so 檔案動態載入的檔案目錄不能隨便放。這是需要注意的一點。

|| 版權宣告:本文為博主杜錦陽原創文章,轉載請註明出處。