美文网首页
Android性能之 OOM 产生和解决

Android性能之 OOM 产生和解决

作者: johnnycmj | 来源:发表于2018-01-12 15:36 被阅读48次

一般而言,android中常见的OOM原因(一般都是内存泄漏引起)主要有以下几个:

  1. 数据库的cursor没有关闭。
  2. 构造adapter没有使用缓存contentview。
  3. 调用registerReceiver()后未调用unregisterReceiver().
  4. 未关闭InputStream/OutputStream。
  5. Bitmap使用后未调用recycle()。
  6. Context泄漏。
  7. static关键字等。

static关键字

  1. 第一,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。
  2. 第二、Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。
  3. 第三、使用WeakReference代替强引用。比如可以使用WeakReference<Context> mContextRef

Context泄漏

上述中包含好几个Context的泄漏。初次之外有一种就是内部类持有外部对象造成的内存泄露,常见是内部线程造成的。

由于我们的线程是Activity的内部类,所以OneThread中保存了Activity的一个引用,当OneThread的run函数没有结束 时,OneThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。

有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重,Thread只有在run函数不结束时才出现这种内 存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应 用程序无法控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题,故一般不建议将AsyncTask作为内部类 使用。

那么上述内存泄露问题应该如何解决呢?

  1. 第一、将线程的内部类,改为静态内部类。并且注意第二条。
  2. 第二、在线程内部采用弱引用保存Context引用。

bitmap内存泄露

第一、及时的销毁。

系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过java堆的限制。因此,在用完Bitmap时,要 及时的recycle掉。

  if(!bitmap.isRecycled()){
            bitmap.recycle()
  }  

此外,最好手动设置为NULL这样 还能大大的加速Bitmap的主要内存的释放。

第二、设置一定的采样率。(压缩)

有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。如下面的代码:

    private ImageView preview;  
    BitmapFactory.Options options = new BitmapFactory.Options();  
    options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一  
    Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri), null, options);  
    preview.setImageBitmap(bitmap);  

第三、巧妙的运用软引用(SoftRefrence)

有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数(特别是在Adapter中)。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。如下例:

private class MyAdapter extends BaseAdapter {   

    private ArrayList> mBitmapRefs = new ArrayList>();  
    public View getView(int i, View view, ViewGroup viewGroup) {  
        View newView = null;  
        if(view != null) {  
            newView = view;  
        } else {  
            newView =(View)mInflater.inflate(R.layout.image_view, false);  
        }   

        Bitmap bitmap = BitmapFactory.decodeFile(mValues.get(i).fileName);  
        mBitmapRefs.add(new SoftReference(bitmap));     //此处加入ArrayList  
        ((ImageView)newView).setImageBitmap(bitmap);   

        return newView;  
    }  
}   

第四 未关闭InputStream/OutputStream

这个就不多说了,我们操作完输入输出流都要关闭流

第五 调用registerReceiver()后未调用unregisterReceiver()

当我们Activity中使用了registerReceiver()方法注册了BroadcastReceiver,一定要在Activity的生命周期内调用unregisterReceiver()方法取消注册

也就是说registerReceiver()和unregisterReceiver()方法一定要成对出现,通常我们可以重写Activity的onDestory().

第六 构造adapter没有使用缓存contentview

这个现在adapter 我们要求一定要这么写

第七 数据库的cursor没有关闭

Cursor是Android查询数据后得到的一个管理数据集合的类,正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉。

所以我们使用Cursor的方式一般如下:

Cursor cursor = null;  
  try {  
    cursor = mContext.getContentResolver().query(uri,null, null,null,null);  
    if(cursor != null) {  
        cursor.moveToFirst();  
        //do something  
    }  
  } catch (Exception e) {  
    e.printStackTrace();    
  } finally {  
    if (cursor != null) {  
       cursor.close();  
    }  
}   

有一种情况下,我们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的情况,但是注意,CursorAdapter在Acivity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭。

@Override  
protected void onDestroy() {        
   if (mAdapter != null && mAdapter.getCurosr() != null) {  
       mAdapter.getCursor().close();  
   }  
   super.onDestroy();   
}   

相关文章

网友评论

      本文标题:Android性能之 OOM 产生和解决

      本文链接:https://www.haomeiwen.com/subject/ipyfoxtx.html