对于handler内存泄漏分析解决+实例

来源:互联网 发布:看得很湿的黄文 知乎 编辑:程序博客网 时间:2024/06/01 23:11

转载请标明出处:http://blog.csdn.net/blackzhangwei/article/details/51930852 谢谢!
先说说什么是内存泄漏?
内存泄漏是简单意义来讲就是长生命周期对象引用短生命周期的对象,导致GC无法回收,会一直占用内存。那么就有可能导致内存溢出
内存泄漏最终会导致内存溢出,程序就崩掉了,没有正确的使用handler就会导致这样的问题发生
比如:当A对象,被B对象所引用,当B对象没有释放时,A对象不能被回收(A短B长),A对象可能造成内存泄漏
什么是内存溢出?
当一个程序内存泄漏过多或者内存不足时,程序会发生内存溢出,导致程序异常中断(崩溃)

解决内存泄漏来防止内存溢出正确的使用方法:
(1).在编写代码时一定要注意是否存在内存泄漏问题
(2).测试项目可以使用内存泄漏的检查工具

在处理Handler的内存泄漏方法:
(1).在结束Activity时候,删除所有回调和消息处理
handler.removeCallbacksAndMessages(null);

(2).定义为静态handler,因为静态不会依赖外部类对象,就会依赖类,所以activity就可以任意被回收
(是否用静态:就得考虑该对象是否长期占用activity)

(3).使用软引用或者弱引用,通过构造方法传递进来的activity利用弱引用来处理

强引用>软引用>弱引用>虚引用

StrongReference强引用:
直接new 出来的都叫强引用,内存不足时宁可抛出异常也不会去回收它

WeakReference弱引用:在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存,
(如果这个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用 Weak Reference 来记住此对象)

SoftReference软引用:
内存不足的时候,那么GC就直接把软引用对象回收掉,只要垃圾回收器没有回收它,该对象就可以被程序使用(软引用可用来实现内存敏感的高速缓存

PhantomReference虚引用:
如果对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收(一般很少使用)

这里写图片描述
关于强弱引用详解传门:http://blog.csdn.net/mazhimazh/article/details/19752475

↓下面是利用软引用+静态方式handler使用获取验证码案列↓

//*:额外加的详细说明,可以忽略

Main.xml
一个Button按钮,设置了点击事件,设置可点击(其实可以不用设,默认是可点击)

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" tools:context="com.moliying.black.soft_weak_activity.MainActivity">    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="获取验证码"        android:id="@+id/button"        android:onClick="onclick"        android:clickable="true"        android:layout_marginTop="149dp"        android:layout_alignParentTop="true"        android:layout_centerHorizontal="true"/></RelativeLayout>

MainActivity代码:

package com.moliying.black.soft_weak_activity;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import java.lang.ref.WeakReference;public class MainActivity extends AppCompatActivity {    Button mButton;        //初始化Myhander子类对象    private Myhander mHandler = new Myhander(this);    private static final int UPDATA_FLAG = 0x2;//设置更新标记    private static final int OVER = 0x1;//设置完成标记    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);       mButton = (Button) findViewById(R.id.button);    }        //获取验证码    public void onclick(View v) {        //设置按钮被不可点击        mButton.setClickable(false);        //点击开始线程        new Thread(new Run()).start();    }    //使用弱引用+静态继承Handler    public static class Myhander extends Handler {        //把所依赖的引用对象,使用弱引用,那么系统在内存不足时会优先回收该对象        SoftReference<MainActivity> mReference;        //通过构造方法把activity传入进来        public Myhander(MainActivity activity) {            mReference= new SoftReference<MainActivity>(activity);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            MainActivity activity = mReference.get();            //如果activty退出了,这里就不应该处理Handler,所以判断是否为空            if (activity != null) {                switch (msg.what) {                    //*优化:这边把case UPDATA_FLAG:匹配比较多的放在前面,优化性能,(性能总是挤一挤就出来了)                    case UPDATA_FLAG:                        //*这里的mButton不能通过构造方法传进来设置,否则发生空指针异常,只能通过传入activity.mButton设置,                        //*因为外部的成员变量new Myhander时候会把没有findByid的Button传入进来                        // 设置相应的按钮显示的数字,msg.arg1+""这样可以把int值转换为String类型,也可以通过String.valueOf来转换                        activity.mButton.setText(msg.arg1 + "s");                        break;                    case OVER:                        activity.mButton.setText("重新获取验证码");                        //循环结束后收到结束空标记,设置按钮可以点击                        activity.mButton.setClickable(true);                        break;                }            }        }    }    //开启线程    public class Run implements Runnable {        @Override        public void run() {            for (int i = 60; i > 0; i--) {                try {                    Thread.sleep(500);                    /**                     * 这里其实可以用new Message(),但是mHandler提供了静态的obtainMessage                     * 因为内部已经new了一个Message()对象,可以重复使用该对象,避免重复创建                     * */                    Message ms = mHandler.obtainMessage();                    ms.what = UPDATA_FLAG;                    ms.arg1 = i;                    //发送带更新标记的Message对象                    mHandler.sendMessage(ms);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            //循环结束发送空标记            mHandler.sendEmptyMessage(OVER);        }    }}

布局图
如果有不对的地方或者建议朋友们都可以提出来,相互讨论,共同进步…谢谢

1 0