Android从target19升级至target26

本文详细介绍了Android应用在升级至targetSdkVersion 26及以上版本时,如何处理运行时权限申请,包括危险权限的动态获取流程及FileProvider的使用方法,以确保应用在Android 7.0及以上版本中正确处理文件URI。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:因为谷歌要求上架到google play上面的应用targetSdkVersion必须为26以上,国内各大应用渠道也纷纷出台这个要求。而之前目前使用的版本为19,所以进行了一系列的升级变动,本文主要记录升级过程中的一些变动。

运行时权限申请

Android6.0引入了新的权限机制,将系统权限区分为正常权限和危险权限。开发者在使用到危险权限相关的功能时,不仅需要在Manifest文件中配置,还需要在代码中动态获取权限,如果没有确认获取到权限而直接执行相应所需权限的代码,将导致App崩溃。
危险权限:

android.permission.READ_CALENDAR
android.permission.WRITE_CALENDAR
android.permission.CAMERA
android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
android.permission.GET_ACCOUNTS
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
android.permission.RECORD_AUDIO
android.permission.READ_PHONE_STATE
android.permission.READ_PHONE_NUMBERS
android.permission.CALL_PHONE
android.permission.ANSWER_PHONE_CALLS
android.permission.READ_CALL_LOG
android.permission.WRITE_CALL_LOG
android.permission.ADD_VOICEMAIL
android.permission.USE_SIP
android.permission.PROCESS_OUTGOING_CALLS
android.permission.BODY_SENSORS
android.permission.SEND_SMS
android.permission.RECEIVE_SMS
android.permission.READ_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.RECEIVE_MMS
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.SYSTEM_ALERT_WINDOW

我司SDK主要应用到了READ_PHONE_STATE 和 READ_EXTERNAL_STORAGE 这个两个权限,所以在初始化的过程中需要首先判断应用是否授予这两个权限。(申请的时机可以是启动的时候就一次性向用户申请,也可以需要用到的时候才申请,但是必须要注意的是,如果不是强制不授权就退出的话,那么在应用到权限的时候一定要先判断是否拥有该权限)

context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED

通过checkSelfPermission来判断是否授权,如果没有,那就需要向用户申请,接口如下:

ActivityCompat.requestPermissions(this, permissions, 1212);

其中参数permissions是String[]类型,可以同时申请多个权限,系统会弹出对话框依次询问是否授权。授权结束之后会通过onRequestPermissionsResult进行回调,在这个回调中判断是否授权成功。

  @Override
  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);
      if (requestCode == 1212) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
	          if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
	                 if (!shouldShowRequestPermissionRationale(permissions[0])) {
	                      //  引导用户跳转设置界面
	                 } else
	                      finish();
	                 }
              } else {
                Toast.makeText(this, "权限获取成功", Toast.LENGTH_SHORT).show();
              }
           }
      }
   }

在申请权限的页面,如果用户勾选了不在提示而且点击拒绝,那么再次申请的话就会不会弹出申请的弹窗而直接回调失败,那么这时候就只有去设置界面手动开启权限了。
这里建议的解决方案是首次申请权限被拒绝之后,不管用户是否勾选了不在提示,都应该弹出对话框向用户解释申请权限的必要性已经不会造成额外的风险。来引导用户授权,避免多次弹出导致用户勾选不再提示。
通过shouldShowRequestPermissionRationale接口判断用户是否勾选了不再提示选项。如果返回false。则只有引导用户跳转到设置界面去手动开启。

Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, 123);

FileProvider

在Android7.0上面禁止在应用外部公开 file:// URI,如果一项包含文件URI的intent离开应用,则应用出现故障,并出现 FileUriExposedException 异常。
此时需要用到的是FileProvider,我们在应用中会使用更新功能,下载一个新版本的APK之后通知系统安装,需要将下载apk的文件路径携带过去。
Manifest中的配置

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="package.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_file"/>
</provider>

在res目录下面创建provider_file.xml文件,其中external-path对应文件的位置。

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path path="." name="external_storage_root" />
</paths>

安装APK

Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
String type = "application/vnd.android.package-archive";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
	Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
	intent.setDataAndType(uri, type);
	intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
	intent.setDataAndType(Uri.fromFile(file), type);
}
context.startActivity(intent);

FileProvider冲突时候处理
试想一下,如果应用接入了很多SDK,里面都有FileProvider的配置,这时候在Manifest里面就会有多个FileProvider的配置,然而里面指向的xml文件又不一致,这肯定是不行的。
如果产品是一个SDK需要给别的应用接入,这时候可以自己写一个XXFileProvider类来继承FileProvider即可,避免和别人的冲突。

<think>好的,我现在要解决用户在Android Studio中遇到的“Unknown Kotlin JVM target: 21”错误。首先,我需要了解这个错误的根本原因。根据引用[1]和[2],这个错误通常是因为Kotlin插件不支持指定的JVM目标版本,或者Gradle配置中的版本不一致。用户提到他们升级Android Studio到最新版本,可能导致了插件或Gradle版本的变动。 首先,我应该检查项目的build.gradle文件中的Kotlin JVM目标设置。用户可能在模块的build.gradle中设置了jvmTarget为21,但当前的Kotlin插件可能还不支持这个版本。例如,如果Kotlin版本是1.8.20,它可能只支持到JVM 19,所以需要将jvmTarget调整为兼容的版本,比如17或19。 接下来,确保Kotlin插件和Gradle的版本兼容性。用户升级Android Studio,可能自动更新了Gradle或Kotlin插件,导致版本不匹配。需要参考官方文档的兼容性矩阵,如引用[3]提到的,确认当前使用的Kotlin版本支持的JVM目标版本。如果用户手动设置了较高的JVM目标,但插件不支持,就需要降级jvmTarget升级Kotlin插件。 另外,清理项目并重新同步Gradle也是一个有效步骤。有时候缓存可能导致配置未正确应用,执行Clean Project和Rebuild Project可以解决这类问题。如果问题依旧,可能需要检查Gradle包装器版本,确保gradle-wrapper.properties中的distributionUrl指向兼容的Gradle版本,例如7.5或更高,具体取决于Kotlin插件的需求。 还有,检查模块中的其他配置,比如sourceCompatibility和targetCompatibility是否与jvmTarget一致。如果这些设置为Java 21,而Kotlin不支持,同样会导致错误。需要将它们调整到与Kotlin兼容的版本,例如Java 17,并保持jvmTarget同步。 最后,如果用户不确定正确的版本组合,建议参考Kotlin官方文档和Gradle的兼容性矩阵,确保所有相关工具的版本相互匹配。如果用户手动注释掉问题行并让Gradle自动选择默认版本,如引用[3]的解决方案,可能能快速解决问题,但长期还是需要正确配置兼容版本。</think>### 解决 Android Studio 中 "Unknown Kotlin JVM target: 21" 错误的步骤 #### 1. **检查并调整 Kotlin JVM 目标版本** - **问题定位**:此错误通常是由于 Kotlin 插件不支持当前设置的 JVM 目标版本(如 21)导致的[^1][^2]。 - **操作方法**: 1. 打开模块的 `build.gradle` 或 `build.gradle.kts` 文件。 2. 找到 `compileOptions` 或 `kotlinOptions` 中的 `jvmTarget` 配置。 3. 将版本改为当前 Kotlin 插件支持的版本(如 1.8、11、17): ```kotlin // build.gradle.kts 示例 kotlin { jvmToolchain(17) // 或直接设置 jvmTarget = "17" } ``` 4. 同步 Gradle 并重新编译。 #### 2. **更新 Kotlin 和 Gradle 版本** - **兼容性验证**:确保 Kotlin 插件版本与 JVM 目标版本兼容。例如: - Kotlin 1.8.20 支持 JVM 目标版本最高到 19[^3]。 - 若需使用 JVM 21,需升级 Kotlin 到最新版本(如 1.9.0+)。 - **操作步骤**: 1. 修改项目根目录的 `build.gradle` 文件: ```kotlin dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0" // 更新版本 } ``` 2. 更新 Gradle 包装器至兼容版本(如 8.0+): ```properties // gradle-wrapper.properties distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip ``` #### 3. **清理并重新构建项目** - **缓存问题处理**:旧缓存可能导致配置未生效。 - **操作步骤**: 1. 点击菜单栏的 **File > Invalidate Caches / Restart**。 2. 手动删除项目目录下的 `.gradle` 和 `build` 文件夹。 3. 重新同步 Gradle 并构建项目。 #### 4. **检查 Java 兼容性设置** - **配置一致性**:若同时配置了 `sourceCompatibility` 和 `targetCompatibility`,需与 `jvmTarget` 一致。 - **示例调整**: ```kotlin android { compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } } ``` #### 5. **降级作为临时解决方案** - **快速修复**:若无法立即升级 Kotlin,可暂时降低 `jvmTarget` 至兼容版本(如 17): ```kotlin kotlinOptions { jvmTarget = "17" // 注释掉原有配置后手动设置 } ``` --- ### 引用说明 - 错误直接关联到 Kotlin 插件与 JVM 目标版本的兼容性[^2]。 - Gradle 和 Kotlin 的版本兼容性需参考官方文档。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值