以下暂停功能将在1秒延迟内更新进度条和2个TextView。进度条分别指示MP3和TextView的进度,经过时间和剩余时间。
用户可以离开片段,然后再次返回片段,这意味着片段(视图)被破坏并再次创建。
我想知道这种实现是否正确和/或是否有更好的实现和/或替代方法(有史以来第一次实现协程)。这是一些代码:
class BookViewFragment : Fragment(), CoroutineScope {
private var _binding: FragmentBookViewerBinding? = null
private val bookViewFragmentBinding get() = _binding!!
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentBookViewerBinding.inflate(layoutInflater)
val view = bookViewFragmentBinding.root
job = Job()
initMediaPlayer()
return view
}
override fun onDestroyView() {
super.onDestroyView()
job.cancel()
_binding = null
mp.stop()
mp.release()
}
private fun initMediaPlayer() {
mp = MediaPlayer()
mp.run {
setDataSource(...)
setVolume(0.5f, 0.5f)
prepare()
}
totalTime = mp.duration
initPositionBar()
}
private fun initPositionBar() {
bookViewFragmentBinding.mediaPosition.max = totalTime
launch {
setTimeOnProgressBar()
}
}
private suspend fun setTimeOnProgressBar() {
coroutineScope {
launch {
var progress = mp.currentPosition
while (progress < mp.duration) {
progress = mp.currentPosition
bookViewFragmentBinding.mediaPosition.progress = progress
val timePlayed = progress
val timeLeft = mp.duration - timePlayed
bookViewFragmentBinding.timePlayed.text = formatIntToTime(timePlayed)
bookViewFragmentBinding.timeLeft.text =
getString(R.string.time_left, formatIntToTime(timeLeft))
delay(1000)
}
}
}
}
}
答案 0 :(得分:1)
这看起来是正确的,但是您在循环周围创建了两个不必要的协程层。在setTimeOnProgressBar()
中,您已将协程包裹在一个新的coroutineScope
中,而您无需使用它。可以删除它,那么它根本不必是暂停函数。因此,您还可以删除将setTimeOnProgressBar()
中的调用包装到initPositionBar()
的协程。
此外,您还重新创建了Android ktx库已经提供的一堆样板。您已经可以使用lifecycleScope
扩展属性来启动协程,并且在onDestroyView()
中会自动取消它。因此,您无需创建父作业或覆盖coroutineContext
或取消父作业。
您可以在启动协同程序时使用lifecycleScope.launch
。
class BookViewFragment : Fragment() {
private var _binding: FragmentBookViewerBinding? = null
private val bookViewFragmentBinding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentBookViewerBinding.inflate(layoutInflater)
val view = bookViewFragmentBinding.root
initMediaPlayer()
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
mp.stop()
mp.release()
}
private fun initMediaPlayer() {
mp = MediaPlayer()
mp.run {
setDataSource(...)
setVolume(0.5f, 0.5f)
prepare()
}
totalTime = mp.duration
initPositionBar()
}
private fun initPositionBar() {
bookViewFragmentBinding.mediaPosition.max = totalTime
setTimeOnProgressBar()
}
private fun setTimeOnProgressBar() {
lifecycleScope.launch {
var progress = mp.currentPosition
while (progress < mp.duration) {
progress = mp.currentPosition
bookViewFragmentBinding.mediaPosition.progress = progress
val timePlayed = progress
val timeLeft = mp.duration - timePlayed
bookViewFragmentBinding.timePlayed.text = formatIntToTime(timePlayed)
bookViewFragmentBinding.timeLeft.text =
getString(R.string.time_left, formatIntToTime(timeLeft))
delay(1000)
}
}
}
}