每日一问:浅谈 onAttachedToWindow 和 onDetachedFromWindow
基本上所有 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 吧。