1、前言
在包大小的占比中,so文件的占比往往是最高的,动辄几兆的大小多一个都会把包大小的指标打爆。
而在各厂商要求对手机CPU ARM架构进行分包适配的情况下,你更需要知道哪些依赖是没有适配v7a/v8a的,这将影响你的APP在应用市场的审核。
所以搞清楚so文件和依赖的关系,它不仅是一个技术指标归因的工具,也是应对厂商分包适配的利器。
2、分析APK
我们一般分析APK是通过Android Studio提供的Analyze APK
工具,可以清晰的看到APK文件的组成部分,比如lib
文件夹下有哪些so文件,但是却无法直观的看出这些so文件属于哪个依赖。
如下图:
3、so文件怎么来的
想要知道so文件是属于哪个依赖,那么得先搞清楚so文件是怎么来的。
如果你分析过apk里面的so文件,你会发现,除了项目中lib文件夹下手动添加的so文件之外,还有一些不知道是哪来的。
要想搞清楚这个,你还得知道我们的依赖是依赖的什么东西。
以okhttp为例:
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
我们通过GAV坐标依赖的实际是square公司发布的jar/aar
文件
所以,除了你自己添加的so文件之外,其余的,都是通过依赖导进来的。
ok,整体思路我们捋一下:
Gradle管理依赖会自动去下载jar/aar,然后我们通过遍历所有依赖,拿到对应的jar/arr,再去获取其中对应的so文件。
听起来并不复杂,实际上也很简单,下面来实战一下。
4、实战
4.1、栗子
以阿里云音视频SDK为例,它一定是包含so文件的,我不信它不用FFmpeg🐶。
添加示例依赖:
//8.全功能:直播推流(含超低延时直播、RTC连麦)+短视频+播放器+美颜特效
implementation 'com.aliyun.aio:AliVCSDK_Premium:6.4.0'
其他配置就不赘述了,可以自行去看文档。
别忘了添加ndk
配置,否则so打不进去。
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'armeabi-v7a', 'armeabi-v8a'
}
}
4.2、遍历
ok,准备工作到位,写个插件遍历所有的依赖文件并打印出来。
Configuration configuration = project.getConfigurations().getByName(applicationVariant.getName() + "CompileClasspath");
configuration.forEach(file -> {
System.out.println(TAG + "file " + file.getName());
String fineName = file.getName();
if (fineName.endsWith(".jar") || fineName.endsWith(".aar")) {
try {
JarFile jarFile = new JarFile(file);
for (Enumeration enums = jarFile.entries(); enums.hasMoreElements(); ) {
JarEntry jarEntry = (JarEntry) enums.nextElement();
System.out.println(TAG + "jarEntry " + jarEntry.getName());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
这里在Project评估完成之后,获取Configuration对象,然后遍历所有的依赖文件,最后通过JarEntry把文件里面所有的子文件打出来。
输出:
可以看到,已经把所有的子文件都打出来了,包括jni/、res/、assets/等等。
4.3、优化
上面的输出还不够直观,我们在过滤一下,只打印so文件,然后优化一下打印的格式。
configuration.forEach(file -> {
String fineName = file.getName();
System.out.println(TAG + "fine name = " + fineName);
if (fineName.endsWith(".jar") || fineName.endsWith(".aar")) {
try {
JarFile jarFile = new JarFile(file);
for (Enumeration enums = jarFile.entries(); enums.hasMoreElements(); ) {
JarEntry jarEntry = (JarEntry) enums.nextElement();
if (jarEntry.getName().endsWith(".so")){
System.out.println(TAG + "----- so name = " + jarEntry.getName());
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
最终效果:
//...
GradleXPlugin >>>>> fine name = AliVCSDK_Premium-6.4.0.aar
GradleXPlugin >>>>> ----- so name = jni/arm64-v8a/libMNN_CL.so
GradleXPlugin >>>>> ----- so name = jni/arm64-v8a/libalivcffmpeg.so
GradleXPlugin >>>>> ----- so name = jni/arm64-v8a/liball_in_one.so
GradleXPlugin >>>>> ----- so name = jni/arm64-v8a/libMNN.so
GradleXPlugin >>>>> ----- so name = jni/armeabi-v7a/libMNN_CL.so
GradleXPlugin >>>>> ----- so name = jni/armeabi-v7a/libalivcffmpeg.so
GradleXPlugin >>>>> ----- so name = jni/armeabi-v7a/liball_in_one.so
GradleXPlugin >>>>> ----- so name = jni/armeabi-v7a/libMNN.so
//...
是不是还挺简单的~
5、最后
如果你不想自己写,这个插件我也发布远端了,按照下面三步走,即可使用。
Step 1. Add the JitPack repository to your build file
repositories {
...
maven { url 'https://jitpack.io' }
}
Step 2. Add the dependency
dependencies {
classpath('com.github.yechaoa.GradleX:plugin:1.2')
}
Step 3. Add the Plugin Id to your build file and configure the gradleX{ } dsl
plugins {
id 'com.yechaoa.plugin.gradleX'
}
gradleX {
printDependencies = false
analysisSo = true
}
ok,以上即是本文介绍内容,学废了吗,写作不易,快来三连~
6、GitHub
https://github.com/yechaoa/GradleX