Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE

发布时间:2024年01月11日

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();
            }
        });
    }

文章来源:https://blog.csdn.net/shenggaofei/article/details/135517945
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。