Android Studio?targetSdkVersion由30升级到33后出现的错误。
1.大致错误如下:
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.
ext {
android = [
compileSdkVersion : 33,
buildToolsVersion : "30.0.2",
minSdkVersion : 21,
targetSdkVersion : 33,
]
}
可以将项目的targetSdkVersion由33改为30,但是Google Play 现在最低是31,所以不上Google Play是可以直接将targetSdkVersion改为30的。
下面我们继续看怎么处理targetSdkVersion为33的错误。
2.项目出现异常的代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
media();
}
private void media(){
Intent svc = new Intent(this, BackgroundMusicService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startService(svc);
}
MediaSessionCompat ms = new MediaSessionCompat(this, "My__MediaSessionTag");
ms.setActive(true);
ms.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setClass(this, MediaButtonReceiver.class);
ms.setMediaButtonReceiver(PendingIntent.getBroadcast(this, 100, mediaButtonIntent, 0));
PlaybackStateCompat ps = new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE |
PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
PlaybackStateCompat.ACTION_FAST_FORWARD | PlaybackStateCompat.ACTION_REWIND)
.setState(PlaybackStateCompat.STATE_PAUSED, 0 /*PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN*/, 1f)
.build();
ms.setPlaybackState(ps);
ms.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
//应用在前台、后台(活动状态)会触发这里的事件
ms.setCallback(new MediaSessionCompat.Callback() {
@Override
public void onCommand(@NonNull String command, @Nullable Bundle args, @Nullable ResultReceiver cb) {
super.onCommand(command, args, cb);
System.out.println("onCommand");
}
@Override
public void onPlay() {
super.onPlay();
System.out.println("onPlay");
}
@Override
public void onPause() {
super.onPause();
System.out.println("onPause");
}
@Override
public void onStop() {
super.onStop();
System.out.println("onStop");
}
});
}
上图就是代码报错的行数了,即使你添加了如下代码也是无济于事:
PendingIntent pendingIntent;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
pendingIntent = PendingIntent.getActivity(this, 123, intent, PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getActivity(this, 123, intent, PendingIntent.FLAG_ONE_SHOT);
}
上面的代码也是需要添加的,但是问题就是出现在new MediaSessionCompat(getBaseContext(), TAG, null, pendingIntent);的时候,错误提示在使用PendingIntent时,无论是创建或使用的时候flags参数要求必须添加 FLAG_IMMUTABLE或者FLAG_MUTABLE的两个之中其中一个,而且官方还强烈推荐使用FLAG_IMMUTABLE,看下图源码的写法:
是不是豁然开朗了,解决方法如下:
//此方法仅仅适用于 targetSdkVersion 30版本
// ms = new MediaSessionCompat(EarTouchActivity.this, "My__MediaSessionTag");
//targetSdkVersion 33版本使用以下方法
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
getBaseContext(),
0,
mediaButtonIntent,
PendingIntent.FLAG_IMMUTABLE
);
ms = new MediaSessionCompat(getBaseContext(), TAG, null, pendingIntent);
在new MediaSessionCompat之前必须添加上图的属性,这样就正常了。
但是,也不要忘记了下面的正常配置:
3.正常配置,在清单文件中所有activity,receiver,service等, 带有intent-filter标签的地方必须添加exported属性:
android:exported="true"
<activity android:name=".ui.TestActivity"
android:screenOrientation="${SCREENORIENTATION}"
android:configChanges="${CONFIGCHANGES}"
android:exported="true" />
<receiver android:name=".MediaButtonIntentReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
<service
android:name=".BackgroundMusicService"
android:exported="true"
android:enabled="true">
<intent-filter >
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</service>
权限:
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
有需要的可以将以下行添加到 build.gradle
implementation 'androidx.work:work-runtime:2.7.1'
我这次是在做App监听蓝牙耳机或者音媒体设备时遇到的,若你也需要做这个功能可以参考:
android监听耳机按键 - laremehpe - 博客园 (cnblogs.com)
写的很棒,值得借鉴。
4.最后贴出完整代码:
private void media(){
if(svc == null){
svc = new Intent(this, BackgroundMusicService.class);
}
// svc.setComponent(new ComponentName( "link.zhidou.btranslator" ,
// "link.zhidou.btranslator.ble.MediaButtonIntentReceiver"));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startService(svc);
}
if(ms == null){
//此方法仅仅适用于 targetSdkVersion 30版本
// ms = new MediaSessionCompat(EarTouchActivity.this, "My__MediaSessionTag");
//targetSdkVersion 33版本使用以下方法
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
getBaseContext(),
0,
mediaButtonIntent,
PendingIntent.FLAG_IMMUTABLE
);
ms = new MediaSessionCompat(getBaseContext(), TAG, null, pendingIntent);
}
ms.setActive(true);
ms.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setClass(this, MediaButtonIntentReceiver.class);
// ms.setMediaButtonReceiver(PendingIntent.getBroadcast(this, 100, mediaButtonIntent, 0));
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
// pendingIntent = PendingIntent.getBroadcast(EarTouchActivity.this, 0, mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
ms.setMediaButtonReceiver(PendingIntent.getBroadcast(EarTouchActivity.this, 0, mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
} else {
ms.setMediaButtonReceiver(PendingIntent.getBroadcast(EarTouchActivity.this, 0, mediaButtonIntent, PendingIntent.FLAG_ONE_SHOT));
}
PlaybackStateCompat ps = new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE |
PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
PlaybackStateCompat.ACTION_FAST_FORWARD | PlaybackStateCompat.ACTION_REWIND)
.setState(PlaybackStateCompat.STATE_PAUSED, 0 /*PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN*/, 1f)
.build();
ms.setPlaybackState(ps);
ms.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
//应用在前台、后台(活动状态)会触发这里的事件
ms.setCallback(new MediaSessionCompat.Callback() {
@Override
public void onCommand(@NonNull String command, @Nullable Bundle args, @Nullable ResultReceiver cb) {
super.onCommand(command, args, cb);
}
@Override
public void onPlay() {
super.onPlay();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onStop() {
super.onStop();
}
});
}