1. 程式人生 > >Android 記憶體管理記錄

Android 記憶體管理記錄

專案中用到大量大圖,造成快速切換Activity後記憶體不足,如登入介面用到3M高清大圖,裝置選擇用到5張大圖背景疊加效果,主介面用到了5張遮罩大圖,設定介面總的子activity中也有大圖出現。

啟動Splash——裝置選擇介面(保留棧低,不finish,但會把背景圖片回收)-->主介面--->設定介面-->設定子介面(有大圖背景\或者WebView(WebView記憶體洩漏 https://www.jianshu.com/p/3e8f7dbb0dc7)

在設定子介面快速返回到裝置選擇介面過程中會出現在主介面,停頓過長然後記憶體爆增加導致UI異常。

通過跟蹤除錯發現,在快速返回過程中,activity沒有及時呼叫ondestry,這就會出現圖片記憶體沒有得到釋放,而此時已經返回到了裝置選擇介面,開啟載入裝置選擇介面的大圖背景。問題發現了百度了ImageView 釋放的記憶體的關鍵字,發現前篇一律的方式都是bitmap的回收方法,但在實際中被並沒有達到效果。

最後通過檢視android 記憶體管理機制,以及andorid 弱引用和軟引用得到了啟發。

當我們直接在xml中ImageView設定了背景圖片和src時候,view中(長期持有 Context 的引用()個人理解),這樣就會出現activity解釋被finish掉了,但它所佔用的記憶體沒有得到釋放,這樣imageview中的 bitmap記憶體依然佔用著。

軟引用 和 弱引用   (引用自https://www.cnblogs.com/zhaoyanjun/p/5977190.html)
    1.  SoftReference<T>:軟引用-->當虛擬機器記憶體不足時,將會回收它指向的物件;需要獲取物件時,可以呼叫get方法。
    2.  WeakReference<T>:弱引用-->隨時可能會被垃圾回收器回收,不一定要等到虛擬機器記憶體不足時才強制回收。要獲取物件時,同樣可以呼叫get方法。
    3. WeakReference一般用來防止記憶體洩漏,要保證記憶體被虛擬機器回收,SoftReference多用作來實現快取機制(cache);

最後參考ImageLoader 寫了一個demo

package com.example.zdyl;


import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.Log;


public class MemoryCache {
      
    //Bitmap 用WeakReference<T>:弱引用-->在系統gc 過程中隨時可能會被垃圾回收器回收


    private static final String TAG = "MemoryCache";

    //bitmap 管理集合

     private Map<String, WeakReference<Bitmap>> cache =            new LinkedHashMap<String, WeakReference<Bitmap>>();
//    private Map<String, Bitmap> cache=Collections.synchronizedMap(
//            new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
    private long size=0;//current allocated size
    private long limit=1000000;//max memory in bytes


    public MemoryCache(){
        //use 25% of available heap size
        setLimit(Runtime.getRuntime().maxMemory()/4);
    }
    
    public void setLimit(long new_limit){
        limit=new_limit;
        Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
    }

   //將bitmap丟入集合
    public void put(String id, WeakReference<Bitmap> bitmap){
        try{
            cache.put(id, bitmap);
        }catch(Throwable th){
            th.printStackTrace();
        }
    }
    




    public void clear() {
        try{
            //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 
            Iterator<Entry<String, WeakReference<Bitmap>>> iter= cache.entrySet().iterator();//least recently accessed item will be the first one iterated


           while(iter.hasNext()){
                   Entry<String, WeakReference<Bitmap>> entry = iter.next();
                   WeakReference<Bitmap> tmp = entry.getValue();
               if(tmp!=null ){
               Bitmap bitmap = tmp.get();
               if(bitmap!=null && !bitmap.isRecycled()){
                   bitmap.recycle();
                       }
               tmp = null;
                       bitmap =null;
               }
           }
            cache.clear();
            size=0;
        }catch(NullPointerException ex){
            ex.printStackTrace();
        }
    }


    long getSizeInBytes(Bitmap bitmap) {
        if(bitmap==null)
            return 0;
        return bitmap.getRowBytes() * bitmap.getHeight();
    }

    //移除,釋放指定bitmap
    public void removeCache(int id) {

        WeakReference<Bitmap> tmp = cache.get(""+id);
        //系統gc可能已經將bitmap 記憶體釋放了,需要判斷null    

        if(tmp!=null ){

            Bitmap bitmap = tmp.get();
            if(bitmap!=null && !bitmap.isRecycled()){
                bitmap.recycle();
            }
            cache.remove(tmp);
            tmp = null;
            bitmap =null;
        }
    }
}
package com.example.zdyl;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.os.Handler;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;

//讀取res中的bitmap 設定到wiew中
public class ImageLoader {
    
    static MemoryCache memoryCache=new MemoryCache();
    public static boolean isFirst;
    
    public ImageLoader(Context context){
       
    }
    
    public static void decode(Resources context,int id,View view){
    Bitmap bmp=BitmapFactory.decodeResource(context, id);
    if(view instanceof ImageView){
    ((ImageView) view).setImageBitmap(bmp);
    }else{
    view.setBackground(new BitmapDrawable(bmp));
    }


        // 軟引用的Bitmap物件,這樣系統會在gc過程中回收掉bitmap,直接使用強引用物件的話,即使呼叫了bitmap的回收函式       //但是imageview 中bitmap在gc的時候跟隨了activity的記憶體,如果activity因為context物件的長期被引用,那麼bitmap的記憶體就得不到銷燬WeakReference<Bitmap> softBitmap = new WeakReference<Bitmap>(bmp);
        // 新增該物件到Map中使其快取
    	   memoryCache.put(""+view.getId(),softBitmap );
        bmp = null;
    }


    public static void removeCache(int id){
        memoryCache.removeCache(id);


    }


    public static void clearCache() {
        memoryCache.clear();
    }


}

 

package com.example.zdyl;


import java.util.ArrayList;
import java.util.List;


import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;


public class MainActivity extends Activity {


    private RelativeLayout t;
private ImageView t1;
private ImageView t2;
private ImageView t3;
private ImageView t4;
private ImageView t5,t6,t7,t8,t9;
private MemoryCache mMemoryCache=new MemoryCache();

private List<View> views = new ArrayList<View>();

//由於圖片用的是公司專案中的圖片,這裡就不提供了,見諒,可以找幀動畫中圖片資源來代替。
private int[] icons ={iconframe_001,iconframe_002,

iconframe_003,iconframe_004,
iconframe_005,iconframe_006,
            iconframe_007,iconframe_008,
            iconframe_009,iconframe_010};
private ImageLoader loader;
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        t = (RelativeLayout)findViewById(R.id.t0);
        t1 = (ImageView)findViewById(R.id.t1);
        t2 = (ImageView)findViewById(R.id.t2);
        t3 = (ImageView)findViewById(R.id.t3);
        t4 = (ImageView)findViewById(R.id.t4);
        t5 = (ImageView)findViewById(R.id.t5);


        t6 = (ImageView)findViewById(R.id.t6);
        t7 = (ImageView)findViewById(R.id.t7);
        t8 = (ImageView)findViewById(R.id.t8);
        t9 = (ImageView)findViewById(R.id.t9);


        views.add(t);
        views.add(t1);
        views.add(t2);
        views.add(t3);
        views.add(t4);
        views.add(t5);
        views.add(t6);
        views.add(t7);
        views.add(t8);
        views.add(t9);

        loader = new ImageLoader(this);

        //因為要管理bitmap物件,所以沒有使用xml中直接設定背景和src

for(int i=0;i<views.size();i++){

        loader.decode(this.getResources(), icons[i], views.get(i));
        }
    }




    public void d(){
        //loader.clearCache();
        for (int i = 0; i < views.size(); i++) {
            View view = views.get(i);
            if (view instanceof ImageView) {
                ((ImageView) view).setImageBitmap(null);
            } else {
                view.setBackground(null);
            }
            ImageLoader.removeCache(view.getId());
        }
        views.clear();
    }


public void onClear(View v){


}
    public void onClearc(View v) {
    if(this.isFinishing()){
        return;
        }
        Intent i = new Intent(MainActivity.this,MainActivity2.class);
        startActivity(i);
        if(!ImageLoader.isFirst){
            ImageLoader.isFirst = true;
            return;
        }
        finish();


    }


    @Override
    public void finish() {
        super.finish();
        d();
    }

}

demo傳輸門(只包含as專案src 部分,):https://download.csdn.net/download/zdy10326621/10496926