Android Developer

倒计时逻辑组件实现

CountDownTimer 是什么

  • 一个可以设置总时间,间隔时间,支持Tick回调和完成回调的实现

用在什么地方

  • 商品详情页预售有倒计时处理

  • 收银台有支付倒计时处理

  • 首页二期也有倒计时处理

不是已经有实现了的么

  • 是的

  • 但是有些实现有问题,比如CountDownTimerUtils,使用了单例,会导致内存泄露问题

  • 缺乏一个统一标准的实现

如何使用

View篇

// 设置倒计时
view.applyCountDownTimer(10 * DateUtils.SECOND_IN_MILLIS, 1000, object: CountDownTimerCallback() {
            override fun onTick(millisUntilFinished: Long) {
                super.onTick(millisUntilFinished)
                smartLog {
                    "applyCountDownTimerForView millisUntilFinished=$millisUntilFinished"
                }
            }

            override fun onFinish() {
                super.onFinish()
                smartLog {
                    "applyCountDownTimerForView onFinish"
                }
            }
        })

//取消倒计时
addTestItem("主动取消View层级倒计时") {
    viewCountDownButton.cancelCountDownTimer()
}

Fragment层级

//设置倒计时
private fun applyCountDownTimerForFragment() {
        this.applyCountDownTimer(10 * 1000, 1000, object: CountDownTimerCallback() {
            override fun onTick(millisUntilFinished: Long) {
                super.onTick(millisUntilFinished)
                smartLog {
                    "applyCountDownTimerForFragment millisUntilFinished=$millisUntilFinished"
                }
            }

            override fun onFinish() {
                super.onFinish()
                smartLog {
                    "applyCountDownTimerForFragment onFinish"
                }
            }
        })
    }

//取消倒计时
addTestItem("主动取消Fragment层级倒计时") {
    this.cancelCountDownTimer()
}

Activity层级

//设置倒计时
private fun applyCountDownTimerForActivity() {
        this.activity.applyCountDownTimer(10 * 1000, 1000, object: CountDownTimerCallback() {
            override fun onTick(millisUntilFinished: Long) {
                super.onTick(millisUntilFinished)
                smartLog {
                    "applyCountDownTimerForActivity millisUntilFinished=$millisUntilFinished"
                }
            }

            override fun onFinish() {
                super.onFinish()
                smartLog {
                    "applyCountDownTimerForActivity onFinish"
                }
            }
        })
    }

//取消倒计时
addTestItem("主动取消Activity层级倒计时") {
    this.activity.cancelCountDownTimer()
}

如果我不主动取消会怎么样

  • 除去业务需求,不主动取消完全没有问题

自带生命周期管理的实现 View

fun View?.applyCountDownTimer(millisInFuture: Long?, countDownIntervalInMills: Long?,
                              timerCallback: CountDownTimerCallback?) {
    this ?: return
    CountDownTimerProvider.renewCountDownTimer(this, millisInFuture, countDownIntervalInMills,
            timerCallback)?.start()

    applyLifecycleForViewCountDownTimer(this)
}

//自带生命周期管理
private fun applyLifecycleForViewCountDownTimer(target: View) {
    target.addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener {
        override fun onViewDetachedFromWindow(view: View?) {
            smartLog {
                "CountDownExt onViewDetachedFromWindow view=$view"
            }
            CountDownTimerProvider.cancelCountDownTimer(view)
            view?.removeOnAttachStateChangeListener(this)
        }

        override fun onViewAttachedToWindow(view: View?) {
            smartLog {
                "CountDownExt onViewAttachedToWindow view=$view"
            }
        }

    })
}

自带生命周期管理实现 Fragment

fun Fragment?.applyCountDownTimer(millisInFuture: Long?, countDownIntervalInMills: Long?,
                                  timerCallback: CountDownTimerCallback?) {
    this ?: return
    CountDownTimerProvider.renewCountDownTimer(this, millisInFuture, countDownIntervalInMills, timerCallback)
            ?.start()
    this.doOnDestroy {
        CountDownTimerProvider.cancelCountDownTimer(this)
    }
}


fun Fragment?.doOnDestroy(runnable: FragmentToUnit? = null) {
    this ?: return
    runnable ?: return
    this.fragmentManager?.registerFragmentLifecycleCallbacks(object: FragmentManager.FragmentLifecycleCallbacks() {
        override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) {
            super.onFragmentDestroyed(fm, f)
            if (f == this@doOnDestroy) {
                runnable.invoke(f)
                fm.unregisterFragmentLifecycleCallbacks(this)
            }
        }
    }, false)
}

自带生命周期实现 Activity

fun Activity?.applyCountDownTimer(millisInFuture: Long?, countDownIntervalInMills: Long?,
                                  timerCallback: CountDownTimerCallback?) {
    this ?: return
    CountDownTimerProvider.renewCountDownTimzujianer(this, millisInFuture, countDownIntervalInMills, timerCallback)
            ?.start()
    this.onDestroy {
        CountDownTimerProvider.cancelCountDownTimer(this)
    }
}

fun Activity.onDestroy(runnable: (Activity) -> Unit) {
    observeCallback(object : ActivityLifecycleCallbacksImpl() {
        override fun onActivityDestroyed(activity: Activity) {
            super.onActivityDestroyed(activity)
            if (this@onDestroy == activity) {
                runnable(activity)
            }
        }
    })
}

CountDownTimerProvider 是什么

  • 一个CountDownTimer的集合体现
package com.secoo.commonsdk.ktx.secoo.countdown

import android.os.CountDownTimer
import com.secoo.commonsdk.ktx.smartLog
import java.util.*

object CountDownTimerProvider {
    private val timerMap = WeakHashMap<Any, CountDownTimer>()

    fun getCountDownTimer(key: Any?, millisInFuture: Long?, countDownIntervalInMills: Long?,
                          timerCallback: CountDownTimerCallback?): CountDownTimer? {
        if (key == null || millisInFuture == null || countDownIntervalInMills == null
                || timerCallback == null) {
            return null
        }

        return timerMap[key] ?: createCountDownTimer(millisInFuture, countDownIntervalInMills,
                timerCallback).also {
            timerMap[key] = it
        }
    }

    fun renewCountDownTimer(key: Any?, millisInFuture: Long?, countDownIntervalInMills: Long?,
                            timerCallback: CountDownTimerCallback?): CountDownTimer? {
        cancelCountDownTimer(key)
        return getCountDownTimer(key, millisInFuture, countDownIntervalInMills, timerCallback)
    }

    private fun createCountDownTimer(millisInFuture: Long, countDownIntervalInMills: Long,
                                     timerCallback: CountDownTimerCallback): CountDownTimer {

        return object : CountDownTimer(millisInFuture, countDownIntervalInMills) {
            override fun onFinish() {
                timerCallback.onFinish()
            }

            override fun onTick(millisUntilFinished: Long) {
                timerCallback.onTick(millisUntilFinished)
            }
        }
    }

    fun cancelCountDownTimer(key: Any?) {
        smartLog {
            "cancelCountDownTimer by key=$key"
        }
        key ?: return
        timerMap[key]?.cancel()
        timerMap.remove(key)
    }

}