我们晋级到Targetsdk29有大半年时间了,本年为了满意检查去除蓝牙的准确定位权限,以及满意上架Google Play的要求,需求将Targetsdkversion晋级到31,适配到Android12。这个进程遇到不少坑,这里记载一下,期望能对大家有所帮助。转载请注明来源「申国骏」

由于我们在适配Android 29的时分现已适配了Scoped storage,因而这篇文章里边没有这部分的描绘。

安全组件输出,exported

编译时报错:

android:exported needs to be explicitly specified for element <activity#xxxActivity>. Apps targeting Android 12 and higher are required to specify an explicit value for android:exported when the corresponding component has an intent filter defined. See developer.android.com/guide/topic… for details.

问题描绘:

在target到Android12之后,一切设置了intent filters的activity、services、broadcast receivers都需求设置 android:exported ,否则会导致编译异常。

解决方法:

假如需求被外部其他app拜访的component(例如设置了android.intent.category.LAUNCHER 的页面),那么需求exported=true,其他状况设置为exported=false

  • Activity

    true表明当时Activity需求被外部运用调用,例如桌面和运用需求翻开当时运用首页,false表明当时Activity只能被当时的运用,或许具有相同userID的运用,或许有调用特权的体系components

  • Service

    true表明能够跟外部运用的component进行交互,false表明只有自己运用内的component以及具有相同userID的运用的component能够发动并绑定这个服务。

  • Receiver

    true表明能够非体系的其他运用的播送,false表明只能收到体系的、自己运用的、具有相同userID运用的播送

关于一些aar或许依靠库有里边component的报错,有两个解决方法:1. 尝试晋级对应的依靠库版别,并看看是否现已进行了target android12适配;2. 在主工程中xml拷贝相关component声明,并掩盖exported设置,例如:

android:exported="true"
tools:replace="android:exported"

PendingIntent mutability

运行时报错:

java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx}: java.lang.IllegalArgumentException: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent. Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.

问题描绘:

在target到Android12之后,PendingIntent创立需求指定可变性FLAG_IMMUTABLE 或许 FLAG_MUTABLE

解决方法:

大部分状况下假如不期望创立的PendingIntent被外部运用修正,那么需求设置成PendingIntent.FLAG_IMMUTABLE既可。一些特殊状况能够设置成FLAG_MUTABLE(参阅:developer.android.com/guide/compo…

PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE);

传感器改写频率问题

运行时报错:

java.lang.SecurityException: To use the sampling rate of 0 microseconds, app needs to declare the normal permission HIGH_SAMPLING_RATE_SENSORS. at android.hardware.SystemSensorManager$BaseEventQueue.enableSensor(SystemSensorManager.java:884) at android.hardware.SystemSensorManager$BaseEventQueue.addSensor(SystemSensorManager.java:802) at android.hardware.SystemSensorManager.registerListenerImpl(SystemSensorManager.java:272) at android.hardware.SensorManager.registerListener(SensorManager.java:835) at android.hardware.SensorManager.registerListener(SensorManager.java:742)

问题描绘:

当运用SensorManager时,假如监听的频率太快,例如sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);,且没有改界说permission HIGH_SAMPLING_RATE_SENSORS权限的话会有这个溃散。

解决方法:

大部分状况下我们并不需求太快的监听频率,能够设置成SensorManager.SENSOR_DELAY_UI。在某些确实需求快速频率监听的话,需求加上HIGH_SAMPLING_RATE_SENSORS权限

ijkplayer

运行时溃散:

运行时的native溃散

问题描绘:

在target到Android11并且在64位的安卓体系版别11及以上的手机,运用ijkplayer会产生溃散。这里的原因是Android11关于64位的处理器中,每个指针的第一个字节将被用作符号位,用于ARM的内存符号扩展(MTE)支撑。在释放内存的时分假如修正这个符号位程序就会溃散。

那么ijkplayer在哪里会导致第一个字节被修正了呢,检查这个issues github.com/bilibili/ij… 以及提交记载 github.com/bilibili/ij… 能够看出,首要的原因是之前将指针转化成了int64_t类型导致了精度丢掉,修正的地方是将指针转成String或许无符号整形,防止精度丢掉导致的首位字节丢掉。

TargetsdkVersion-升级31(Android12)适配

例如,在上面的图中,拜访0x8000的内存是可行的,由于用于进行拜访的指针具有与被拜访的内存相同的标签(用色彩表明)。可是,对0x9000的拜访将会失败,由于指针对内存有不同的符号。

解决方法:

解决方法有两个,一个是拉一下ijkplayer最新的代码从头build一个依靠库更新一下,由于ijkplayer现已修正了这个错误。第二个方法是经过设置<application android:allowNativeHeapPointerTagging="false">暂时禁用Pointer Tagging功用。

TelephonyManager.getNetworkType

运行时溃散:

************* Crash INFO AT 04/01/2022 10:16 *************java.lang.SecurityException: getDataNetworkTypeForSubscriber android.os.Parcel.createExceptionOrNull(Parcel.java:2389) android.os.Parcel.createException(Parcel.java:2373) android.os.Parcel.readException(Parcel.java:2356) android.os.Parcel.readException(Parcel.java:2298) com.android.internal.telephony.ITelephony$Stub$Proxy.getNetworkTypeForSubscriber(ITelephony.java:8762) android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:3024) android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:2988)

问题描绘:

我们运用到的一个一键登录的库调用的TelephonyManager.getNetworkType被符号为deprecated,需求改成运用 getDataNetworkType ,并且需求加上权限READ_PHONE_STATE 或许 READ_BASIC_PHONE_STAT

解决方法:

晋级一键登录的库,并且加上对应权限

webview拜访文件

运行时问题:

加载file://data目录底下数据时webview报错: 网页无法加载,net:ERR_ACCESS_DENIED

问题描绘:

在target到Android11及以上的时分,默认setAllowFileAccess从true改成了false,无法拜访到context.getDir()里边的文件,参阅:developer.android.com/reference/a…

解决方法:

手动调用一下webSettings.setAllowFileAccess(true)

Package可见性

运行时问题:

当运用[queryIntentActivities()](developer.android.com/reference/a…, int)), [getPackageInfo()](developer.android.com/reference/a…, int))或许 getInstalledApplications()查询是其他运用信息的话会查不到

问题描绘:

当运用target到Android11之后,Package可见性受到了限制,查询其他运用信息需求加上QUERY_ALL_PACKAGES权限或许运用queries方法获取。

解决方法:

  1. 在AndroidManifest.xml中加入权限<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />,这个需求谨慎运用,由于运用市场上线检查可能会需求供给运用的必要性阐明,例如Google Play政策:support.google.com/googleplay/…

  2. 在AndroidMainifest.xml中界说需求拜访的运用信息,例如

    • 需求拜访某个运用信息,直接指定运用包名

      <queries>
        <package android:name="com.example.store" />
      </queries>
      
    • 需求拜访具有某些intent的外部组件,指定需求拜访的intent

      <queries>
        <intent>
          <action android:name="android.intent.action.SEND" />
          <data android:mimeType="image/jpeg" />
        </intent>
      </queries>
      
    • 需求拜访某些外部content provider,指定authoritites

      <queries>
        <provider android:authorities="com.example.settings.files" />
      </queries>
      

微博SDK

运行时问题:

微博SDK更新到最新版支撑适配安卓11,遇到一个初始化的报错please init sdk before use it. Wb.install()

问题描绘:

在微博进行登录授权的时分,需求获取授权信息,不过获取授权信息的时分,有一个断语判断失败了。需求在初始化之后等待一段时间。

解决方法:

经过循环等待的方法等待初始化断语经过再进行其他SDK操作:

public static void waitForWeiboSDKValid() {
    // 微博sdk初始化需求等待一下
    // https://github.com/sinaweibosdk/weibo_android_sdk/issues/608
    // https://xie.infoq.cn/article/974795351e87627681cc353b5
    int retryCount = 0;
    while (retryCount <= 10) {
        try {
            a.b();
            break;
        } catch (Exception ignore) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException ignored) {
            }
            retryCount++;
        }
    }
}
private void installWbSdk() {
   WBAPIFactory.createWBAPI(getApplicationContext());
   mWBAPI.registerApp(getApplicationContext(), authInfo);
   waitForWeiboSDKValid();
}

后台发动前台服务

运行时溃散:

Caused by: android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service XXXXService 16 at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54) 17 at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50) 18 at android.os.Parcel.readParcelable(Parcel.java:3345) 19 at android.os.Parcel.createExceptionOrNull(Parcel.java:2432) 20 at android.os.Parcel.createException(Parcel.java:2421) 21 at android.os.Parcel.readException(Parcel.java:2404) 22 at android.os.Parcel.readException(Parcel.java:2346) 23 at android.app.IActivityManagerStubStubProxy.setServiceForeground(IActivityManager.java:8040) 24 at android.app.Service.startForeground(Service.java:733)

问题描绘:

运用在target到Android12之后,假如运用在后台启用前台服务,那么就会报ForegroundServiceStartNotAllowedException

解决方法:

  1. 运用WorkManager来处理后台任务
  2. 防止在后台发动前台服务

蓝牙权限

运行溃散:

Caused by: java.lang.SecurityException: Need android.permission.BLUETOOTH_CONNECT permission for android.content.AttributionSource@db46d647: enable 37 at android.os.Parcel.createExceptionOrNull(Parcel.java:2425) 38 at android.os.Parcel.createException(Parcel.java:2409) 39 at android.os.Parcel.readException(Parcel.java:2392) 40 at android.os.Parcel.readException(Parcel.java:2334) 41 at android.bluetooth.IBluetoothManagerStubStubProxy.enable(IBluetoothManager.java:611) 42 at android.bluetooth.BluetoothAdapter.enable(BluetoothAdapter.java:1217)

问题描绘:

在target到Android12之后,查找蓝牙设备需求增加 BLUETOOTH_SCAN 权限,与匹配的蓝牙设备传输数据需求获取BLUETOOTH_CONNECT 权限

解决方法:

在查找和匹配蓝牙设备之前,先动态申请 BLUETOOTH_SCAN 权限以及BLUETOOTH_CONNECT 权限。

其他

检查依靠的SDK中是否有新的版别,并进行更新,由于安全组件输出Exported以及包可见性的问题关于大多数SDK都可能会存在,所以最好都检查一下,例如华为小米OV相关的产商推送SDK,以及微信QQ微博等登录和共享的SDK。

参阅

  1. Bluetooth permissions
  2. Target API level requirements for Play Console
  3. Sensor Rate-Limiting
  4. Behavior changes: Apps targeting Android 12
  5. Behavior changes: Apps targeting Android 11
  6. vivo Android 12运用适配指南
  7. vivo Android 11运用适配指南
  8. oppo Android 12 运用兼容性适配辅导
  9. oppo Android 11 运用兼容性适配辅导
  10. 小米 Android 12运用适配指南
  11. Tagged Pointers
  12. Memory Tagging Extension: Enhancing memory safety through architecture
  13. Package visibility filtering on Android
  14. Declaring package visibility needs