android bitmap缓存机制(下)

来源:互联网 发布:qq视频录像软件注册码 编辑:程序博客网 时间:2024/04/30 11:36

上篇文章说明了图片的加载时,要注意大小问题,和并发的处理问题

下面我们再说明一下bitmap的缓存机制

缓存有两种,一种是内存缓存,较快,但是大小有限

一种是硬盘缓存,较慢,但是空间较大

内存缓存主要是使用一个LruCache类,它可以存储一个键值对,也就是存储图片id和图片的bitmap对象

LruCache<String, Bitmap>

基本原理就是每次加载图片前,先判断是否在LruCache里面缓存了图片,有直接拿出来,没有再去加载,把加载后的图片缓存到LruCache里面用于下次的使用

private LruCache<String, Bitmap> mMemoryCache; @Overrideprotected void onCreate(Bundle savedInstanceState) {    ...    // Get memory class of this device, exceeding this amount will throw an    // OutOfMemory exception.    final int memClass = ((ActivityManager) context.getSystemService(            Context.ACTIVITY_SERVICE)).getMemoryClass();     // Use 1/8th of the available memory for this memory cache.    final int cacheSize = 1024 * 1024 * memClass / 8;     mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {        @Override        protected int sizeOf(String key, Bitmap bitmap) {            // The cache size will be measured in bytes rather than number of items.            return bitmap.getByteCount();        }    };    ...} public void addBitmapToMemoryCache(String key, Bitmap bitmap) {    if (getBitmapFromMemCache(key) == null) {        mMemoryCache.put(key, bitmap);    }} public Bitmap getBitmapFromMemCache(String key) {    return mMemoryCache.get(key);}


硬盘缓存使用的是DIskLruCache类,这是google推荐的一个类,其实本质也是跟内存缓存一样

private DiskLruCache mDiskLruCache;private final Object mDiskCacheLock = new Object();private boolean mDiskCacheStarting = true;private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MBprivate static final String DISK_CACHE_SUBDIR = "thumbnails"; @Overrideprotected 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);}

OK,缓存机制就是上面,下面再讨论一个问题,就是配置更改的处理,例如activity的重建,或者横屏,这时会重新加载图片,造成浪费,我们可以把这些图片给缓存起来

保留原来缓存的方法是:可以通过使用Fragment传递到信的Activity(活动)实例,这个Fragment可以调用setRetainInstance(true)方法保留下来。在Activity(活动)被重新创建后,你可以在上面的Fragment中访问到已经存在的缓存对象,使得图片能快加载并重新填充到ImageView对象中。

private LruCache<String, Bitmap> mMemoryCache; @Overrideprotected void onCreate(Bundle savedInstanceState) {    ...    RetainFragment mRetainFragment =            RetainFragment.findOrCreateRetainFragment(getFragmentManager());    mMemoryCache = RetainFragment.mRetainedCache;    if (mMemoryCache == null) {        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {            ... // Initialize cache here as usual        }        mRetainFragment.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();        }        return fragment;    }     @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setRetainInstance(true);    }}

最后我们再来谈论一下viepager,gridview

滑动浏览模式(Swipe View Pattern)是一种很好的浏览详细图片的方式。你可以使用ViewPager组件配合PagerAdapter(适配器)来实现这种模式。然而,更加合适的适配器是FragmentStatePagerAdapter,它可以在ViewPager退出屏幕的时候自动销毁并存储Fragments的状态,使得内存依然保留下来。

        注意:如果你只有少量的图片,并且确信它们不会超出程序的内存限制,使用常规的PagerAdapter或者FragmentPagerAdapter或许更加合适。

public class ImageDetailActivity extends FragmentActivity {    public static final String EXTRA_IMAGE = "extra_image";     private ImagePagerAdapter mAdapter;    private ViewPager mPager;     // A static dataset to back the ViewPager adapter    public final static Integer[] imageResIds = new Integer[] {            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};     @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.image_detail_pager); // Contains just a ViewPager         mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), imageResIds.length);        mPager = (ViewPager) findViewById(R.id.pager);        mPager.setAdapter(mAdapter);    }     public static class ImagePagerAdapter extends FragmentStatePagerAdapter {        private final int mSize;         public ImagePagerAdapter(FragmentManager fm, int size) {            super(fm);            mSize = size;        }         @Override        public int getCount() {            return mSize;        }         @Override        public Fragment getItem(int position) {            return ImageDetailFragment.newInstance(position);        }    }}

public class ImageDetailFragment extends Fragment {    private static final String IMAGE_DATA_EXTRA = "resId";    private int mImageNum;    private ImageView mImageView;     static ImageDetailFragment newInstance(int imageNum) {        final ImageDetailFragment f = new ImageDetailFragment();        final Bundle args = new Bundle();        args.putInt(IMAGE_DATA_EXTRA, imageNum);        f.setArguments(args);        return f;    }     // Empty constructor, required as per Fragment docs    public ImageDetailFragment() {}     @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;    }     @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        // image_detail_fragment.xml contains just an ImageView        final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);        mImageView = (ImageView) v.findViewById(R.id.imageView);        return v;    }     @Override    public void onActivityCreated(Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        final int resId = ImageDetailActivity.imageResIds[mImageNum];        mImageView.setImageResource(resId); // Load image into ImageView    }}


0 0