java类的作用_java 内部类的好处和缺点(下)

上一篇讲了关于静态内部类的作用和分类,这一篇来讲一下关于非静态内部类的缺点:容易造成内存泄露,这一篇几乎照搬人家的博客啦,想去看原篇,可以直接点击文章最后的超链接啦。

非静态内部类: 成员内部类, 局部内部类、 匿名内部类。 会有对外部类的引用。这样内部类中耗时操作在用户频繁退出重启APP相关Activity时很容易导致内存泄漏。

一、匿名内部类:Runnable

1、泄漏版

new Thread(new Runnable() {

@Override

public void run() {

try {

//模拟耗时操作

Thread.sleep(15000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}).start();

连续多次退出重启后发现:

430a00d72488

image.png

为什么?

上面代码在 activity 中创建了一个匿名类 Runnable,匿名类和非静态内部类相同,会持有外部类对象,这里也就是 activity,因此如果你在 Activity 里声明且实例化一个匿名的 Runnable 对象,则可能会发生内存泄漏,如果这个线程在Activity销毁后还一直在后台执行,那这个线程会继续持有这个 Activity 的引用从而不会被 GC 回收,直到线程执行完成。

2、优化版:

将 非静态内部类 改为 静态非匿名内部类

new Thread(new MyRunnable()).start();

private static class MyRunnable implements Runnable {

@Override

public void run() {

try {

Thread.sleep(15000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

二、成员内部类:Handler

1、泄漏版:

private final static int MESSAGECODE = 1;

private Handler handler = new Handler() {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

Log.d("mmmmmmmm", "handler " + msg.what);

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

new Thread(new Runnable() {

@Override

public void run() {

handler.sendEmptyMessage(MESSAGECODE);

try {

Thread.sleep(8000);

} catch (InterruptedException e) {

e.printStackTrace();

}

handler.sendEmptyMessage(MESSAGECODE);

}

}).start();

}

连续多次退出重启后发现:

430a00d72488

image.png

为什么?

当 Android Application 启动以后,framework 会首先帮助我们完成UI线程的消息循环,也就是在 UI 线程中,Loop、MessageQueue、Message 等等这些实例已经由 framework 帮我们实现了。所有的 Application 主要事件,比如 Activity 的生命周期方法、Button 的点击事件都包含在这个 Message 里面,这些 Message 都会加入到 MessageQueue 中去,所以,UI 线程的消息循环贯穿于整个 Application 生命周期,所以当你在UI线程中生成 Handler 的实例,就会持有 Loop 以及 MessageQueue 的引用。并且在 Java 中非静态内部类和匿名内持有外部类的引用,而静态内部类则不会持有外部类的引用。

2、优化版:

使用静态内部类

使用弱引用

在onDestroy() 里面取消异步任务。(注意:单纯的取消还是会内存泄漏)

private final static int MESSAGECODE = 1;

private static Handler handler;//静态

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//创建Handler

handler = new MyHandler(this);

//创建线程并且启动线程

new Thread(new MyRunnable()).start();

}

//1、避免Handler引用activity造成的内存泄漏:使用静态内部类+ 使用弱引用

private static class MyHandler extends Handler {

WeakReference weakReference;

public MyHandler(HandlerActivity activity) {

weakReference = new WeakReference(activity);

}

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

if (weakReference.get() != null) {

// update android ui

Log.d("mmmmmmmm", "handler " + msg.what);

}

}

}

//2、避免非静态Runnable内部类引用activity造成的内存泄漏

private static class MyRunnable implements Runnable {

@Override

public void run() {

handler.sendEmptyMessage(MESSAGECODE);

try {

Thread.sleep(8000);

} catch (InterruptedException e) {

e.printStackTrace();

}

handler.sendEmptyMessage(MESSAGECODE);

}

}

@Override

protected void onDestroy() {

super.onDestroy();

//3、如果参数为null的话,会将所有的Callbacks和Messages全部清除掉。

handler.removeCallbacksAndMessages(null);

}

三、匿名内部类:TimerTask

1、泄漏版:

new Timer().schedule(new TimerTask() {

@Override

public void run() {

while (true) ;

}

}, 1000); // 1秒后启动一个任务

连续多次退出重启后发现:

430a00d72488

image.png

为什么?

这里内存泄漏在于 Timer 和 TimerTask 没有进行 Cancel,从而导致 Timer 和 TimerTask 一直引用外部类 Activity。

2、优化版:

1、在适当的时机进行Cancel。

2、TimerTask用静态内部类

private TimerTask timerTask ;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

timerTask = new MyTimerTask() ;

new Timer().schedule( timerTask ,1000 ); // 1秒后启动一个任务

}

private static class MyTimerTask extends TimerTask {

@Override

public void run() {

while(true){

Log.d( "ttttttttt" , "timerTask" ) ;

}

}

}

@Override

protected void onDestroy() {

super.onDestroy();

//取消定时任务

if ( timerTask != null ){

timerTask.cancel() ;

}

}

四、匿名内部类:AsyncTask

1、泄露版:

new AsyncTask(){

@Override

protected String doInBackground(String... params) {

try {

Thread.sleep( 6000 );

} catch (InterruptedException e) {

}

return "ssss";

}

@Override

protected void onPostExecute(String s) {

super.onPostExecute(s);

Log.d( "mmmmmm activity2 " , "" + s ) ;

}

}.execute();

连续多次退出重启后发现:

430a00d72488

image.png

为什么?

上面代码在 activity 中创建了一个匿名类 AsyncTask,匿名类和非静态内部类相同,会持有外部类对象,这里也就是 activity,因此如果你在 Activity 里声明且实例化一个匿名的 AsyncTask 对象,则可能会发生内存泄漏,如果这个线程在 Activity 销毁后还一直在后台执行,那这个线程会继续持有这个 Activity 的引用从而不会被 GC 回收,直到线程执行完成。

2、优化版

1、自定义静态AsyncTask类

2、AsyncTask的周期和Activity周期应该保持一致。也就是在Activity生命周期结束时要将AsyncTask cancel掉。

private static MyTask myTask;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

myTask = new MyTask();

myTask.execute();

}

//1、创建静态内部类

private static class MyTask extends AsyncTask {

@Override

protected Object doInBackground(Object[] params) {

try {

//模拟耗时操作

Thread.sleep(15000);

} catch (InterruptedException e) {

e.printStackTrace();

}

return "";

}

}

@Override

protected void onDestroy() {

super.onDestroy();

//2、取消异步任务

if (myTask != null) {

myTask.cancel(true);

}

}

最后

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值