android reference,Android WeakReference

探讨一下为什么Android Studio会提示handler类要写成static的。

下面这张图是匿名内部类创建一个对象(本质上是继承了Handler然后覆写了它的方法)。定义一个非静态内部类然后再new也会有这个提示。(为什么直接new 一个Handler类的对象就不会提示,因为不会持有外部类的引用)。

35557dc05f0e

image.png

更典型的场景:

35557dc05f0e

image.png

问题原因

35557dc05f0e

image.png

Activity在被结束之后,MessageQueue并不会随之被结束,如果这个消息队列中存在msg,则导致持有handler的引用,但是又

由于Activity被结束了,msg无法被处理,从而导致永久持有handler对象,handler永久持有Activity对象,于是发生内存泄漏。但是为什么为static类型就

会解决这个问题呢?因为在java中所有非静态的对象都会持有当前类的强引用,而静态对象则只会持有当前类的弱引用。声明为静态后,handler将会持

有一个Activity的弱引用,而弱引用会很容易被gc回收,这样就能解决Activity结束后,gc却无法回收的情况。

也就是说这里造成了Activity不能被回收的情况。因为Handler是不应该阻碍Activity回收的(本来就应该是独立的)。

怎么改

如果改成直接把内部类static class会发生啥大家都知道,就是mTextView不能用了,因为静态内部类不能访问外部对象呀。那总不能把TextView设置成静态的吧。。那TextView可就一直不能被回收了。

所以用弱引用。同样,不仅仅是View,Activity也可以通过弱引用传给Handler。

那么我们这样做吧:

35557dc05f0e

image.png

这样可以解除报错,但其实我写的这个例子还不够典型,因为这个例子里TextView是要被更新的,所以Activity被关闭View都没了的话就没有意义了。。那如果WeakReference里包的是一个Activity就更恰当了,因为弱引用非常过分,只要GC遇到了就会回收(GC一般是无向图遍历不到的位置才回收,包括互相引用的两个对象),很适合handleMessage里完全用不到的Activity对象。

简单解释

把Handler类写在Activity内部,或者用匿名内部类来声明Handler对象,这个类都会默认持有外部类(比如Activity/Service)的引用。这是由Java编译器在构造函数初始化的时候完成的。那如果外部Activity已经finish了,Activity/Service对象由于被MainLooper->MessageQueue->Message->Handler->Activity/Service这样一个链条持有着,Activity就会一直无法被GC回收。

Handler导致内存泄露

我看了一篇博客说得比较容易懂,就是为了防止内存泄漏。他举了个例子,

当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。

但是有一句话表达的不太准确:

35557dc05f0e

TIM图片20171017195350.png

首先,图里想表的应该是「静态内部类」。

因为Java中非静态内部类可以访问外部类的成员(非静态内部类持有外部类的this引用),但静态内部类不行。至于为什么:

1 编译器自动为内部类添加一个成员变量, 这个成员变量的类型和外部类的类型相同, 这个成员变量就是指向外部类对象(this)的引用;

2 编译器自动为内部类的构造方法添加一个参数, 参数的类型是外部类的类型, 在构造方法内部使用这个参数为内部类中添加的成员变量赋值;

3 在调用内部类的构造函数初始化内部类对象时,会默认传入外部类的引用。

可以理解成静态内部类就相当于在外面写一个单独的类文件。

什么是WeakReference?

WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了。

其他方法

另外,上面那篇BLOG还提到可以通过程序逻辑来防止内存泄露。

1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。

2.如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值