日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

Handler引發(fā)的泄露(通常發(fā)生在Activity、Fragment等容器)、crash是Android開發(fā)中常見的問題,也是面試時(shí)非常容易被問到的技術(shù)點(diǎn)。關(guān)于Handler為何會(huì)引起容器泄露,網(wǎng)上有很多的文章,這里就簡(jiǎn)單提一下引用鏈:

Thread->ThreadLocal->Looper->MessageQueue->Message->Handler->Activity

Handler產(chǎn)生的泄露一般是暫時(shí)的,當(dāng)消息成功調(diào)度后,從消息隊(duì)列中移除,上面的引用鏈也便就不存在了,在下次gc時(shí),Activity便可以正常釋放。因此大多情況下,Handler引起的泄露問題并不可怕(極端情況另說),可怕的是引起crash。下面重點(diǎn)討論下Handler如何引起crash,看個(gè)偽代碼:

class TestFragment: Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Toast.makeText(activity, "AAA", Toast.LENGTH_SHORT).show()
        Handler().postDelayed({
            Toast.makeText(activity, "BBB", Toast.LENGTH_SHORT).show()
        }, 5000)
        parentFragmentManager.beginTransaction().run {
            remove(this@TestFragment)
            commitAllowingStateLoss()
        }
    }
}

啟動(dòng)TestFragment后,會(huì)先看到一條"AAA"的吐司,然后在logcat中看到如下crash日志:

JAVA.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference

at android.widget.Toast.<init>(Toast.java:121)

at android.widget.Toast.makeText(Toast.java:286)

at android.widget.Toast.makeText(Toast.java:276)

at com.ada.test_App.TestFragment.onCreate$lambda-0(MainActivity.kt:136)

at com.ada.test_app.TestFragment.$r8$lambda$ZvBB3ieIi-neYI0Ok2qP--pCPEg(Unknown Source:0)

at com.ada.test_app.TestFragment$$ExternalSyntheticLambda0.run(Unknown Source:2)

at android.os.Handler.handleCallback(Handler.java:883)

at android.os.Handler.dispatchMessage(Handler.java:100)

at android.os.Looper.loop(Looper.java:238)

at android.app.ActivityThread.main(ActivityThread.java:7798)

at java.lang.reflect.Method.invoke(Native Method)

at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:995)

原因是當(dāng)Fragment生命周期結(jié)束時(shí),會(huì)將activity對(duì)象置為null,等消息延遲調(diào)度時(shí),取得的便是一個(gè)空的activity,因此出現(xiàn)了空指針異常。解決的辦法很簡(jiǎn)單,加個(gè)空判斷就好了。然而現(xiàn)實(shí)中的場(chǎng)景會(huì)復(fù)雜很多,而且開發(fā)人員素質(zhì)參差不齊,沒法保證所有場(chǎng)景都正確處理了,我們希望能有一套通用的解決方案。以下是筆者寫的SafeHandler,在實(shí)際項(xiàng)目中已經(jīng)廣泛使用,是一個(gè)小而美的組件:

class SafeHandler(owner: LifecycleOwner, looper: Looper = Looper.getMainLooper()): Handler(looper), LifecycleObserver {
    private val host = WeakReference(owner)

    init {
        owner.lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
        removeCallbacksAndMessages(null)
    }

    override fun dispatchMessage(msg: Message) {
        val owner = host.get()
        if (owner != null && owner.lifecycle.currentState != Lifecycle.State.DESTROYED) {
            super.dispatchMessage(msg)
        }
    }
}

fun handlerOf(owner: LifecycleOwner, looper: Looper = Looper.getMainLooper()): Handler {
    return SafeHandler(owner, looper)
}

fun LifecycleOwner.newHandler(looper: Looper = Looper.getMainLooper()): Handler {
    return handlerOf(this, looper)
}

分析下代碼:

  • 這里looper默認(rèn)為主線程looper,而構(gòu)建系統(tǒng)的Handler在沒有設(shè)置looper時(shí),默認(rèn)是獲取當(dāng)前線程looper。從Handler的通用性來說這樣設(shè)計(jì)沒有問題,但從業(yè)務(wù)的角度來說,我們使用的Handler絕大多數(shù)是位于主線程中,因此這樣設(shè)計(jì)會(huì)更安全一些,避免一些開發(fā)者因?yàn)閷?duì)Handler的機(jī)制不夠了解而使用默認(rèn)構(gòu)建方法構(gòu)建出了錯(cuò)誤的Handler。
  • LifecycleOwner使用弱引用存儲(chǔ),SafeHandler本身就是為了解決內(nèi)存泄露及crash,當(dāng)然不能因?yàn)樽陨淼娜毕輰?dǎo)致另外的泄露了。
  • 監(jiān)聽LifecycleOwner的銷毀,在銷毀時(shí)清除所有消息。
  • 在調(diào)度消息時(shí)判斷LifecycleOwner的狀態(tài),如果已經(jīng)銷毀,就不允許執(zhí)行。既然已經(jīng)在onDestroy時(shí)清空消息了,為什么還要做這步操作呢?這是因?yàn)橥獠坑锌赡茉趏nDestroy后依然使用Handler去發(fā)送消息。

分享到:
標(biāo)簽:Android
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定