适配API33后GCM旧应用在Android31+崩溃:PendingIntent标志问题
问题详情
将仍在使用无官方支持GCM的遗留应用Target SDK升级至API 33后,在Android API 31及以上设备运行时出现崩溃,抛出异常:
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 it
at android.app.PendingIntent.checkPendingIntent(PendingIntent.java:428)
at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:731)
at android.app.PendingIntent.getBroadcast(PendingIntent.java:718)
at com.google.android.gms.iid.zzl.zzi(Unknown Source:18)
at com.google.android.gms.iid.zzl.zzb(Unknown Source:224)
at com.google.android.gms.iid.zzl.zza(Unknown Source:0)
at com.google.android.gms.iid.InstanceID.zzb(Unknown Source:60)
at com.google.android.gms.iid.InstanceID.getToken(Unknown Source:120)
经调试确认,崩溃触发在以下代码行:
regid = instanceID.getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
当前限制:无法投入大量资源将GCM升级至Firebase。
解决方案
优先方案:升级GCM依赖版本
将项目中com.google.android.gms:play-services-iid依赖升级至17.0.0及以上版本,该版本已适配Android 12+对PendingIntent标志位的强制要求,无需修改业务代码即可解决崩溃问题。
应急方案:通过反射修改PendingIntent标志
若无法升级依赖,可通过反射手段为GCM内部创建的PendingIntent添加FLAG_IMMUTABLE标志(推荐使用该标志,仅当业务需要修改PendingIntent内容时才用FLAG_MUTABLE),示例代码如下:
try { // 反射获取PendingIntent的getBroadcast方法 Method getBroadcastMethod = PendingIntent.class.getMethod( "getBroadcast", Context.class, int.class, Intent.class, int.class ); // 调用时注入FLAG_IMMUTABLE标志 Intent gcmIntent = new Intent("com.google.android.gms.iid.InstanceID"); PendingIntent modifiedIntent = (PendingIntent) getBroadcastMethod.invoke( null, getApplicationContext(), 0, gcmIntent, PendingIntent.FLAG_IMMUTABLE ); // 将修改后的PendingIntent替换到GCM内部逻辑(需结合具体反射定位内部字段) } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); }
核心原因说明
Android 12(API31)及以上系统强制要求所有PendingIntent必须指定FLAG_IMMUTABLE或FLAG_MUTABLE标志,而旧版本GCM依赖未处理该规则,导致创建PendingIntent时触发参数校验异常。
内容的提问来源于stack exchange,提问作者Varun

