![640?wx_fmt=gif](https://i-blog.csdnimg.cn/blog_migrate/8dac68db896be24f6c38eb8815030b3f.gif)
温馨提示
请拖动到文章末尾,长按识别「抽奖」小程序。现金红包等你来拿。
1
「小新」近来遇到了 Handler 的泄露,百思不得其解。
2
在正文开始之前,听「大师兄」讲述一段经历,作为一名职场的老前辈,问过很多人,你知道 Handler 吗?大家回答都知道,那什么是 Handler ?没多少人能够准备的概述,如果是你,你会怎么答?
官方 API 是这么介绍的:
Handler是用来结合线程的消息队列来发送、处理“Message对象”和“Runnable对象”的工具。每一个Handler实例之后会关联一个线程和该线程的消息队列。当你创建一个Handler的时候,从这时开始,它就会自动关联到所在的线程/消息队列,然后它就会陆续把Message/Runnalbe分发到消息队列,并在它们出队的时候处理掉。
从官方文档中,我们不难找出其中的关键词,就是“线程”。
我们都知道,一个涉及到网络操作,耗时操作等的 Android 应用,都离不开多线程操作,然而,如果这时我们允许并发更新 UI,那么最终导致控件的状态都是不可确定的。
所以,我们可以通过对控件进行加锁,在不需要用时解锁,这是一个解决方案之一,但最后很容易造成线程阻塞,效率会非常差。所以,谷歌采用了只允许在主线程更新 UI,所以作为线程通信桥梁的 Handler 也就应运而生了。
在耗时操作,通过 Handler 来更新 UI,经常会遇到这里提示 This Handler class should be static or leaks might occur 那么为什么会有这样的内存泄露警告呢?
在 Java 中,非静态内部类和匿名内部类都会隐式地持有其外部类的引用。静态内部类不会持有外部类的应用。
每个 Handler 都需要一个 Looper 来处理消息,但是线程是默认没有 Looper 的,如果需要使用 Handler,就需要为线程创建 Looper。在应用的主线程(ActivityThread)创建时会自动初始化一个 Looper,这也是为什么主线程中可以默认使用Handler 的原因。因此主线程中的 Looper 生命周期是和应用的一样长。
发送的消息中包含有 Handler 实例的引用,只有消息被处理后,handler 的引用才会释放,如果 Activity 已经销毁,但是Looper 中还有消息没有处理完。handler 就不能释放,如果handler 不是 Activity 的静态内部类,那么 handler 就会持有 Activity 的引用,导致该 Activity 不能正常回收,这样就造成了内存泄露。
大家都在说内部类,那么什么又是匿名内部类,方法内部类呢?
3
java内部类分为:
成员内部类
静态嵌套类
方法内部类
匿名内部类
内部类的共性
内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的 .class 文件,但是前面冠以外部类的类名和 $ 符号。
内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是 private 的。
内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量。
成员内部类
public class User {
class People{
}
}
People 为 User 的内部类,编译后会产生两个文件:User.class 和User$People.class。
方法内部类
public class User {
// 方法内部类
public void doSomething(){
class People{
}
}
}
(1)、方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
(2)、方法内部类对象不能使用该内部类所在方法的非 final 局部变量。
因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。
来看下完整例子:
public class User {
// 方法内部类
public void doSomething() {
final int a = 10;
int b = 20;
class People {
public void seeUser() {
System.out.println(a);
// 报错 System.out.println(b);
}
}
People people = new People();
people.seeUser();
}
public static void main(String[] args) {
User user = new User();
user.doSomething();
}
}
结果打印:10
匿名内部类
顾名思义,没有名字的内部类。当程序中使用匿名内部类时,在定义匿名内部类的地方往往直接创建该类的一个对象。
匿名内部类的声明格式如下:
new User(){
};
那么什么情况下需要使用匿名内部类?如果满足下面的一些条件,使用匿名内部类是比较合适的:
只用到类的一个实例 。
类在定义后马上用到。
类非常小(SUN推荐是在4行代码以下)
给类命名并不会导致你的代码更容易被理解。
在使用匿名内部类时,要记住以下几个原则:
匿名内部类不能有构造方法。
匿名内部类不能定义任何静态成员、静态方法。
匿名内部类不能是
public,protected,private,static。
只能创建匿名内部类的一个实例。
一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
继承式的匿名内部类
public class Car {
public void drive() {
System.out.println("Driving a car!");
}
public static void main(String[] args) {
Car car = new Car() {
@Override
public void drive() {
// super.drive();
System.out.println("Driving another car!");
}
};
// 调用
car.drive();
}
}
输出结果:Driving another car!
接口式的匿名内部类
public class Animal {
public static void main(String[] args) {
Move move = new Move() {
@Override
public void eat() {
System.out.println("Animal eat!");
}
};
move.eat();
}
}
interface Move {
void eat();
}
输入结果:Animal eat!
静态嵌套类
public class User {
static class People {
}
public static void main(String[] args) {
User.People people = new User.People();
}
}
静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。
静态嵌套类仅能访问外部类的静态成员和方法。
4
那么如何避免 Handler 引起的内存泄露,这里推荐以下两种做法。
当 Activity 生命周期结束时,移除 Handler 中未执行的消息:
@Override
protected void onDestroy(){
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
第二种方法,将 Handler 类声明为静态内部类,同时持有 Activity 的弱引用,在 gc 时就可以对 Activity 进行回收。代码可以这样写:
private static class MyHandler extends Handler {
private final WeakReference<Activity> mActivityWeakReference;
private MyHandler(Activity activity) {
mActivityWeakReference = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mActivityWeakReference == null || mActivityWeakReference.get() == null) {
return;
}
}
}
以上仅个人观点,有什么疑问,请留言哟。
推荐阅读
LinearLayout和RelativeLayout性能对比,你认为谁的效率更高?
长按识别小程序,参与抽奖
![640?wx_fmt=other](https://i-blog.csdnimg.cn/blog_migrate/ef33f2462ceb71efa605ba44d031eea7.jpeg)
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/1d989f6c62380180ff0add21d6642221.png)
目前100000+人已关注加入我们
微信扫描参与抽奖,喜欢请点击「再看」哟