Android minSdkVersion大于等于23时,应用找不到so文件

当Android项目的minSdkVersion设置为23及以上时,遇到应用找不到so文件的问题。原因是Android 6.0引入的extractNativeLibs属性,默认值在Gradle 3.6.0以上被重置为false,导致so文件未解压。解决方案是在AndroidManifest.xml中明确设置extractNativeLibs为true,确保so文件在运行时能正确加载。

项目背景

应用依赖的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 项目中正确设置 minSdkVersionAndroid 项目中,`minSdkVersion` 是一个非常重要的属性,用于指定应用可以运行的最低 API 级别。它决定了哪些设备能够安装和运行该应用[^1]。以下是关于如何正确设置 `minSdkVersion` 的详细说明: #### 1. 在 `build.gradle` 文件中设置 `minSdkVersion` 从 Android Studio 的 Gradle 构建系统开始,`minSdkVersion` 通常是在模块级别的 `build.gradle` 文件中定义的。以下是一个典型的配置示例: ```gradle android { compileSdkVersion 33 defaultConfig { applicationId "com.example.myapp" minSdkVersion 21 // 设置最小支持的 API 级别为 21 (Android 5.0) targetSdkVersion 33 // 设置目标 API 级别为 33 versionCode 1 versionName "1.0" } } ``` 在上述代码中,`minSdkVersion` 被设置为 21,这意味着该应用将兼容从 Android 5.0(API Level 21)及更高版本的设备[^2]。 #### 2. 在 `AndroidManifest.xml` 文件中的作用 虽然可以在 `build.gradle` 中设置 `minSdkVersion`,但它的值也会反映到 `AndroidManifest.xml` 文件中。例如: ```xml <uses-sdk android:minSdkVersion="21" /> ``` 需要注意的是,`build.gradle` 中的值会覆盖 `AndroidManifest.xml` 中的值。因此,建议仅在 `build.gradle` 文件中进行设置以避免冲突[^4]。 #### 3. 设置的注意事项 - **不要随意降低 `minSdkVersion`**:如果应用使用了较高 API 级别才支持的功能或库,降低 `minSdkVersion` 可能会导致运行错误[^1]。 - **测试兼容性**:确保应用在指定的最低 API 级别上进行了充分测试,以避免潜在的问题。 - **内置应用与第三方应用的区别**:对于内置应用,通常不需要手动设置 `minSdkVersion` 和 `targetSdkVersion`,因为它们会被编译系统自动处理。而对于基于 SDK 开发的第三方应用,则需要明确设置这些值[^3]。 #### 4. 示例场景 假设开发者希望支持从 Android 6.0(API Level 23)及更高版本的设备,那么可以在 `build.gradle` 文件中这样设置: ```gradle android { compileSdkVersion 33 defaultConfig { minSdkVersion 23 targetSdkVersion 33 } } ``` 这表明应用将不会在低于 Android 6.0 的设备上显示或安装。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值