我想對於靜態加載 so 庫文件,大家都已經很熟悉了,這裏就不多說了。在 Android 開發中調用動態庫文件(*.so)都是通過 jni 的方式,而靜態加載往往是在 apk 或 jar 包中調用so文件時,都要將對應 so 文件打包進 apk 或 jar 包。
動態加載的優點
靜態加載,不靈活,apk 包有可能大。所以採用動態加載 so 庫文件,有以下幾點好處:
- 靈活,so 文件可以動態加載,不是綁定死的,修改方便,so 庫有問題,我們可以動態更新。
- so 庫文件很大的話,採用動態加載可以減少 apk 的包,變小。
- 其實我們常用第三方 so 庫,單個可能沒問題,如果多個第三方 so 庫文件,同時加載可能會出現衝突,而動態加載就能夠解決這一問題。
注意路徑陷阱
動態加載 so 庫文件,並不是說可以把文件隨便存放到某個 sdcard 文件目錄下,這樣做既不安全,系統也加載不了。
我們在 Android 中加載 so 文件,提供的 API 如下:
1 2 3 4
|
//第一種,pathName 庫文件的絕對路徑 void System.load(String pathName); //第二種,參數爲庫文件名,不包含庫文件的擴展名,必須是在JVM屬性Java.library.path所指向的路徑中,路徑可以通過System.getProperty('java.library.path') 獲得 void loadLibrary(String libname)
|
注意:而這裏加載的文件路徑只能加載兩個目錄下的 so 文件。那就是:
- /system/lib
- 應用程序安裝包的路徑,即:/data/data/packagename/…
所以,so 文件動態加載的文件目錄不能隨便放。這是需要注意的一點。
實現思路
既然使用動態加載的好處和陷阱我們都大致瞭解了,那就可以在實現的時候,注意陷阱就可以了。那基本思路如下:
- 網絡下載 so 文件到指定目錄
- 從指定下載的目錄複製 copy so文件到可動態加載的文件目錄下,比如:/data/data/packagename/…
- 配置 gradle ,指定 cpu 架構
- load 加載
第一步,我們這裏可以簡單忽略,假設我們把 so 文件下載到了 /mnt/sdcard/armeabi 目錄下。
複製目錄到包路徑下
那我們就應該把 /mnt/sdcard/armeabi 目錄下的 so 文件,複製到 應用的包路徑下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
/** * Created by loonggg on 2017/3/29. */ public class SoFile { /** * 加載 so 文件 * @param context * @param fromPath 下載到得sdcard目錄 */ public static void loadSoFile(Context context, String fromPath) { File dir = context.getDir("libs", Context.MODE_PRIVATE); if (!isLoadSoFile(dir)) { copy(fromPath, dir.getAbsolutePath()); } } /** * 判斷 so 文件是否存在 * @param dir * @return */ public static boolean isLoadSoFile(File dir) { File[] currentFiles; currentFiles = dir.listFiles(); boolean hasSoLib = false; if (currentFiles == null) { return false; } for (int i = 0; i < currentFiles.length; i++) { if (currentFiles[i].getName().contains("libwedsa23")) { hasSoLib = true; } } return hasSoLib; } /** * * @param fromFile 指定的下載目錄 * @param toFile 應用的包路徑 * @return */ public 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 { //如果當前項爲文件則進行文件拷貝 if (currentFiles[i].getName().contains(".so")) { int id = copySdcardFile(currentFiles[i].getPath(), toFile + File.separator + currentFiles[i].getName()); } } } 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; } } }
|
配置 grade 指定 cpu 架構
我們都知道,在使用 so 文件的時候,so 庫類型和 CPU 架構類型,要一致,否則是會報錯的。原因很簡單,不同 CPU 架構的設備需要用不同類型 so 庫。CPU架構有如下幾種類型:ARMv5,ARMv7,x86,MIPS,ARMv8,MIPS64 和 x86_64。如果要適配很多手機,就需要在不同的類型下,放置對應的 so 文件。
配置方法如下:
1 2 3 4 5 6 7 8 9 10 11
|
defaultConfig { applicationId "xxxx" minSdkVersion 17 targetSdkVersion 25 versionCode 1 versionName "1.0" ndk { abiFilters "armeabi","armeabi-v7a","x86" } }
|
load 加載 so 文件
複製到可加載使用的包路徑下後,配置完 gradle 之後,就可以使用 load API 調用了。
1 2 3 4 5 6
|
File dir = getApplicationContext().getDir("l ibs", Context.MODE_PRIVATE); File[] currentFiles; currentFiles = dir.listFiles(); for (int i = 0; i < currentFiles.length; i++) { System.load(currentFiles[i].getAbsolutePath()); }
|
這樣,我們就實現了動態加載 so 文件。