Handler的正确使用,使用静态内部类+弱引用,解决内存泄漏,举例说明在使用ProgressBar时的Handler类的静态内部类实现。

       在Android中使用ProgressBar进行回调设置进度时候会提示‘This Handler class should be static or leaks might occur’的警告,那就说明是你的自定义Handler类有内存泄露的问题,一般来说就是持有外部类的引用。要解决这个ProgressBar的使用问题需要明确如下几个知识点:


1.Android的UI(比如ProgressBar)也是线程不安全的,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则会出现异常。

2.不能在UI主线程中使用Thread.sleep();会导致视图变化看不到(后面做ProgressBar演示需要用到)。

3.静态内部类是不持有外部类的引用,因此自定义Handler类可以用静态内部类来完成,至于为什么不持有外部类的引用可参考:非静态内部类、非静态匿名内部类会持有外部对象的引用

4.如果你想在静态内部类/匿名类中使用外部类的属性或方法时,可以显示的持有一个弱引用(WeakReference )。



  • 强引用:Java中里面最广泛的使用的一种,也是对象默认的引用类型,如果又一个对象具有强引用,那么垃圾回收器是不会对它进行回收操作的,当内存的空间不足的时候,Java虚拟机将会抛OutOfMemoryError错误,这时应用将会被终止运行
  • 软引用:一个对象如果只有一个软引用,那么当内存空间充足是,垃圾回收器不会对他进行回收操作,只有当内存空间不足的时候,这个对象才会被回收,软引用可以用来实现内存敏感的高速缓存,如果配合引用队列(ReferenceQueue使用,当软引用指向对象被垃圾回收器回收后,java会把这个软引用加入到与之关联的引用队列中)
  • 弱引用:弱引用是比软引用更弱的一种的引用的类型,只有弱引用指向的对象的生命周期更短,当垃圾回收器扫描到只有具有弱引用的对象的时候,不敢当前空间是否不足,都会对弱引用对象进行回收,当然弱引用也可以和一个队列配合着使用
  • 引用队列:ReferenceQueue一般是作为WeakReference SoftReference 的构造的函数参数传入的,在WeakReference 或者是 softReference 的指向的对象被垃圾回收后,ReferenceQueue就是用来保存这个已经被回收的Reference

下面给大家展示用静态内部类实现Handler类来完成ProgressBar的进度条显示的源码。

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
   
    private ProgressBar progressBar;
    
    private MyHandler handler = new MyHandler(this);
    
    /**
     * 静态内部类实现Handler
     */
  
    private static class MyHandler extends Handler{

        private  WeakReference mActivity;

        private MyHandler(MainActivity mActivity) {
            super();
            this.mActivity = new WeakReference(mActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            if(mActivity.get() == null){
                return;
            }

            //在静态内部类/匿名类中使用外部类的属性或方法时,可以显示的持有一个弱引用
            final MainActivity activity = mActivity.get(); //弱引用
            activity.progress.setText(msg.arg1+"%");
                
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        progressBar = findViewById(R.id.progress_bar); //实例化ProgressBar
       
        Button button = findViewById(R.id.button);
       
         button.setOnClickListener(this); //启动进度条的按钮添加点击事件
        
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.button){
            new Thread(new Runnable() {
                @Override
                public void run() {
                   int progress = progressBar.getProgress();
                   while (progress < 100) {
                       try {
                         
                           Message message = new Message();
                           progress = progress + 1;
                           progressBar.setProgress(progress);
                           message.arg1 = progress;
                           handler.sendMessage(message);
                           Thread.sleep(100); //不能在主线程中使用
                       } catch (InterruptedException e) {
                           e.printStackTrace();
                       }
                   }
                }
            }).start();
         
        }
   }
}

当然只是针对ProgressBar的显示而又不想发生‘This Handler class should be static or leaks might occur’我们还可以使用Android提供的runOnUiThread来完成。源码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
   
    private ProgressBar progressBar;

    private int pro;   //进度条的值
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        progressBar = findViewById(R.id.progress_bar); //实例化ProgressBar
       
        Button button = findViewById(R.id.button);
       
         button.setOnClickListener(this); //启动进度条的按钮添加点击事件
        
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.button){
            pro = progressBar.getProgress();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (pro <= 100) {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            runOnUiThread(new Runnable() {//切换回主线程更改ProgressBar的值
                                @Override
                                public void run() {
                                      progress.setText(String.format("%d%%", pro));
                                    
                                    }
                                    pro = pro + 1;
                                }
                            });
                        }
                    }
                }).start();
        }
   }
}

小弟也是最近再跟着郭霖大牛的《第一行代码》在学习android,纯纯的菜鸟一枚,对很多东西的认识不深,知其然不知其所以然,这也是我的第一篇博文,写的不好,有错误的地方希望各位大神包含和指正,小弟感激不尽!

你可能感兴趣的:(Android)