为什么定义一个static 定义ImageView会导致内存泄露

13630人阅读
Android(32)
更多内容,可访问个人博客
如果在Activiy中通过(Runnable)的方式定义了一个变量runnable,
final Runnable runnable = new Runnable() {
public void run() {
// ... do some work
handler.postDelayed(runnable, TimeUnit.SECONDS.toMillis(10)
因为Runnable不是static类型,所以会有一个包含Activity实例的implicit reference --- Activity.this。
如果Activity在runnable变量run之前(10s内)被finish掉了但是Activity.this仍然存在,那么Activity的对象就不会被GC回收,从而导致memory leak。
即使使用一个静态内部类,也不能保证万事大吉。
static class MyRunnable implements Runnable {
public MyRunnable(View view) {
this.view =
public void run() {
// ... do something with the view
假设在runnable执行之前,View被移除了,但是成员变量view还在继续引用它,仍然会导致memory leak。
上面的两个例子当中,导致内存泄露的两种用法分别是隐式引用(implicit reference) 和 显式引用(explicit reference)。
解决隐式引用的方法比较简单,只要使用内部非静态类(non-static inner class)或者 top-level class(在一个独立的java文件中定义的变量)就可以将隐式变为显式,从而避免内存泄露。
如果继续使用非静态内部类,那么就要在onPause的时候手动结束那些挂起的任务(pending task)。
关于如何结束任何,Handler可参考中的Canceling a pending Runnable和Canceling pending Messages。HandlerThread可参考。
解决第二个问题要用到WeakReference,WeakReference的用法可以google一下,简而言之就是:只要还有其他的stronger reference,WeakReference就可以继续引用。
static class MyRunnable implements Runnable {
private WeakReference&View&
public MyRunnable(View view) {
this.view = new WeakReference&View&(view);
public void run() {
View v = view.get();
if (v != null) {
// ... do something with the view
这样一来问题就解决了,美中不足的是每次使用view之前都要做空指针判断。另外一个比较高效的方法就是在onResume中为runnable的view赋值,在onPause中赋值为null。
private static class MyHandler extends Handler {
private TextV
public void attach(TextView view) {
this.view =
public void detach() {
public void handleMessage(Message msg) {
在继承Handler或者HandlerThread的时候,
尽量定义一个static类或者top-level类。如果用到了ui元素,一定要在Activity的生命周期接触之前释放掉。
Asynchronous Android - Steve Liles
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:884912次
积分:8818
积分:8818
排名:第2040名
原创:174篇
转载:27篇
评论:228条
阅读:11050
阅读:68902Android内存泄露优化总结 - xuanhg221的博客 - CSDN博客
Android内存泄露优化总结
Android手机给应用分配的内存通常是8兆左右,如果处理内存处理不当很容易造成OutOfMemoryError,我们的产品出现最多的错误也是OutOfMemoryError的异常,在解决这个异常时在网上发现很多关于OutOfMemoryError的原因的介绍。&
OutOfMemoryError主要由以下几种情况造成:&
1.数据库的cursor没有关闭。&&
& & 操作Sqlite数据库时,Cursor是数据库表中每一行的集合,Cursor提供了很多方法,可以很方便的读取数据库中的值,&
&&& 可以根据索引,列名等获取数据库中的值,通过游标的方式可以调用moveToNext()移到下一行&
&&& 当我们操作完数据库后,一定要记得调用Cursor对象的close()来关闭游标,释放资源。&
2.构造adapter没有使用缓存contentview。&
&&& 在继承BaseAdapter时会让我们重写getView(int position, View&& convertView, ViewGroup parent)方法,&
&&& 第二个参数convertView就是我们要用到的重用的对象&
Java代码&&
@Override&&
public&View&getView(int&position,&View&convertView,&ViewGroup&parent)&{&&
&&&&ViewHolder&vHolder&=&null;&&
&&&&&&&&&&&&&&&&&
&&&&if&(convertView&==&null)&{&&
&&&&&&&&convertView&=&inflater.inflate(...,&null);&&
&&&&&&&&&&
&&&&&&&&vHolder&=&new&ViewHolder();&&
&&&&&&&&vHolder.img=&(ImageView)&convertView.findViewById(...);&&
&&&&&&&&vHolder.tv=&(TextView)&convertView&&
&&&&&&&&&&&&&&&&.findViewById(...);&&
&&&&&&&&&&
&&&&&&&&convertView.setTag(vHolder);&&
&&&&}&else&{&&
&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&vHolder&=&(ViewHolder)&convertView.getTag();&&
&&&&vHolder.img.setImageBitmap(...);&&
&&&&vHolder.tv.setText(...);&&
&&&&return&convertV&&
static&class&ViewHolder&{&&
&&&&TextView&&&
&&&&ImageView&&&
&&& 这里只讲使用方法,具体性能测试文章请见:&
&&& ListView中getView的原理+如何在ListView中放置多个item&
&&& /xiaowenji/archive//1900579.html&
&&& Android开发之ListView适配器(Adapter)优化&
&&& /blog/1231511&
3.调用registerReceiver()后未调用unregisterReceiver().&
&&&& 广播接收者(BroadcastReceiver)经常在应用中用到,可以在多线程任务完成后发送广播通知UI更新,也可以接收系统广播实现一些功能&
&&&& 可以通过代码的方式注册:&
&&& IntentFilter postFilter = new IntentFilter();&
&&& postFilter.addAction(getPackageName() + &.background.job&);&
&&& this.registerReceiver(receiver, postFilter);&
&&& 当我们Activity中使用了registerReceiver()方法注册了BroadcastReceiver,一定要在Activity的生命周期内调用unregisterReceiver()方法取消注册&
&&& 也就是说registerReceiver()和unregisterReceiver()方法一定要成对出现,通常我们可以重写Activity的onDestory()方法:&
Java代码&&
@Override&&
protected&void&onDestroy()&{&&
&&&&&&this.unregisterReceiver(receiver);&&
&&&&&&super.onDestroy();&&
4.未关闭InputStream/OutputStream。&
&&& 这个就不多说了,我们操作完输入输出流都要关闭流&
5.Bitmap使用后未调用recycle()。&
&&& 图片处理不好是造成内存溢出的又一个头号原因,(在我们的产品中也有体现),&
&&& 当我们处理完图片之后可以通过调用recycle()方法来回收图片对象&
Java代码&&
if(!bitmap.isRecycled())&&
&&&&bitmap.recycle()&&
}&&&&&&&&&&
&&& 除此之外:&
&&& 直接使用ImageView显示bitmap会占用较多资源,特别是图片较大的时候,可能导致崩溃。&
&&& 使用BitmapFactory.Options设置inSampleSize, 这样做可以减少对系统资源的要求。&
&&& 属性值inSampleSize表示缩略图大小为原始图片大小的几分之一,即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。&
&&&&&&& BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();&&
&&&&&&& bitmapFactoryOptions.inJustDecodeBounds =&&
&&&&&&& bitmapFactoryOptions.inSampleSize = 2;&&
&&&&&&& // 这里一定要将其设置回false,因为之前我们将其设置成了true&&
&&&&&&& // 设置inJustDecodeBounds为true后,decodeFile并不分配空间,即,BitmapFactory解码出来的Bitmap为Null,但可计算出原始图片的长度和宽度&&
&&&&&&& options.inJustDecodeBounds =&
&&&&&&& Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);&&
6.Context泄漏。&
&&& 这是一个很隐晦的OutOfMemoryError的情况。先看一个Android官网提供的例子:&
Java代码&&
private&static&Drawable&sB&&
@Override&&
protected&void&onCreate(Bundle&state)&{&&
&&super.onCreate(state);&&
&&TextView&label&=&new&TextView(this);&&
&&label.setText(&Leaks&are&bad&);&&
&&if&(sBackground&==&null)&{&&
&&&&sBackground&=&getDrawable(R.drawable.large_bitmap);&&
&&label.setBackgroundDrawable(sBackground);&&
&&setContentView(label);&&
&&& 这段代码效率很快,但同时又是极其错误的;&
&&& 在第一次屏幕方向切换时它泄露了一开始创建的Activity。当一个Drawable附加到一个 View上时,&
&&& View会将其作为一个callback设定到Drawable上。上述的代码片段,意味着Drawable拥有一个TextView的引用,&
&&& 而TextView又拥有Activity(Context类型)的引用,换句话说,Drawable拥有了更多的对象引用。即使Activity被 销毁,内存仍然不会被释放。&
&&& 另外,对Context的引用超过它本身的生命周期,也会导致Context泄漏。所以尽量使用Application这种Context类型。&
&&& 这种Context拥有和应用程序一样长的生命周期,并且不依赖Activity的生命周期。如果你打算保存一个长时间的对象,&
&&& 并且其需要一个 Context,记得使用Application对象。你可以通过调用Context.getApplicationContext()或 Activity.getApplication()轻松得到Application对象。&
&&& 最近遇到一种情况引起了Context泄漏,就是在Activity销毁时,里面有其他线程没有停。&
&&& 总结一下避免Context泄漏应该注意的问题:&
&&& 1.使用Application这种Context类型。&
&&& 2.注意对Context的引用不要超过它本身的生命周期。&
&&& 3.慎重的使用“static”关键字。&
&&& 4.Context里如果有线程,一定要在onDestroy()里及时停掉。&
7.static关键字&
&&& 当类的成员变量声明成static后,它是属于类的而不是属于对象的,如果我们将很大的资源对象(Bitmap,context等)声明成static,那么这些资源不会随着对象的回收而回收,&
&&& 会一直存在,所以在使用static关键字定义成员变量的时候要慎重。
我的热门文章
即使是一小步也想与你分享问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
提示说:不要把「Do not place android context classes in static fields, this is a memory leak.」
我知道activity context 不应该被放到static fields里面因为它在app生命周期内一直存在,而且如果它通过构造函数传递给其他class的话,其他class也会一直不被回收。
但为什么ImageView不能是static(我发现SwipeRefreshLayout也不行)?是因为ImageView包含context?Button和TextView之类的view就没有这种问题。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
在Activity中填充布局,可以通过如下方式:
1.view view = View.inflate(context, R.layout.test,null );
2.View view = LayoutInflater.from(context).inflate(R.layout.test,null);
可以发现,无论哪种方式,填充后的View都是持有context的引用的,也就是Activity的引用.如果此时填充的view是static修饰,那么静态的对象将持续持有Activity的引用,导致Activity无法销毁,Activity中所有的控件也都将无法彻底销毁和回收.最终造成内存泄露.
PS:为什么要使用静态View的方式?有什么特殊的业务需求?
分享到微博?
Hi,欢迎来到 SegmentFault 技术社区!⊙▽⊙ 在这里,你可以提出编程相关的疑惑,关注感兴趣的问题,对认可的回答投赞同票;大家会帮你解决编程的问题,和你探讨技术更新,为你的回答投上赞同票。
明天提醒我
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:
扫扫下载 App君,已阅读到文档的结尾了呢~~
这个是很好的介绍内存溢出和内存泄漏。
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
内存溢出和内存泄露
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口

我要回帖

更多关于 static 内存泄露 的文章

 

随机推荐