如何解决“Targeting S+ (版本31及以上)要求FLAG_IMMUTABLE或FLAG_MUTABLE之一”的问题?

19

我一直在尝试解决这个问题,但是一直没有成功。这是一个Udacity课程,在课程中我正在尝试使用待定意图创建通知,但是待定意图是可变的,我已经尝试了以下方法。

ViewModel

private val REQUEST_CODE = 0
private val TRIGGER_TIME = "TRIGGER_AT"

private val minute: Long = 60_000L
private val second: Long = 1_000L

private val timerLengthOptions: IntArray
private val notifyPendingIntent: PendingIntent

private val alarmManager = app.getSystemService(Context.ALARM_SERVICE) as AlarmManager
private var prefs =
    app.getSharedPreferences("com.shivaConsulting.androidProjects.kotlinnotificatons", Context.MODE_PRIVATE)
private val notifyIntent = Intent(app, AlarmReciever::class.java)

private val _timeSelection = MutableLiveData<Int>()
val timeSelection: LiveData<Int>
    get() = _timeSelection

private val _elapsedTime = MutableLiveData<Long>()
val elapsedTime: LiveData<Long>
    get() = _elapsedTime

private var _alarmOn = MutableLiveData<Boolean>()
val isAlarmOn: LiveData<Boolean>
    get() = _alarmOn


private lateinit var timer: CountDownTimer


init {
    _alarmOn.value = PendingIntent.getBroadcast(
        getApplication(),
        REQUEST_CODE,
        notifyIntent,
        PendingIntent.FLAG_NO_CREATE
    ) != null

    notifyPendingIntent = PendingIntent.getBroadcast(
        getApplication(),
        REQUEST_CODE,
        notifyIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )

    timerLengthOptions = app.resources.getIntArray(R.array.minutes_array)

    //If alarm is not null, resume the timer back for this alarm
    if (_alarmOn.value!!) {
        createTimer()
    }

}

/**
 * Turns on or off the alarm
 *
 * @param isChecked, alarm status to be set.
 */
fun setAlarm(isChecked: Boolean) {
    when (isChecked) {
        true -> timeSelection.value?.let { startTimer(it) }
        false -> cancelNotification()
    }
}

/**
 * Sets the desired interval for the alarm
 *
 * @param timerLengthSelection, interval timerLengthSelection value.
 */
fun setTimeSelected(timerLengthSelection: Int) {
    _timeSelection.value = timerLengthSelection
}

/**
 * Creates a new alarm, notification and timer
 */
private fun startTimer(timerLengthSelection: Int) {
    _alarmOn.value?.let {
        if (!it) {
            _alarmOn.value = true
            val selectedInterval = when (timerLengthSelection) {
                0 -> second * 10 //For testing only
                else ->timerLengthOptions[timerLengthSelection] * minute
            }
            val triggerTime = SystemClock.elapsedRealtime() + selectedInterval

            // TODO: Step 1.5 get an instance of NotificationManager and call sendNotification

            // TODO: Step 1.15 call cancel notification

            AlarmManagerCompat.setExactAndAllowWhileIdle(
                alarmManager,
                AlarmManager.ELAPSED_REALTIME_WAKEUP,
                triggerTime,
                notifyPendingIntent
            )

            viewModelScope.launch {
                saveTime(triggerTime)
            }
        }
    }
    createTimer()
}

/**
 * Creates a new timer
 */
private fun createTimer() {
    viewModelScope.launch {
        val triggerTime = loadTime()
        timer = object : CountDownTimer(triggerTime, second) {
            override fun onTick(millisUntilFinished: Long) {
                _elapsedTime.value = triggerTime - SystemClock.elapsedRealtime()
                if (_elapsedTime.value!! <= 0) {
                    resetTimer()
                }
            }

            override fun onFinish() {
                resetTimer()
            }
        }
        timer.start()
    }
}

/**
 * Cancels the alarm, notification and resets the timer
 */
private fun cancelNotification() {
    resetTimer()
    alarmManager.cancel(notifyPendingIntent)
}

/**
 * Resets the timer on screen and sets alarm value false
 */
private fun resetTimer() {
    timer.cancel()
    _elapsedTime.value = 0
    _alarmOn.value = false
}

private suspend fun saveTime(triggerTime: Long) =
    withContext(Dispatchers.IO) {
        prefs.edit().putLong(TRIGGER_TIME, triggerTime).apply()
    }

private suspend fun loadTime(): Long =
    withContext(Dispatchers.IO) {
        prefs.getLong(TRIGGER_TIME, 0)
    }'''

片段

class BlankFragment : Fragment() {

companion object {
    fun newInstance() = BlankFragment()
}
private val viewModel : BlankViewModel by lazy {
    ViewModelProvider(this).get(BlankViewModel::class.java)
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val binding = FragmentBlankBinding.inflate(inflater)
    binding.lifecycleOwner = this
    binding.blankViewModel = viewModel
  
    return binding.root
} '''

我在查看其他类似问题时发现这些问题都可以通过我已经添加的 gradle 依赖项来解决。

 implementation 'androidx.work:work-runtime-ktx:2.7.1'  

但是出现了这个错误

E/AndroidRuntime: Caused by: java.lang.IllegalArgumentException: com.shivaconsulting.kotlinnotifications: 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.
        at android.app.PendingIntent.checkFlags(PendingIntent.java:378)
        at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:648)
        at android.app.PendingIntent.getBroadcast(PendingIntent.java:635)
        at com.shivaconsulting.kotlinnotifications.ui.BlankViewModel.<init>(BlankViewModel.kt:52)
            ... 44 more

Udacity Starter Code


你试过我的解决方案了吗? - Ashvin solanki
你解决了这个问题吗?@Shiva - Ashvin solanki
6个回答

19

参考文献: https://developer.android.com/reference/android/app/PendingIntent#FLAG_MUTABLE

FLAG_MUTABLE是一个标识,表示创建的PendingIntent应该是可变的。这个标识不能与FLAG_IMMUTABLE组合使用。

在Build.VERSION_CODES.R版本之前,默认情况下假定PendingIntent是可变的,除非设置了FLAG_IMMUTABLE。从Build.VERSION_CODES.S开始,将要求在创建PendingIntents时显式指定其可变性,可以使用(@link #FLAG_IMMUTABLE}FLAG_MUTABLE来指定。强烈建议在创建PendingIntent时使用FLAG_IMMUTABLE。只有在一些功能依赖于修改基础意图的情况下,例如任何需要与内联回复或气泡一起使用的PendingIntent,才应使用FLAG_MUTABLE

    notifyPendingIntent = PendingIntent.getBroadcast(
            getApplication(),
            REQUEST_CODE,
            notifyIntent,
            PendingIntent.FLAG_UPDATE_CURRENT
        )

代码应该是这样的。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
 notifyPendingIntent = PendingIntent.getBroadcast(
            getApplication(),
            REQUEST_CODE,
            notifyIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
        )
    } else {
      notifyPendingIntent = PendingIntent.getBroadcast(
            getApplication(),
            REQUEST_CODE,
            notifyIntent,
            PendingIntent.FLAG_UPDATE_CURRENT
        )
    }

你必须以同样的方式去做

_alarmOn.value = PendingIntent.getBroadcast(
        getApplication(),
        REQUEST_CODE,
        notifyIntent,
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_MUTABLE else PendingIntent.FLAG_NO_CREATE
    ) != null

编辑2

如果您没有使用PendingIntent,但在使用其他组件(如MediaSessionCompat等)时系统仍然崩溃。

implementation 'androidx.work:work-runtime-ktx:RUNTIME-VERSION'

RUNTIME版本: https://developer.android.com/jetpack/androidx/releases/work


我正在使用reactnative,并且仅在Android 12+版本上出现了此错误。我使用了Work Manager,在gradle文件中添加了一行代码,并在RNPushNotificationHelper.java文件中进行了一些更改,因为我搜索了PendingIntent并对PendingIntent.getActivity()和PendingIntent.getBroadcast()进行了一些更改,但仍然出现相同的错误。您有解决此问题的任何想法吗? - Ayush Katuwal
更新Facebook SDK,如果该SDK存在任何问题,也可以修复您的错误。 - Ashvin solanki
在RNPushNotificationHelper.java文件中。 - Ayush Katuwal
1
将 "react-native-fbsdk-next" 升级到版本 "10.1.0" 可以解决这个问题,非常感谢你帮我解决了这个问题,兄弟。 - Ayush Katuwal
1
@AyushKatuwal 欢迎,你做到了这一切功劳都归于你自己 :) - Ashvin solanki
显示剩余4条评论

7

[解决方案] 添加以下依赖项。这对我来说是有效的。

implementation 'androidx.work:work-runtime:2.7.0-alpha05'

2

这是更好的方法

 private val flags = when{
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ->
        PendingIntent.FLAG_MUTABLE
    else -> {PendingIntent.FLAG_MUTABLE}
}

它在sdk 31及以下版本中运行正常。 - Sumit Ojha

2
除了@Ashvinsolanki的答案之外,您可能需要将implementation "androidx.work:work-runtime-ktx:2.5.0"的版本升级到最新版本。

2

[解决方案] 如@Thilina所述

我从2.7.0-alpha04升级到2.7.0-alpha05,这对我有用 适用于API 31和33

实现'androidx.work:work-runtime:2.7.0-alpha05'


-8
如果您想要最快的解决方案,您应该修改app/build.gradle中的targetSdkVersion
最大值为30

3
现在需要在Google Play上部署31版。 - Terry Windwalker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接