基本上所有 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 進行日志打印看看。

可以看到,並沒有。我們只有在 onWindowFocusChanged 回調的時候才能真正的拿到 View 的寬高值。
所以,在 Activity 的 onAttachedToWindow() 回調之后,布局中的 View 會回調 onAttachedToWindow() ,然后才會去進行測量和繪制等。那么我們要獲取一個 View 的寬高就最好是 View.post() 了。
View 的 onAttachedToWindow 使用場景
View 的 onAttachedToWindow() 的調用時機會發生在 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
說到 Adapter 的 onViewAttachedToWindow,咋發現這里面竟然還有一個 onAttachedToRecyclerView 方法,根據源碼我們可以發現,onAttachedToRecyclerView() 是在 setAdapter() 的時候觸發。
對比一下,我們便能得出以下它們的使用場景:
RecyclerView本質上也是一個ViewGroup,那么它的Item要顯示出來,自然需要addView()進來,移出的時候,當然也要removeView()出去,所以對應的自然是onViewAttachedToWindow()和onViewAttachedToWindow()了。所以在一定場景下,可以通過這兩個回調來處理一些 Item 移出屏幕,移進屏幕鎖需要的工作。為什么說一定場景下呢,因為如果調用了notifyDataSetChanged()方法的話,會觸發當前在屏幕中的所有 Item 的onViewAttachedToWindow()。- 而
onAttachedToRecyclerView和onAttachedToRecyclerView()的話,就更加適合做一些資源回收的工作啦。
我的 RecyclerView.Adapter 的 onViewAttachedToWindow 為啥沒起作用?
可能會有小伙伴會遇到這個問題,在遇到這個問題前,先檢查一下你這個 RecyclerView 是否是一個正常滾動的 View,你如果是被別人嵌套滾動,把自己設置了 isNestedScrollingEnabled 為 false 的話,那你都失去了 Recyclerview 的功用了,那自然是不行的。
可能又有小伙伴說了,由於需求歷史原因,我就是用了 NestedScrollView 嵌套了 Recyclerview,並禁掉了 Recyclerview 的滑動功能,但又想做上面的曝光埋點功能,那如何是好?
如果是這樣的話,大概你就只能通過類似 View 的 getGlobalVisibleRect() 這樣的方法來判斷 View 的可見性來處理了。關於 View 的可見性分析,這里就點到為止,大家就自行 Google 吧。
