zoukankan      html  css  js  c++  java
  • Android Training精要(六)如何防止Bitmap对象出现OOM

    1.使用AsyncTask異步加載bitmap圖片避免OOM:

     class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { 
        private final WeakReference<ImageView> imageViewReference; 
        private int data = 0; 
        public BitmapWorkerTask(ImageView imageView) { 
            // Use a WeakReference to ensure the ImageView can be garbage collected 
            imageViewReference = new WeakReference<ImageView>(imageView); 
        } 
     
        // Decode image in background. 
        @Override 
        protected Bitmap doInBackground(Integer... params) { 
            data = params[0]; 
            return decodeSampledBitmapFromResource(getResources(), data, 100, 100)); 
        } 
     
        // Once complete, see if ImageView is still around and set bitmap. 
        @Override 
        protected void onPostExecute(Bitmap bitmap) { 
            if (imageViewReference != null && bitmap != null) { 
                final ImageView imageView = imageViewReference.get(); 
                if (imageView != null) { 
                    imageView.setImageBitmap(bitmap); 
                } 
            } 
        } 
    }
    


    decodeSampledBitmapFromResource方法具体的实现见上一篇 Android Training精要(五)讀取Bitmap對象實際的尺寸和類型


    如果有多个Task执行如何處理task的幷發问题:在imageview組件中存一個AsyncTask的引用,之後用來判斷task是否執行完成:

    static class AsyncDrawable extends BitmapDrawable { 
        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; 
     
        public AsyncDrawable(Resources res, Bitmap bitmap, 
                BitmapWorkerTask bitmapWorkerTask) { 
            super(res, bitmap); 
            bitmapWorkerTaskReference = 
                new WeakReference<BitmapWorkerTask>(bitmapWorkerTask); 
        } 
     
        public BitmapWorkerTask getBitmapWorkerTask() { 
            return bitmapWorkerTaskReference.get(); 
        } 
    }
    


    在執行task之前,創建一個AsyncDrawable用來綁定imageView和加載其bitmap

    task

    public void loadBitmap(int resId, ImageView imageView) { 
        if (cancelPotentialWork(resId, imageView)) { 
            final BitmapWorkerTask task = new BitmapWorkerTask(imageView); 
            final AsyncDrawable asyncDrawable = 
                    new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); 
            imageView.setImageDrawable(asyncDrawable); 
            task.execute(resId); 
        } 
    }
    
    public static boolean cancelPotentialWork(int data, ImageView imageView) { 
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); 
     
        if (bitmapWorkerTask != null) { 
            final int bitmapData = bitmapWorkerTask.data; 
            if (bitmapData != data) { 
                // Cancel previous task 
                bitmapWorkerTask.cancel(true); 
            } else { 
                // The same work is already in progress 
                return false; 
            } 
        } 
        // No task associated with the ImageView, or an existing task was cancelled 
        return true; 
    }
    
    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { 
       if (imageView != null) { 
           final Drawable drawable = imageView.getDrawable(); 
           if (drawable instanceof AsyncDrawable) { 
               final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; 
               return asyncDrawable.getBitmapWorkerTask(); 
           } 
        } 
        return null; 
    }
    

    最後在onPostExecute方法中判斷task是否被取消或者不是同一個task

    class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { 
        ... 
        @Override 
        protected void onPostExecute(Bitmap bitmap) { 
            if (isCancelled()) { 
                bitmap = null; 
            } 
     
            if (imageViewReference != null && bitmap != null) { 
                final ImageView imageView = imageViewReference.get(); 
                final BitmapWorkerTask bitmapWorkerTask = 
                        getBitmapWorkerTask(imageView); 
                if (this == bitmapWorkerTask && imageView != null) { 
                    imageView.setImageBitmap(bitmap); 
                } 
            } 
        } 
    }
    


    2.使用LruCache構建Bitmap緩存(內存緩存)

    private LruCache<String, Bitmap> mMemoryCache; 
     
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        ... 
        // Get max available VM memory, exceeding this amount will throw an 
        // OutOfMemory exception. Stored in kilobytes as LruCache takes an 
        // int in its constructor. 
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 
     
        // Use 1/8th of the available memory for this memory cache. 
        final int cacheSize = maxMemory / 8; 
     
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { 
            @Override 
            protected int sizeOf(String key, Bitmap bitmap) { 
                // The cache size will be measured in kilobytes rather than 
                // number of items. 
                return bitmap.getByteCount() / 1024; 
            } 
        }; 
        ... 
    } 
     
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) { 
        if (getBitmapFromMemCache(key) == null) { 
            mMemoryCache.put(key, bitmap); 
        } 
    } 
     
    public Bitmap getBitmapFromMemCache(String key) { 
        return mMemoryCache.get(key); 
    }
    
    從緩存中得到bitmap.
    public void loadBitmap(int resId, ImageView imageView) { 
        final String imageKey = String.valueOf(resId); 
     
        final Bitmap bitmap = getBitmapFromMemCache(imageKey); 
        if (bitmap != null) { 
            mImageView.setImageBitmap(bitmap); 
        } else { 
            mImageView.setImageResource(R.drawable.image_placeholder); 
            BitmapWorkerTask task = new BitmapWorkerTask(mImageView); 
            task.execute(resId); 
        } 
    }
    


    之前的BitmapWorkerTask需要修改加入bitmap到緩存

    class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { 
        ... 
        // Decode image in background. 
        @Override 
        protected Bitmap doInBackground(Integer... params) { 
            final Bitmap bitmap = decodeSampledBitmapFromResource( 
                    getResources(), params[0], 100, 100)); 
            addBitmapToMemoryCache(String.valueOf(params[0]), bitmap); 
            return bitmap; 
        } 
    }
    


    3.使用硬盤緩存Use a Disk Cache

    如果加載的圖片太多或者app被系統應用打斷,內存被清掉重新加載。就需要使用硬盤上緩存,讀寫硬盤緩存比較耗時,需起線程處理

    private DiskLruCache mDiskLruCache; 
    private final Object mDiskCacheLock = new Object(); 
    private boolean mDiskCacheStarting = true; 
    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB 
    private static final String DISK_CACHE_SUBDIR = "thumbnails"; 
     
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        ... 
        // Initialize memory cache 
        ... 
        // Initialize disk cache on background thread 
        File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR); 
        new InitDiskCacheTask().execute(cacheDir); 
        ... 
    } 
     
    class InitDiskCacheTask extends AsyncTask<File, Void, Void> { 
        @Override 
        protected Void doInBackground(File... params) { 
            synchronized (mDiskCacheLock) { 
                File cacheDir = params[0]; 
                mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE); 
                mDiskCacheStarting = false; // Finished initialization 
                mDiskCacheLock.notifyAll(); // Wake any waiting threads 
            } 
            return null; 
        } 
    } 
     
    class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { 
        ... 
        // Decode image in background. 
        @Override 
        protected Bitmap doInBackground(Integer... params) { 
            final String imageKey = String.valueOf(params[0]); 
     
            // Check disk cache in background thread 
            Bitmap bitmap = getBitmapFromDiskCache(imageKey); 
     
            if (bitmap == null) { // Not found in disk cache 
                // Process as normal 
                final Bitmap bitmap = decodeSampledBitmapFromResource( 
                        getResources(), params[0], 100, 100)); 
            } 
     
            // Add final bitmap to caches 
            addBitmapToCache(imageKey, bitmap); 
     
            return bitmap; 
        } 
        ... 
    } 
     
    
    public void addBitmapToCache(String key, Bitmap bitmap) { 
        // Add to memory cache as before 
        if (getBitmapFromMemCache(key) == null) { 
            mMemoryCache.put(key, bitmap); 
        } 
        // Also add to disk cache 
        synchronized (mDiskCacheLock) { 
            if (mDiskLruCache != null && mDiskLruCache.get(key) == null) { 
                mDiskLruCache.put(key, bitmap); 
            } 
        } 
    } 
     
    public Bitmap getBitmapFromDiskCache(String key) { 
        synchronized (mDiskCacheLock) { 
            // Wait while disk cache is started from background thread 
            while (mDiskCacheStarting) { 
                try { 
                    mDiskCacheLock.wait(); 
                } catch (InterruptedException e) {} 
            } 
            if (mDiskLruCache != null) { 
                return mDiskLruCache.get(key); 
            } 
        } 
        return null; 
    } 
     
    // Creates a unique subdirectory of the designated app cache directory. Tries to use external 
    // but if not mounted, falls back on internal storage. 
    public static File getDiskCacheDir(Context context, String uniqueName) { 
        // Check if media is mounted or storage is built-in, if so, try and use external cache dir 
        // otherwise use internal cache dir 
        final String cachePath = 
                Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :  context.getCacheDir().getPath(); 
        return new File(cachePath + File.separator + uniqueName); 
    }
    


    4.MemoryCache和Fragment結合

    private LruCache<String, Bitmap> mMemoryCache; 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        RetainFragment retainFragment = 
                RetainFragment.findOrCreateRetainFragment(getFragmentManager()); 
        mMemoryCache = retainFragment.mRetainedCache; 
        if (mMemoryCache == null) { 
            mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { 
             // Initialize cache here as usual 
            } 
            retainFragment.mRetainedCache = mMemoryCache; 
        } 
      
    } 
     
    class RetainFragment extends Fragment { 
        private static final String TAG = "RetainFragment"; 
        public LruCache<String, Bitmap> mRetainedCache; 
        public RetainFragment() {} 
     
        public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) { 
            RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG); 
            if (fragment == null) { 
                fragment = new RetainFragment(); 
                fm.beginTransaction().add(fragment, TAG).commit(); 
            } 
            return fragment; 
        } 
     
        @Override 
        public void onCreate(Bundle savedInstanceState) { 
            super.onCreate(savedInstanceState); 
            setRetainInstance(true); 
        } 
    }
    




  • 相关阅读:
    垃圾收集器
    垃圾收集算法
    动态绑定
    数据库连接池原理
    分布式事务:两段式提交(最终一致性)
    C# 推箱子游戏&对战游戏
    C# 结构体和类的区别
    C# 类&结构体&枚举
    C# 哈希表&列队&栈
    C# 数组&集合&泛型集合
  • 原文地址:https://www.cnblogs.com/krislight1105/p/3748334.html
Copyright © 2011-2022 走看看