0

我有一個加載並顯示較大位圖的活動。冒險的生意擺在首位,但我們在使用BitmapFactory使用下面的標準方法加載:Android內存泄漏導致方向更改和垃圾回收的性質

private static Bitmap decodeSampledBitmapFromFile(String filepath, int reqWidth,int orientation) { 
    final BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inJustDecodeBounds = true; 
    BitmapFactory.decodeFile(filepath,options); 
    if(orientation==0){ 
    options.inSampleSize = calculateInSampleSizeWidth(options, reqWidth); 
    // Decode bitmap with inSampleSize set 
    options.inJustDecodeBounds = false; 
    Bitmap d = BitmapFactory.decodeFile(filepath,options); 
    return Bitmap.createScaledBitmap(d, reqWidth, d.getHeight()*reqWidth/d.getWidth(), true); 
    } 
    else{ 
     options.inSampleSize = calculateInSampleSizeHeight(options,reqWidth); 
     options.inJustDecodeBounds = false; 
     Bitmap d = BitmapFactory.decodeFile(filepath,options); 
     return Bitmap.createScaledBitmap(d, d.getWidth()*reqWidth/d.getHeight(), reqWidth, true); 
    } 
} 
private static int calculateInSampleSizeWidth(BitmapFactory.Options options, int reqWidth) { 
    int inSampleSize = 1; 
    if (options.outWidth > reqWidth) { 
     final int halfWidth = options.outWidth/2; 
     while ((halfWidth/inSampleSize) > reqWidth) { 
      inSampleSize *= 2; 
     } 
    } 
    return inSampleSize; 
} 

該位圖的加載到ImageView的是在的AsyncTask做如下圖所示:

private static class LoadBitmapTask extends AsyncTask<String, Void, Bitmap>{ 
    private ViewSampleActivity _activity; 
    public LoadBitmapTask(ViewSampleActivity activity){ 
     this._activity = activity; 
    } 
    public void detach(){ 
     this._activity = null; 
    } 
    @Override 
    protected Bitmap doInBackground(String... params) { 
     try{ 
      String filename = params[0]; 
      Display display = _activity.getWindowManager().getDefaultDisplay(); 
      Point size = new Point(); 
      display.getSize(size); 
      int orientation = getCameraPhotoOrientation(_activity,Uri.fromFile(new File(filename)),filename); 
      Bitmap d = null; 

      d = decodeSampledBitmapFromFile(filename, (int)(((double)size.x)*0.85),orientation); 
      d = rotateBitmapAppropriately(_activity,Uri.fromFile(new File(filename)),filename,d); 
      return d; 
     } 
     catch(Exception e){ 
      return null; 
     } 
    } 

    @Override 
    protected void onPostExecute(Bitmap result){ 
     try{ 
      _activity.sampleImageView.setImageBitmap(result); 
      _activity.sampleImageView.setVisibility(View.VISIBLE); 
      _activity.sampleImageView.getLayoutParams().height = result.getHeight(); 
      _activity.sampleImageView.getLayoutParams().width = result.getWidth(); 
      _activity.viewSampleLayout.getLayoutParams().height = LayoutParams.WRAP_CONTENT; 
      _activity.hasPicture = true; 
     } 
     catch(Exception e){ 
      e.printStackTrace(); 
     } 
    } 
} 

爲了儘量避免內存泄漏,我已經重載了activity的onStop方法從AsyncTask中分離活動。

@Override 
public void onStop(){ 
    if(this.loadBitmapTask!=null){ 
     loadBitmapTask.detach(); 
    } 
    super.onStop(); 
} 

現在,我看到的奇怪行爲是,活動在第一次運行時加載得很好。方向更改後,活動將被正確重新加載。但是,經過五次左右的方向更改後,應用程序會因內存不足錯誤而崩潰。位圖似乎是明顯的罪魁禍首。我沒有保存任何ui元素或位圖或任何東西的靜態副本,所以我想知道,如果這些內存不足錯誤是由於內存泄漏。

有問題的活動是活動層次結構的最後一個。層次結構的根,主要活動,有一個帶有WeakReference的android Handler對象,也是一個管理藍牙網絡線程的持久片段。層次結構中的第二個活動是一個簡單的ListActivity。

相關活動的父活動會對Java的垃圾回收活動產生影響嗎?

+0

你是如何管理AsyncTask跨方向變化的重新附加? – 2014-11-14 15:09:46

+0

我不是,我只是運行另一個,讓舊的一個試試趕上忘記。 – user1843784 2014-11-14 15:56:59

+0

所以,如果你旋轉4次,有可能有4個AsyncTasks在那裏,每個都有自己的位圖? – 2014-11-14 15:59:32

回答

1

Bitmap d在您的decodeSampledBitmapFromFile()方法被垃圾收集?我不是100%清楚這些概念,但它似乎是從d創建縮放副本而沒有回收d本身是可疑的。

我也建議嘗試重新附加到您的AsyncTask,而不是讓它死(併產卵更多?!)。如果您使用FragmentActivity,則可以使用非配置實例。在活動中存儲對任務的引用,並將其保存爲非配置實例。然後嘗試重新連接你的活動的onCreate:

private LoadBitmapTask mTask; 

@Override 
public Object onRetainCustomNonConfigurationInstance() { 
    if(mTask != null){ 
     mTask.detach(); 
    } 
    return mTask; 
} 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    mTask = (LoadBitmapTask) getLastCustomNonConfigurationInstance(); 

    if (mTask != null) { 
     mTask.attach(this); 
    } 
} 

這樣你就可以「拿起您離開的地方」用的AsyncTask,並沒有產生更多。

+0

偉大的解決方案,但我最終沒有去,因爲在我目前的實現中,我希望根據不同的方向變化來調整大小。在我目前的實現中,這個任務的回收可能會導致錯誤的大小被加載到imageView中。我所做的是添加一個檢查,以查看在從文件加載位圖之前以及在旋轉位圖之前,對活動的處理是否爲空。 – user1843784 2014-11-14 16:26:55