Android中使用Handler引发的内存泄露的问题?

中才德创 2016-03-12 07:02:30
This Handler class should be static or leaks might occur相信很多人见过,可为什么却很含糊。

下面的代码,一直不理解为什么就不报警,难道因此不会发生内存泄漏了吗?
/**
* avoid 'This Handler class should be static or leaks might occur' warning
*/
private static class weakreferenceHandler extends Handler {
}

/**
* handler
*/
private final Handler mHandler = new weakreferenceHandler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_RUNNABLE_BEGIN1:
onChangeText("thread1 begin " + msg.arg1);
break;

case MSG_USER_RUNNABLE_END1:
onChangeText("thread1 end " + msg.arg1);
break;

case MSG_USER_RUNNABLE_BEGIN2:
onChangeText("thread2 begin " + msg.arg1);
break;

case MSG_USER_RUNNABLE_END2:
onChangeText("thread2 end " + msg.arg1);
break;

case MSG_USER_RUNNABLE_BEGIN3:
onChangeText("thread3 begin " + msg.arg1);
break;

case MSG_USER_RUNNABLE_END3:
onChangeText("thread3 end " + msg.arg1);
break;
}
}
};
...全文
623 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
clbeyond 2017-12-18
  • 打赏
  • 举报
回复
楼上的是明白人,这么多年,只有你说对了,其他人都没有好好理解。
wanli_smile 2017-09-21
  • 打赏
  • 举报
回复
我觉得大家都在误区吧。首先是在activity里面的handler,你能用来干啥?无非就是在其他非UI线程里面,来操作UI。本身规范的话,是不会用来做耗时的操作的。再就是即使有activity退出后,handler还在处理任务,除非你设置的delay的时间非常长,或者自己在handlemessage乱做了啥,一般handler都能处理完毕吗,顶多算的是 延迟让系统GC 掉这个activity。不要解决handle有内存泄露,肯定没有 。自己乱使用,不要乱怪handler!
中才德创 2016-03-15
  • 打赏
  • 举报
回复
Demo测试了,使用弱引用,与不使用弱引用,测试结果相同。反而,mHandler.removeCallbacksAndMessages(null)是真正有用的。 代码如下:
package com.example.testweakreference.activity;

import java.lang.ref.WeakReference;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import com.example.testweakreference.R;

public class SlaveActivity extends Activity {
	private static final String TAG = "SlaveActivity";
	private static final int WM_USER_WAITINGG = 1;
	private final long WAIT_TIME1 = 30000;
	private final long WAIT_TIME2 = 60000;
	
	private TextView mTvGC;
	private Button mBtnPostMessage;
	TestGC testGC1;
	TestGC testGC2;
	private int i = 0;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.v(TAG, "onCreate is called");
		setContentView(R.layout.activity_slave);
		iniUI();
		iniData();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	/**
	 * ini ui
	 */
	private void iniUI() {
		mTvGC = (TextView)findViewById(R.id.tvGC);
		mBtnPostMessage = (Button)findViewById(R.id.btnPostMessage);
		
		mBtnPostMessage.setOnClickListener(mAnonymousOnClickListener);
	}
	
	/**
	 * ini data
	 */
	private void iniData() {
		for (i=411; i<=413; i++) {
			TestGC testGC = new TestGC(i);
			Log.v(TAG, "TestGC value : " + testGC.joke);
		}
		
		testGC1 = new TestGC(94411);
		testGC2 = new TestGC(94412);
		Log.v(TAG, "testGC1 value : " + testGC1.joke);
		Log.v(TAG, "testGC2 value : " + testGC2.joke);
	}
	
	@Override
	protected void onStart() {
		super.onStart();
		Log.v(TAG, "onStart is called");
	};
	
	@Override
	protected void onResume() {
		super.onResume();
		Log.v(TAG, "onResume is called");
	};
	
	@Override
	protected void onPause() {
		super.onPause();
		Log.v(TAG, "onPause is called");
	};
	
	@Override
	protected void onStop() {
		super.onStop();
		Log.v(TAG, "onStop is called");
	};
	
	@Override
	protected void onRestart() {
		super.onRestart();
		Log.v(TAG, "onRestart is called");
	};
	
	@Override
	protected void onDestroy() {
		super.onStart();
		Log.v(TAG, "onDestroy is called");
		//mHandler.removeCallbacksAndMessages(null);
	};
	
	@Override
	protected void finalize() {
		try {
			super.finalize();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			Log.v(TAG, "finalize is called");
		}
	};
	
	private class TestGC {
		int joke;
		
		private TestGC(int j) {
			joke = j;
		}
		
		@Override
		protected void finalize() {
			// TODO Auto-generated method stub
			try {
			super.finalize();
			} catch (Throwable e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				Log.v(TAG, "TestGC finalize is called : " + joke);
			}
		}
	}
	
	/**
	 * anonymous inner class
	 */
	private final OnClickListener mAnonymousOnClickListener = new OnClickListener() {
		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			switch(arg0.getId()) {
			case R.id.btnPostMessage: {
				mTvGC.setText("btnPostMessage clicked ");
				mHandler.postDelayed(mRunnable, WAIT_TIME1);
				mHandler.sendEmptyMessageDelayed(WM_USER_WAITINGG, WAIT_TIME2);
			}
				break;
				
			default:
				break;
			}
		}
	};
	
	private Runnable mRunnable = new Runnable() {
		@Override
		public void run() {
			// TODO Auto-generated method stub
			Log.v(TAG, "Runnable is called : " + testGC1.joke);
		}
	};
	
	private MyHandler mHandler = new MyHandler(this);
	private static class MyHandler extends Handler {
		private SlaveActivity mActivity;
		
		/**
		 * @param activity
		 */
		public MyHandler(SlaveActivity activity) {
			WeakReference<SlaveActivity> WeakReference = new WeakReference<SlaveActivity>(activity);
			mActivity = WeakReference.get();
	    }
		
		/**
	     * Subclasses must implement this to receive messages.
	     */
		@Override
	    public void handleMessage(Message msg) {
			switch (msg.what) {
			case WM_USER_WAITINGG:
				Log.v(TAG, "WM_USER_WAITINGG message is doing : " + mActivity.testGC2.joke);
				break;
				
			default:
				break;
			}
	    }
	}
	
//	private Handler mHandler = new Handler() {
//		/**
//	     * Subclasses must implement this to receive messages.
//	     */
//		@Override
//	    public void handleMessage(Message msg) {
//			switch (msg.what) {
//			case WM_USER_WAITINGG:
//				Log.v(TAG, "WM_USER_WAITINGG message is doing : " + testGC2.joke);
//				break;
//				
//			default:
//				break;
//			}
//	    }
//	};
}
中才德创 2016-03-15
  • 打赏
  • 举报
回复
根据写的demo测试结果,即便非静态的Handler实例有未处理的消息,Activity销毁还是会释放new出来变量,但有一部分变量被Handler消息挂住的,暂时不能释放。一旦消息处理了,就能够释放了。 其实很多人是照本宣科:隐式持有了Activity的引用,出现内存泄露。说得玄之又玄! 实际测试的结果显示,与其叫“内存泄露”,不如叫“被Handler挂住的那部分变量,要待Handler处理完毕,才能释放”。 所以,解决方法有好几种: 1)最简单的就是,在Activity销毁前,调用mHandler.removeCallbacksAndMessages(null)。提前结束Handler处理。【这个经过测试,OK】 2)就是使用弱引用。将this传进去,用WeakReference告诉GC,它是弱引用。【这个未经过测试,但我很怀疑,它如果可以提前释放,那这个引用所使用到的Activity里的变量,到底会是个什么值?】
private MyHandler mHandler = new MyHandler(this);
	private static class MyHandler extends Handler {
		private MainActivity mActivity;
		
		/**
		 * @param activity
		 */
		public MyHandler(MainActivity activity) {
			WeakReference<MainActivity> WeakReference = new WeakReference<MainActivity>(activity);
			mActivity = WeakReference.get();
	    }
只为搞笑 2016-03-15
  • 打赏
  • 举报
回复
引用 6 楼 HawkOfWinter 的回复:
Handler之所以会导致内存泄露是因为Handler其实隐式的持有了Activity的引用,如果handler进行的是比较耗时的操作,有可能出现activity结束了,而这个操作没有结束,本来activity结束后就会被回收,但因为这个activity还有引用被持有,导致不能回收,出现了内存泄露。一般来说解决这个问题都是把这个handler持有的activity引用变成弱引用,这样activity被销毁后,它就可以被回收,从而避免出现内存泄露。 1)handler进行的是比较耗时的操作 -- 那要是时间够了,操作结束了,会回收吗? 2)弱引用,可回收 -- 那弱引用与强引用,到底区别在哪呢? 其实,看大家的说法,至少没讲明白究竟为什么的?只是在讲点概念!
这个我只能给你说个模糊的概念,其实这些网上都有很详细的说明,也不知道为什么你硬要发一个问题。 java虚拟机在回收内存时,就会去看看这个对象被哪些引用着,如果是强引用,则不会回收,但是如果只被弱引用持有,则依然会回收。至于你的第一个问题,操作结束了,也是要看这个activity有没有被持有,才会选择是否进行回收。java回收机制也不是时时刻刻都回收这么简单,深入了解,多查资料。别当伸手党。
中才德创 2016-03-15
  • 打赏
  • 举报
回复
我写了下面的一个测试程序,发现,SlaveActivity 退出后,调用System,gc()还是会释放的,还是会打印出finalize函数的释放过程。(也就是说不影响内存回收,就不知道所谓的handler进行的是比较耗时的操作是什么了。) 所谓的handler进行的是比较耗时的操作,这里也模拟了,也就是mHandler.sendEmptyMessageDelayed(WM_USER_WAITINGG, WAIT_TIME)有个30秒的延时消息。 难道说,handler进行的是比较耗时的操作是指它本身在耗cpu,那不是卡死SlaveActivity 界面了吗?
package com.example.testweakreference.activity;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import com.example.testweakreference.R;

public class SlaveActivity extends Activity {
	private static final String TAG = "SlaveActivity";
	private static final int WM_USER_WAITINGG = 1;
	private final int WAIT_TIME = 30000;
	
	private TextView mTvGC;
	private Button mBtnPostMessage;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.v(TAG, "onCreate is called");
		setContentView(R.layout.activity_slave);
		iniUI();
		iniData();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	/**
	 * ini ui
	 */
	private void iniUI() {
		mTvGC = (TextView)findViewById(R.id.tvGC);
		mBtnPostMessage = (Button)findViewById(R.id.btnPostMessage);
		
		mBtnPostMessage.setOnClickListener(mAnonymousOnClickListener);
	}
	
	/**
	 * ini data
	 */
	private void iniData() {
		for (int i=0; i<=413; i++) {
			TestGC testGC = new TestGC(i);
			Log.v(TAG, "TestGC value : " + testGC.joke);
		}
	}
	
	@Override
	protected void onStart() {
		super.onStart();
		Log.v(TAG, "onStart is called");
	};
	
	@Override
	protected void onResume() {
		super.onResume();
		Log.v(TAG, "onResume is called");
	};
	
	@Override
	protected void onPause() {
		super.onPause();
		Log.v(TAG, "onPause is called");
	};
	
	@Override
	protected void onStop() {
		super.onStop();
		Log.v(TAG, "onStop is called");
	};
	
	@Override
	protected void onRestart() {
		super.onRestart();
		Log.v(TAG, "onRestart is called");
	};
	
	@Override
	protected void onDestroy() {
		super.onStart();
		Log.v(TAG, "onDestroy is called");
	};
	
	@Override
	protected void finalize() {
		try {
			super.finalize();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			Log.v(TAG, "finalize is called");
		}
	};
	
	private class TestGC {
		int joke;
		
		private TestGC(int j) {
			joke = j;
		}
		
		@Override
		protected void finalize() {
			// TODO Auto-generated method stub
			try {
			super.finalize();
			} catch (Throwable e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				Log.v(TAG, "TestGC finalize is called : " + joke);
			}
		}
	}
	
	/**
	 * anonymous inner class
	 */
	private final OnClickListener mAnonymousOnClickListener = new OnClickListener() {
		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			switch(arg0.getId()) {
			case R.id.btnPostMessage: {
				mTvGC.setText("btnPostMessage clicked ");
				mHandler.sendEmptyMessageDelayed(WM_USER_WAITINGG, WAIT_TIME);
			}
				break;
				
			default:
				break;
			}
		}
	};
	
	private Handler mHandler = new Handler() {
		/**
	     * Subclasses must implement this to receive messages.
	     */
		@Override
	    public void handleMessage(Message msg) {
			switch (msg.what) {
			case WM_USER_WAITINGG:
				Log.v(TAG, "WM_USER_WAITINGG message is done");
				break;
				
			default:
				break;
			}
	    }
	};
}
只为搞笑 2016-03-14
  • 打赏
  • 举报
回复
这也是为什么加上static后,编译器就不会警告的原因。
只为搞笑 2016-03-14
  • 打赏
  • 举报
回复
引用 1 楼 u013545836 的回复:
这种写法并没有鸟用,Handler之所以会导致内存泄露是因为Handler其实隐式的持有了Activity的引用,如果handler进行的是比较耗时的操作,有可能出现activity结束了,而这个操作没有结束,本来activity结束后就会被回收,但因为这个activity还有引用被持有,导致不能回收,出现了内存泄露。一般来说解决这个问题都是把这个handler持有的activity引用变成弱引用,这样activity被销毁后,它就可以被回收,从而避免出现内存泄露。而看你贴出的代码,并没有建立这个弱引用,所以最多只能避免ide出现警告,真正的风险还是存在的。真正避免应该是这么写的(代码是别人的 http://blog.csdn.net/anialy/article/details/9300677) 注意这个 WeakReference<MainActivity> mActivity; 这个就是弱引用
private static class MyHandler extends Handler {  
        WeakReference<MainActivity> mActivity;  
          
        MyHandler(MainActivity mActivity){  
            this.mActivity = new WeakReference<MainActivity>(mActivity);  
        }  
          
        @Override  
        public void handleMessage(Message msg) {  
            switch(msg.what){  
            case IMAGE_FAILURE:  
                Toast.makeText(mActivity.get()  
                        , "Image Failure", Toast.LENGTH_LONG).show();  
                break;  
            case IMAGE_SUCCESS:  
                Log.i(TAG, "success");  
                FileInputStream fis = null;  
                byte[] bytes = new byte[1024];  
                ByteArrayOutputStream baos = new ByteArrayOutputStream();  
                int len;  
                try {  
                    File sdCardDir = Environment.getExternalStorageDirectory();  
                    File imgFile = new File(sdCardDir, "image/xxx");  
                    fis =  new FileInputStream(imgFile);  
                    while( (len=fis.read(bytes))!=-1 ){  
                        baos.write(bytes, 0, len);  
                    }  
                } catch (FileNotFoundException e) {  
                    Log.i(TAG, e.toString());  
                } catch (IOException e) {  
                    Log.i(TAG, e.toString());  
                }  
                Bitmap bMap = BitmapFactory.decodeByteArray(  
                        baos.toByteArray(), 0, baos.toByteArray().length);   
                mActivity.get().iv.setImageBitmap(bMap);  
                try {  
                    baos.flush();  
                    baos.close();  
                } catch (IOException e) {  
                    Log.i(TAG, e.toString());  
                }  
            }  
        }  
    }  
好像是内部类会隐式持有,所以加上static,静态类,就不会隐式持有activity,但是内部静态类又不能直接地用activity中的成员变量,所以要传进去,使用弱引用。
中才德创 2016-03-14
  • 打赏
  • 举报
回复
Handler之所以会导致内存泄露是因为Handler其实隐式的持有了Activity的引用,如果handler进行的是比较耗时的操作,有可能出现activity结束了,而这个操作没有结束,本来activity结束后就会被回收,但因为这个activity还有引用被持有,导致不能回收,出现了内存泄露。一般来说解决这个问题都是把这个handler持有的activity引用变成弱引用,这样activity被销毁后,它就可以被回收,从而避免出现内存泄露。 1)handler进行的是比较耗时的操作 -- 那要是时间够了,操作结束了,会回收吗? 2)弱引用,可回收 -- 那弱引用与强引用,到底区别在哪呢? 其实,看大家的说法,至少没讲明白究竟为什么的?只是在讲点概念!
小白在此 2016-03-14
  • 打赏
  • 举报
回复
引用 2 楼 u010668114 的回复:
[quote=引用 1 楼 u013545836 的回复:] 这种写法并没有鸟用,Handler之所以会导致内存泄露是因为Handler其实隐式的持有了Activity的引用,如果handler进行的是比较耗时的操作,有可能出现activity结束了,而这个操作没有结束,本来activity结束后就会被回收,但因为这个activity还有引用被持有,导致不能回收,出现了内存泄露。一般来说解决这个问题都是把这个handler持有的activity引用变成弱引用,这样activity被销毁后,它就可以被回收,从而避免出现内存泄露。而看你贴出的代码,并没有建立这个弱引用,所以最多只能避免ide出现警告,真正的风险还是存在的。真正避免应该是这么写的(代码是别人的 http://blog.csdn.net/anialy/article/details/9300677) 注意这个 WeakReference<MainActivity> mActivity; 这个就是弱引用
private static class MyHandler extends Handler {  
        WeakReference<MainActivity> mActivity;  
          
        MyHandler(MainActivity mActivity){  
            this.mActivity = new WeakReference<MainActivity>(mActivity);  
        }  
          
        @Override  
        public void handleMessage(Message msg) {  
            switch(msg.what){  
            case IMAGE_FAILURE:  
                Toast.makeText(mActivity.get()  
                        , "Image Failure", Toast.LENGTH_LONG).show();  
                break;  
            case IMAGE_SUCCESS:  
                Log.i(TAG, "success");  
                FileInputStream fis = null;  
                byte[] bytes = new byte[1024];  
                ByteArrayOutputStream baos = new ByteArrayOutputStream();  
                int len;  
                try {  
                    File sdCardDir = Environment.getExternalStorageDirectory();  
                    File imgFile = new File(sdCardDir, "image/xxx");  
                    fis =  new FileInputStream(imgFile);  
                    while( (len=fis.read(bytes))!=-1 ){  
                        baos.write(bytes, 0, len);  
                    }  
                } catch (FileNotFoundException e) {  
                    Log.i(TAG, e.toString());  
                } catch (IOException e) {  
                    Log.i(TAG, e.toString());  
                }  
                Bitmap bMap = BitmapFactory.decodeByteArray(  
                        baos.toByteArray(), 0, baos.toByteArray().length);   
                mActivity.get().iv.setImageBitmap(bMap);  
                try {  
                    baos.flush();  
                    baos.close();  
                } catch (IOException e) {  
                    Log.i(TAG, e.toString());  
                }  
            }  
        }  
    }  
好像是内部类会隐式持有,所以加上static,静态类,就不会隐式持有activity,但是内部静态类又不能直接地用activity中的成员变量,所以要传进去,使用弱引用。[/quote] 果然,应该是这样
bdmh 2016-03-14
  • 打赏
  • 举报
回复
static,静态类,解决handler内存泄露问题,就是声明为static类,这样他就不会持有外部类
小白在此 2016-03-13
  • 打赏
  • 举报
回复
这种写法并没有鸟用,Handler之所以会导致内存泄露是因为Handler其实隐式的持有了Activity的引用,如果handler进行的是比较耗时的操作,有可能出现activity结束了,而这个操作没有结束,本来activity结束后就会被回收,但因为这个activity还有引用被持有,导致不能回收,出现了内存泄露。一般来说解决这个问题都是把这个handler持有的activity引用变成弱引用,这样activity被销毁后,它就可以被回收,从而避免出现内存泄露。而看你贴出的代码,并没有建立这个弱引用,所以最多只能避免ide出现警告,真正的风险还是存在的。真正避免应该是这么写的(代码是别人的 http://blog.csdn.net/anialy/article/details/9300677) 注意这个 WeakReference<MainActivity> mActivity; 这个就是弱引用
private static class MyHandler extends Handler {  
        WeakReference<MainActivity> mActivity;  
          
        MyHandler(MainActivity mActivity){  
            this.mActivity = new WeakReference<MainActivity>(mActivity);  
        }  
          
        @Override  
        public void handleMessage(Message msg) {  
            switch(msg.what){  
            case IMAGE_FAILURE:  
                Toast.makeText(mActivity.get()  
                        , "Image Failure", Toast.LENGTH_LONG).show();  
                break;  
            case IMAGE_SUCCESS:  
                Log.i(TAG, "success");  
                FileInputStream fis = null;  
                byte[] bytes = new byte[1024];  
                ByteArrayOutputStream baos = new ByteArrayOutputStream();  
                int len;  
                try {  
                    File sdCardDir = Environment.getExternalStorageDirectory();  
                    File imgFile = new File(sdCardDir, "image/xxx");  
                    fis =  new FileInputStream(imgFile);  
                    while( (len=fis.read(bytes))!=-1 ){  
                        baos.write(bytes, 0, len);  
                    }  
                } catch (FileNotFoundException e) {  
                    Log.i(TAG, e.toString());  
                } catch (IOException e) {  
                    Log.i(TAG, e.toString());  
                }  
                Bitmap bMap = BitmapFactory.decodeByteArray(  
                        baos.toByteArray(), 0, baos.toByteArray().length);   
                mActivity.get().iv.setImageBitmap(bMap);  
                try {  
                    baos.flush();  
                    baos.close();  
                } catch (IOException e) {  
                    Log.i(TAG, e.toString());  
                }  
            }  
        }  
    }  

80,349

社区成员

发帖
与我相关
我的任务
社区描述
移动平台 Android
androidandroid-studioandroidx 技术论坛(原bbs)
社区管理员
  • Android
  • yechaoa
  • 失落夏天
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧