Android性能优化篇(三),常客Handler、Thread泄露

看了之前前2篇博客,发现排版有问题,现在不管了,有时间在看下排版,现在继续把这篇写完,例外一周都尽力更新1—3篇博客,给自己的学习历程留下脚步。

前篇中单例模式中和观察者模式中发生的泄露其实就是static变量引起的。这篇介绍下非静态发生的泄露。如下:

1、非静态内部类引起泄露

在mainactivity中有如下的内部类

	    public void loadData(){//隐士持有MainActivity实例。MainActivity.this.a
		new Thread(new Runnable() {
		    @Override
		    public void run() {
			while(true){
			    try {
				//int b=a;
				Thread.sleep(1000);
			    } catch (InterruptedException e) {
				e.printStackTrace();
			    }
			}
		    }
		}).start();
	    }


 内部类都隐式的持有了外部类的引用,因为在内部类中可以直接拿到外部类的成员变量。 
  

分析:在activty中关闭后,如果run里面有网络操作,这时候用户不想等待直接退出activity,就造成了内存泄露。直到请求结束,方法执行完,GC才可以回收内存。

解决:把方法定义成static,里面的内部类也就是static了,静态的不属于任何对象的,只属于应用程序的,这时候就不会持有外部类的引用的。要想调用外部类的实例,那就要弱引用。

2、内部类Timer的使用

//        new Timer().schedule(new TimerTask() {
//            @Override
//            public void run() {
//                while(true){
//                    try {
//                        //int b=a;
//                        Thread.sleep(1000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                }
//            }
//        }, 20000);//这个线程延迟5分钟执行
 
解决方案:activity onDestroy把timer.cancel掉然后赋空

3、Handler的使用不当

//    private Handler mHandler = new Handler(){
//        @Override
//        public void handleMessage(Message msg) {
//            super.handleMessage(msg);
//            switch (msg.what){
//                case 0:
//                    //加载数据
//                    break;
//
//            }
//        }
//    };
分析:mHandler是匿名内部类的实例,会引用外部对象MainActivity.this。如果Handler在Activity退出的时候,它可能还活着,这时候就会一直持有Activity

解决方案:代码如下

    private static class MyHandler extends Handler{
//        private MainActivity mainActivity;//直接持有了一个外部类的强引用,会内存泄露
        private WeakReference mainActivity;//设置软引用保存,当内存一发生GC的时候就会回收。

        public MyHandler(MainActivity mainActivity) {
            this.mainActivity = new WeakReference(mainActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity main =  mainActivity.get();
            if(main==null||main.isFinishing()){
                return;
            }
            switch (msg.what){
                case 0:
                    //加载数据
//                    MainActivity.this.a;//有时候确实会有这样的需求:需要引用外部类的资源。怎么办?
                    int b = main.a;
                    break;

            }
        }
    };
这里有人会想,这里用弱引用,那不是GC会随时干掉Activity嘛? 其实,你想,GC会干掉一个正在呈现的ativity嘛?肯定不会。如果其他引用这个activity的实例没有问题的话,那当activity退出的时候,就会随时被干掉了。 有时候在轮播图的用到Handler时,记得在onDestory中remove掉。

4、getViewTreeObserver()的使用

有时候需要在onCreate方法中知道某个View组件的宽度和高度等信息,而直接调用View组件的getWidth()、getHeight()、getMeasuredWidth()、getMeasuredHeight()、getTop()、getLeft()等方法是无法获取到真实值的,只会得到0。这是因为View组件布局要在onResume回调后完成。这时候getViewTreeObserver()就派上用场了

mGridView.getViewTreeObserver().addOnGlobalLayoutListener( //view 布局完成时调用,每次view改变时都会调用  
        new ViewTreeObserver.OnGlobalLayoutListener() {  
            @Override  
            public void onGlobalLayout() {  
                if (mAdapter.getNumColumns() == 0) {  
                        final int numColumns = (int) Math.floor(  
                                 mGridView.getWidth() / (mImageThumbSize + mImageThumbSpacing));  
                    if (numColumns > 0) {  
                        final int columnWidth =  
                            (mGridView.getWidth() / numColumns) - mImageThumbSpacing;  
                        mAdapter.setNumColumns(numColumns);   //设置 列数  
                            mAdapter.setItemHeight(columnWidth);  //设置 高度  
                    }  
                }  
            }  
        });  
在gridview布局完成后,根据girdview的宽和高设置adapter列数和每个item高度,但是需要注意的是OnGlobalLayoutListener可能会被多次触发,因此在得到了高度之后,要将OnGlobalLayoutListener注销掉。即在onGlobaLayout()中调用getViewTreeObserver() .removeGlobalOnLayoutListener(this);

结论:像传感器,广播,服务,之类,有注册添加的,在activity退出的时候就要考虑是否要取消注册或者remove掉,动画的时候

完结此篇,废话,废话,还是不说废话了,再接再厉!



你可能感兴趣的:(Android性能优化篇(三),常客Handler、Thread泄露)