每日一問:淺談 onAttachedToWindow 和 onDetachedFromWindow

南塵發表於2019-06-27

基本上所有 Android 開發都會接觸到 onCreate()onDestory()onStart()onStop() 等這些生命週期方法,但卻不是所有人都會去關注到 onAttachXXX() 這樣的方法群體,今天,筆者就希望用簡短的文章對此進行一定講解。

Activity 中的 onAttachedToWindow

首先在 Activity 中我們可以重寫 onAttachedToWindow()onDetachedFromWindow() 這一對方法。顧名思義,"Attached" 就是附加的意思,所以我們可以確定 onAttachedToWindow() 就是在 View 附加到 window 上的時候進行回撥,而 onDetachedFromWindow() 就剛好相反。

這一對方法會在我們熟悉的 Activity 生命週期的 onResume()onPause() 中間,但並不是每一次 onResume()onPause() 回撥的時候都會在接下來回撥它們。應該比較好理解,我們當然不需要頻繁往 window 中附加和分離 View 嘛。

這裡自然我們容易產生一個問題,在 onAttachedToWindow() 回撥的時候,我們能拿到 View 的寬高麼?換句話說就是這時候 View 是否已經經過了測量和繪製呢?

我們編寫一個 Demo 進行日誌列印看看。
每日一問:淺談 onAttachedToWindow 和 onDetachedFromWindow

可以看到,並沒有。我們只有在 onWindowFocusChanged 回撥的時候才能真正的拿到 View 的寬高值。

所以,ActivityonAttachedToWindow() 回撥之後,佈局中的 View 會回撥 onAttachedToWindow() ,然後才會去進行測量和繪製等。那麼我們要獲取一個 View 的寬高就最好是 View.post() 了。

View 的 onAttachedToWindow 使用場景

ViewonAttachedToWindow() 的呼叫時機會發生在 onMeasure() 之前,那麼它們到底有什麼使用場景呢?

我們在自定義 View 的時候,某些比較重量級的資源,而且不能與其他 View 通用的時候,就可以重寫這兩個方法,並在 onAttachedToWindow() 中進行初始化,onDetachedFromWindow() 方法裡釋放掉。

比如 Bitmap,雖說現在不用主動呼叫 recycle() 方法來回收,但在 8.0 及以上系統,手動呼叫是會立即釋放所佔用的記憶體的,所以個人認為還是有必要手動回收的,當然了,如果圖片比較小,對記憶體沒什麼影響的就不用了。

再比如一些用作計算的子執行緒,或其他跟 View 顯示有關的任務,在 onDetachedFromWindow() 中也可以停掉了,因為大多數情況下,這些實時資料對於被分離後 View 已經沒有意義了。

RecyclerView.Adapter 的 onViewAttachedToWindow

細心的小夥伴會發現,在 RecyclerView.Adapter 中也會有這麼一個 onViewAttachedToWindow()onViewAttachedToWindow()

這兩個方法在列表佈局的時候,用作曝光埋點非常好用,當 Adapter 建立的 View 被視窗分離(即滑動離開了當前視窗介面的)的時候,onViewAttachedToWindow() 會被直接回撥,反之,在列表項 View 在被滑動進螢幕的時候,onViewAttachedToWindow() 會立馬被呼叫。

有了這樣的屬性,對於我們的曝光埋點,就手到擒來了,直接在裡面做就完事兒了。

RecyclerView.Adapter 咋還有一個 onAttachedToRecyclerView

說到 AdapteronViewAttachedToWindow,咋發現這裡面竟然還有一個 onAttachedToRecyclerView 方法,根據原始碼我們可以發現,onAttachedToRecyclerView() 是在 setAdapter() 的時候觸發。

對比一下,我們便能得出以下它們的使用場景:

  1. RecyclerView 本質上也是一個 ViewGroup,那麼它的 Item 要顯示出來,自然需要 addView() 進來,移出的時候,當然也要 removeView() 出去,所以對應的自然是 onViewAttachedToWindow()onViewAttachedToWindow() 了。所以在一定場景下,可以通過這兩個回撥來處理一些 Item 移出螢幕,移進螢幕鎖需要的工作。為什麼說一定場景下呢,因為如果呼叫了 notifyDataSetChanged() 方法的話,會觸發當前在螢幕中的所有 Item 的 onViewAttachedToWindow()
  2. onAttachedToRecyclerViewonAttachedToRecyclerView() 的話,就更加適合做一些資源回收的工作啦。

我的 RecyclerView.Adapter 的 onViewAttachedToWindow 為啥沒起作用?

可能會有小夥伴會遇到這個問題,在遇到這個問題前,先檢查一下你這個 RecyclerView 是否是一個正常滾動的 View,你如果是被別人巢狀滾動,把自己設定了 isNestedScrollingEnabled 為 false 的話,那你都失去了 Recyclerview 的功用了,那自然是不行的。

可能又有小夥伴說了,由於需求歷史原因,我就是用了 NestedScrollView 巢狀了 Recyclerview,並禁掉了 Recyclerview 的滑動功能,但又想做上面的曝光埋點功能,那如何是好?

如果是這樣的話,大概你就只能通過類似 ViewgetGlobalVisibleRect() 這樣的方法來判斷 View 的可見性來處理了。關於 View 的可見性分析,這裡就點到為止,大家就自行 Google 吧。

相關文章