Android 中so的动态加载实现

正常情况下我们都是通过System.loadLibrary()的方式来加载位于libs下的so。最近做的一个项目由于对于aar体积有严格的大小要求,所以将so做成了动态加载的策略

获取手机cpu架构的命令
adb shell getprop ro.product.cpu.abi 

1.加载远程so

我们需要将so下载到本地进行加载,通过System.load()方法将本地so路径作为参数传入!

2. 代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/**
* @param context
* @param so 所在的路径
*/
@SuppressLint("UnsafeDynamicallyLoadedCode")
public static void loadSoFile(Context context, String originFilePath) {

File dir = context.getDir("libs", Context.MODE_PRIVATE);
File file = new File(originFilePath);
String toFilePath = dir.getAbsolutePath() + File.separator + file.getName();

copySdcardFile(originFilePath, toFilePath);

System.load(toFilePath);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

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;
}
}

一定要将so拷贝到app内部目录,否则无权限执行so

3. 平台适配问题

由于android平台存在多种类型的cpu架构,所以需要我们根据不同的cpu加载对应的so库,比如 x86,armabi等

代码获取当前设备的cpu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static String getFirstSupportedAbi() {
String abi1 = "";
if (Build.VERSION.SDK_INT >= 21) {
String[] abis = Build.SUPPORTED_ABIS;
if (abis != null && abis.length > 0) {
String abistr = "";
//第一个是原生支持的,后面的是兼容模式.虽然是兼容,但手动加载时很多并不兼容.
abi1 = abis[0];
for (String abi : abis) {
abistr = abistr + abi + ",";
}
JdGame.print(abistr);
}
} else {
if (!TextUtils.isEmpty(Build.CPU_ABI)) {
abi1 = Build.CPU_ABI;
} else if (!TextUtils.isEmpty(Build.CPU_ABI2)) {
abi1 = Build.CPU_ABI2;
}
}
return abi1;
}

然后根据获取的 cpu值,去下载对应的 so 再进行加载