所以这里有一些事情需要考虑,因为有很多方法可以给这只猫剥皮。虽然答案都已经给出了选择和选择。我认为通过适当的编码指南重新审视这一点很重要,以避免任何人仅仅因为“多数选择简单答案”而走错方向。
首先让我们讨论一下简单的延迟发布答案,它是本主题中的获胜者选择的答案。
有几点需要考虑。在 post 延迟之后,您可能会遇到内存泄漏、死对象、已经消失的生命周期等等。所以正确处理也很重要。您可以通过多种方式做到这一点。
为了现代发展,我会在KOTLIN中提供
这是一个在回调中使用 UI 线程的简单示例,并在您点击回调时确认您的 Activity 仍然存在且运行良好。
Handler(Looper.getMainLooper()).postDelayed({
if(activity != null && activity?.isFinishing == false){
txtNewInfo.visibility = View.GONE
}
}, NEW_INFO_SHOW_TIMEOUT_MS)
但是,这仍然不是完美的,因为如果活动已经消失,没有理由点击您的回调。所以更好的方法是保留对它的引用并像这样删除它的回调。
private fun showFacebookStylePlus1NewsFeedOnPushReceived(){
A35Log.v(TAG, "showFacebookStylePlus1NewsFeedOnPushReceived")
if(activity != null && activity?.isFinishing == false){
txtNewInfo.visibility = View.VISIBLE
mHandler.postDelayed({
if(activity != null && activity?.isFinishing == false){
txtNewInfo.visibility = View.GONE
}
}, NEW_INFO_SHOW_TIMEOUT_MS)
}
}
当然还要处理 onPause 上的清理,这样它就不会触发回调。
override fun onPause() {
super.onPause()
mHandler.removeCallbacks(null)
}
现在我们已经讨论了显而易见的问题,让我们来谈谈现代协程和 kotlin 的更简洁的选择:)。如果你还没有使用这些,那你真的错过了。
fun doActionAfterDelay()
launch(UI) {
delay(MS_TO_DELAY)
actionToTake()
}
}
或者,如果您想始终在该方法上启动 UI,您可以简单地执行以下操作:
fun doActionAfterDelay() = launch(UI){
delay(MS_TO_DELAY)
actionToTake()
}
当然,就像 PostDelayed 一样,您必须确保处理取消,以便您可以在延迟调用之后进行活动检查,或者您可以像其他路线一样在 onPause 中取消它。
var mDelayedJob: Job? = null
fun doActionAfterDelay()
mDelayedJob = launch(UI) {
try {
delay(MS_TO_DELAY)
actionToTake()
}catch(ex: JobCancellationException){
showFancyToast("Delayed Job canceled", true, FancyToast.ERROR, "Delayed Job canceled: ${ex.message}")
}
}
}
}
//处理清理
override fun onPause() {
super.onPause()
if(mDelayedJob != null && mDelayedJob!!.isActive) {
A35Log.v(mClassTag, "canceling delayed job")
mDelayedJob?.cancel() //this should throw CancelationException in coroutine, you can catch and handle appropriately
}
}
如果您将启动(UI)放入方法签名中,则可以在调用代码行中分配作业。
故事的寓意是确保您的延迟操作安全,确保您删除回调,或取消您的工作,当然还要确认您有正确的生命周期来触摸延迟回调完成的项目。协程还提供可取消的操作。
另外值得注意的是,您通常应该处理协程可能带来的各种异常。例如,取消、异常、超时,无论您决定使用什么。如果您决定真正开始使用协程,这里有一个更高级的示例。
mLoadJob = launch(UI){
try {
//Applies timeout
withTimeout(4000) {
//Moves to background thread
withContext(DefaultDispatcher) {
mDeviceModelList.addArrayList(SSDBHelper.getAllDevices())
}
}
//Continues after async with context above
showFancyToast("Loading complete", true, FancyToast.SUCCESS)
}catch(ex: JobCancellationException){
showFancyToast("Save canceled", true, FancyToast.ERROR, "Save canceled: ${ex.message}")
}catch (ex: TimeoutCancellationException) {
showFancyToast("Timed out saving, please try again or press back", true, FancyToast.ERROR, "Timed out saving to database: ${ex.message}")
}catch(ex: Exception){
showFancyToast("Error saving to database, please try again or press back", true, FancyToast.ERROR, "Error saving to database: ${ex.message}")
}
}