项目背景
应用依赖的jar包存在动态加载Dex,而且Dex依赖so文件。
依赖的so路径为context.getApplicationInfo().nativeLibraryDir
动态加载部分代码如下:
String libraryPath = context.getApplicationInfo().nativeLibraryDir;
DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedDirectoryPath, libraryPath, context.getClassLoader());
Grale配置:
Android Gradle Plugin Version: 4.0.1
Gradle Version:6.1.1
项目build.gradle
注意,这里设置
minSdkVersion 22
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.jc.demoset4"
minSdkVersion 22
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
...
}
找不到so问题
把
minSdkVersion设置成23,安装应用报找不到so
项目build.gradle如下:
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.jc.demoset4"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
...
}
安装运行发现应用崩溃,日志如下:
java.lang.UnsatisfiedLinkError: dalvik.system.DexClassLoader[DexPathList[[dex file
"/data/user/0/com.jc.demoset4/files/nepcore.dex"],nativeLibraryDirectories=
[/data/app/com.jc.demoset4-2/lib/arm, /data/resource/lib, /vendor/lib, /system/lib]]]
couldn't find "libDeviceConfig.so"
at java.lang.Runtime.loadLibrary0(Runtime.java:984)
at java.lang.System.loadLibrary(System.java:1562)
at com.pax.dal.impl.utils.DeviceConfig.<clinit>(SourceFile:22)
at com.pax.dal.impl.utils.ServiceUtils.getPedMode(SourceFile:127)
at com.pax.dal.impl.Dal.getPed(SourceFile:130)
at com.jc.demoset4.PedProxy.<init>(PedProxy.java:43)
at com.jc.demoset4.MainActivity.setClick(MainActivity.java:24)
at com.jc.demoset4.MainActivity.onCreate(MainActivity.java:18)
at android.app.Activity.performCreate(Activity.java:6723)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2619)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2727)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1478)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6121)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
解决方案
在应用AndroidManifest文件application标签增加android:extractNativeLibs="true",即可解决问题。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.DemoSet4"
android:extractNativeLibs="true">
......
</application>
问题分析
在Android API 23即 6.0增加extractNativeLibs标签,在android.content.pm.PackageParser部分代码可以看到
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
...
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
true)) {
ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
}
...
}
如果没有在AndroidManifest设置extractNativeLibs标签,默认是true,系统安装应用时,会将应用的nativelibs文件解压到Application.nativeLibraryDir目录下,一般为/data/app/<packagename>/lib目录。
如果设置了extractNativeLibs标签为false,系统则不会解压nativelibs文件。
我们查看APK中AndroidManifest文件:
<application
android:theme="@ref/0x7f0f0196"
android:label="@ref/0x7f0e001b"
android:icon="@ref/0x7f0c0000"
android:debuggable="true"
android:testOnly="true"
android:allowBackup="true"
android:supportsRtl="true"
android:extractNativeLibs="false"
android:roundIcon="@ref/0x7f0c0001"
android:appComponentFactory="androidx.core.app.CoreComponentFactory">
...
</application>
发现即使没有设置extractNativeLibs标签,AS打包的时候,也设置extractNativeLibs = "false",查看Android开发者文档找到了原因。
android:extractNativeLibs
Whether or not the package installer extracts native libraries from the APK to the filesystem. If set to “false”, then your native libraries must be page aligned and stored uncompressed in the APK. Although your APK might be larger, your application should load faster because the libraries are directly loaded from the APK at runtime. On the other hand, if set to “true”, native libraries in the APK can be compressed. During installation, the installer decompresses the libraries, and the linker loads the decompressed libraries at runtime; in this case, the APK would be smaller, but installation time might be slightly longer.
The default value is “true” if extractNativeLibs is not configured in AndroidManifest.xml. However, when building your app using Android Gradle plugin 3.6.0 or higher, this property is reset to “false” if it is NOT configured in AndroidManifest.xml; so if your native libraries in the APK are compressed, you must explicitly set it to “true” in AndroidManifest.xml.
大概意识是:如果extractNativeLibs为false时,应用的so文件不解压而且页面对齐;如果设置为true时,系统安装服务会把so文件解压到系统目录。extractNativeLibs默认值是true,但是在使用Android Gradle plugin 3.6.0 及以上,没有配置extractNativeLibs时,会把此属性重置为false。
由于要动态加载Dex,Dex依赖的so没有解压到/data/app/<packagename>/lib目录下,在加载Dex是指定的libraryPath 没有so文件,所以报找不到so的异常。
当Android项目的minSdkVersion设置为23及以上时,遇到应用找不到so文件的问题。原因是Android 6.0引入的extractNativeLibs属性,默认值在Gradle 3.6.0以上被重置为false,导致so文件未解压。解决方案是在AndroidManifest.xml中明确设置extractNativeLibs为true,确保so文件在运行时能正确加载。
10万+

被折叠的 条评论
为什么被折叠?



