內存泄露的幾種情況

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代碼  收藏代碼
  1. @Override  
  2. public View getView(int position, View convertView, ViewGroup parent) {  
  3.     ViewHolder vHolder = null;  
  4.                //如果convertView對象爲空則創建新對象,不爲空則複用  
  5.     if (convertView == null) {  
  6.         convertView = inflater.inflate(..., null);  
  7.         // 創建 ViewHodler 對象  
  8.         vHolder = new ViewHolder();  
  9.         vHolder.img= (ImageView) convertView.findViewById(...);  
  10.         vHolder.tv= (TextView) convertView  
  11.                 .findViewById(...);  
  12.         // 將ViewHodler保存到Tag中  
  13.         convertView.setTag(vHolder);  
  14.     } else {  
  15.                        //當convertView不爲空時,通過getTag()得到View  
  16.         vHolder = (ViewHolder) convertView.getTag();  
  17.     }  
  18.     // 給對象賦值,修改顯示的值  
  19.     vHolder.img.setImageBitmap(...);  
  20.     vHolder.tv.setText(...);  
  21.     return convertView;  
  22. }  
  23.        //將顯示的View 包裝成類  
  24. static class ViewHolder {  
  25.     TextView tv;  
  26.     ImageView img;  
  27. }  

    這裏只講使用方法,具體性能測試文章請見: 
    ListView中getView的原理+如何在ListView中放置多個item 
    http://www.cnblogs.com/xiaowenji/archive/2010/12/08/1900579.html 
    Android開發之ListView適配器(Adapter)優化 
    http://shinfocom.iteye.com/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代碼  收藏代碼
  1. @Override  
  2. protected void onDestroy() {  
  3.       this.unregisterReceiver(receiver);  
  4.       super.onDestroy();  
  5. }  

4.未關閉InputStream/OutputStream。 
    這個就不多說了,我們操作完輸入輸出流都要關閉流 
5.Bitmap使用後未調用recycle()。 
    圖片處理不好是造成內存溢出的又一個頭號原因,(在我們的產品中也有體現), 

    當我們處理完圖片之後可以通過調用recycle()方法來回收圖片對象 

Java代碼  收藏代碼
  1. if(!bitmap.isRecycled())  
  2. {  
  3.     bitmap.recycle()  
  4. }          

    除此之外: 
    直接使用ImageView顯示bitmap會佔用較多資源,特別是圖片較大的時候,可能導致崩潰。 
    使用BitmapFactory.Options設置inSampleSize, 這樣做可以減少對系統資源的要求。 
    屬性值inSampleSize表示縮略圖大小爲原始圖片大小的幾分之一,即如果這個值爲2,則取出的縮略圖的寬和高都是原始圖片的1/2,圖片大小就爲原始大小的1/4。 
        BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();  
        bitmapFactoryOptions.inJustDecodeBounds = true;  
        bitmapFactoryOptions.inSampleSize = 2;  
        // 這裏一定要將其設置回false,因爲之前我們將其設置成了true  
        // 設置inJustDecodeBounds爲true後,decodeFile並不分配空間,即,BitmapFactory解碼出來的Bitmap爲Null,但可計算出原始圖片的長度和寬度  
        options.inJustDecodeBounds = false; 
        Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);  
6.Context泄漏。 
    這是一個很隱晦的OutOfMemoryError的情況。先看一個Android官網提供的例子: 
Java代碼  收藏代碼
  1. private static Drawable sBackground;  
  2. @Override  
  3. protected void onCreate(Bundle state) {  
  4.   super.onCreate(state);  
  5.   
  6.   TextView label = new TextView(this);  
  7.   label.setText("Leaks are bad");  
  8.   
  9.   if (sBackground == null) {  
  10.     sBackground = getDrawable(R.drawable.large_bitmap);  
  11.   }  
  12.   label.setBackgroundDrawable(sBackground);  
  13.   
  14.   setContentView(label);  
  15. }  

    這段代碼效率很快,但同時又是極其錯誤的; 
    在第一次屏幕方向切換時它泄露了一開始創建的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關鍵字定義成員變量的時候要慎重。
發佈了7 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章