Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android圖片緩存處理

Android圖片緩存處理

編輯:關於Android編程

LruCache以鍵-值對的形式存儲(內部定義了一個LinkedHashMap)數據,通過new LruCache(int size)實例化,參數使指定分配給LruCache的緩存大小。

LruCache緩存大小設置

對於分配給LruCache的緩存大小,可以直接指定固定的數值,但是更好的做法應該是通過獲取最大內存(int)Runtime.getRuntime.maxMemory,然後通過返回的最大內存/int n的大小動態分配給LruCache。

LruCache的存儲和讀取

LruCache是以為鍵值對形式存儲數據,所以它的讀寫方法都和HashMap一樣,都可以通過key操作。

存儲

LruCache.put(Key,Values)

讀取

LruCache.get(Key)

通過LruCache緩存從網絡讀取的圖片資源

在開發中,經常會遇到請求網絡,從網絡讀取圖片資源的時候,如果每次都去請求讀取,很浪費資源(都會開辟子線程進行耗時操作),這樣也會影響用戶的體驗。那麼我們應該想到的是,啟動app後,只從網絡中讀取一次資源,之後如果還需要同樣的操作,就直接通過緩存讀取,這樣就可以通過LruCache類來進行操作。
構造一個工具類,用來存儲圖片到緩存和從緩存中讀取圖片
public class CustomLruCache {
    private LruCache stringBitmapLruCache;
    int maxMemory = (int) Runtime.getRuntime().maxMemory();//獲取最大內存
    int cacheSize = maxMemory / 16;//大小為最大內存的1/16
    private static CustomLruCache customLruCache;

    /**
     * 私有化構造方法
     */
    private CustomLruCache() {
        stringBitmapLruCache = new LruCache(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
    }

    /**
     * 單例模式獲取實例,保證只有一個CustomLruCache對象,同時保證只有一個CustomLruCache.stringBitmapLruCache
     *
     * @return
     */
    public static CustomLruCache getInstance() {
        if (customLruCache == null) {
            customLruCache = new CustomLruCache();
        }
        return customLruCache;
    }

    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemoryCache(key) != bitmap)//如果緩存中不存在bitmap,就存入緩存
            stringBitmapLruCache.put(key, bitmap);
    }

    public Bitmap getBitmapFromMemoryCache(String key) {
        return stringBitmapLruCache.get(key);
    }
}

從網絡讀取圖片,存儲到緩存用昨天學過的AsyncTask異步任務類來處理邏輯

AsyncTask stringVoidBitmapAsyncTask = new AsyncTask() {
            @Override
            protected Bitmap doInBackground(String... params) {
                Bitmap bitmap = null;
                try {
                    CustomLruCache customLruCache = CustomLruCache.getInstance();
                    bitmap = customLruCache.getBitmapFromMemoryCache(params[0]);
                    //先從緩存中讀取圖片,如果緩存中不存在,再請求網絡,從網絡讀取圖片添加至LruCache中
                    //啟動app後第一次bitmap為null,會先從網絡中讀取添加至LruCache,如果app沒銷毀,再執行讀取圖片操作時
                    //就會優先從緩存中讀取
                    if (bitmap == null) {
                        //從網絡中讀取圖片數據
                        URL url = new URL(params[0]);
                        bitmap = BitmapFactory.decodeStream(url.openStream());
                        //添加圖片數據至LruCache
                        customLruCache.addBitmapToMemoryCache(params[0], bitmap);
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return bitmap;
            }

            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);
                imageView.setImageBitmap(bitmap);
            }
        };
        stringVoidBitmapAsyncTask.execute(imageURL);

 

 


文件緩存-第三方類DiskLruCache

利用DiskLruCache從網絡上獲取到之後都會存入到本地緩存中,因此即使手機在沒有網絡的情況下依然能夠加載顯示圖片數據。DiskLruCache存儲的位置沒有限制,但是一般選擇存儲在context.ExternolStorageCacheDir(),即這個手機的外部存儲這個app的私有區域,即/sdcard/Android/data/應用包名/cache,因為是存儲在外部存儲私有區域,當app被卸載時,這部分的內容會被一起清除。

 

1、使用DiskLruCache

實例化DiskLruCache是通過 DiskLruCache.open(File directory, int appVersion, int valueCount, long maxSize),四個參數分別:為directory緩存的路徑;appVersion 應用版本;alueCount 指定同一個key可以對應多少個緩存文件,一般指定為1;maxSize 指定可以緩存多少字節的數據。

(1)directory 緩存路徑

 

public File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        if (isExternalStorageWritable()) {
            cachePath = context.getExternalCacheDir().getPath();//如果掛載了sdcard,獲取外部存儲私有區域路徑
        } else {
            cachePath = context.getCacheDir().getPath();//如果沒有掛載sdcard,則獲取內部存儲緩存區域
        }
        return new File(cachePath + File.separator + uniqueName);
    }

 

其中isExternalStorageWritable()是檢測手機是否掛在sdcard。

 

private boolean isExternalStorageWritable() {
        String state = Environment.getExternalStorageState();
        if (state.equals(Environment.MEDIA_MOUNTED)) {
            return true;//掛載了sdcard,返回真
        } else {
            return false;//否則返回假
        }
    }

 

因為要對外部存儲區域進行讀寫操作,所以要在androidManifest中添加相應的權限

 

 

 
    
 

 

(2)appVersion 應用版本

 

 public int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }

當版本號變更時,緩存路徑下所有數據會被清除,因為DiskLruCache認為當版本更新時,所有數據應從網絡重新獲取

 

(3)alueCount Key對應的緩存文件個數

DiskLruCache從緩存讀取文件和寫入文件到緩存時是通過 key來識別的,所以一般是指定alueCountKey為1。

(4)maxSize 緩存存儲內容的大小

可以自己指定

 

通過上面的方法可以,這下可以完整的實例化一個DiskLruCache

 

 private DiskLruCacheHelper(Context context) {
        try {
            File cacheDir = getDiskCacheDir(context, "bitmap");
            //如果文件不存在,則創建
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 

 

2、通過DiskLruCache存儲和讀取圖片

 

 

為了方便操作,我們可以封裝一個類來進行操作

 

public class DiskLruCacheHelper {
    DiskLruCache mDiskLruCache = null;
    static DiskLruCacheHelper diskLruCacheHelper;

    private DiskLruCacheHelper(Context context) {
        try {
            File cacheDir = getDiskCacheDir(context, "bitmap");
            //如果文件不存在,則創建
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static DiskLruCacheHelper getInstance(Context context) {
        if (diskLruCacheHelper == null)
            diskLruCacheHelper = new DiskLruCacheHelper(context);
        return diskLruCacheHelper;
    }

    public File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        if (isExternalStorageWritable()) {
            cachePath = context.getExternalCacheDir().getPath();//如果掛載了sdcard,獲取外部存儲私有區域路徑
        } else {
            cachePath = context.getCacheDir().getPath();//如果沒有掛載sdcard,則獲取內部存儲緩存區域
        }
        return new File(cachePath + File.separator + uniqueName);
    }

    /**
     * 檢查外部存儲是否可用
     *
     * @return
     */
    private boolean isExternalStorageWritable() {
        String state = Environment.getExternalStorageState();
        if (state.equals(Environment.MEDIA_MOUNTED)) {
            return true;//掛載了sdcard,返回真
        } else {
            return false;//否則返回假
        }
    }

    /**
     * 獲取應用版本號
     * 當版本號改變,緩存路徑下存儲的所有數據都會被清除掉,因為DiskLruCache認為
     * 當應用程序有版本更新的時候,所有的數據都應該從網上重新獲取。
     *
     * @param context
     * @return
     */
    public int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }

    /**
     * 寫入圖片數據到文件緩存
     *
     * @param imageUrl
     * @param bitmap
     */
    public void writeToCache(String imageUrl, Bitmap bitmap) {
        try {
            String key = hashKeyForDisk(imageUrl);
            DiskLruCache.Editor editor = mDiskLruCache.edit(key);
            if (editor != null) {
                OutputStream outputStream = editor.newOutputStream(0);
                if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)) {
                    editor.commit();
                } else {
                    editor.abort();
                }

            }
            mDiskLruCache.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 從緩存讀取數據
     *
     * @param imageUrl
     * @return
     */

    public Bitmap readFromCache(String imageUrl) {
        Bitmap bitmap = null;
        try {
            String key = hashKeyForDisk(imageUrl);
            DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
            if (snapShot != null) {//如果文件存在,讀取數據轉換為Bitmap對象
                InputStream is = snapShot.getInputStream(0);
                bitmap = BitmapFactory.decodeStream(is);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    /**
     * 將文件名轉換成"MD5"編碼
     *
     * @param key
     * @return
     */
    public String hashKeyForDisk(String key) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }

    private String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

}

 

下面是從網絡獲取一張圖片顯示到ImageView上,第一次進入app時從網絡獲取,並存儲至外部存儲私有區域,以後(即使沒有網絡的情況)進入若相同的緩存文件存在,則直接從緩存讀取。

注:涉及到網絡操作,需要添加網絡操作的相關權限

 

 
 

 

 public class MainActivity extends AppCompatActivity {
        ImageView mImageView;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mImageView = (ImageView) findViewById(R.id.get_image_from_network_img);
            new AsyncTask() {
                @Override
                protected Bitmap doInBackground(String... strings) {
                    Bitmap bitmap = DiskLruCacheHelper.getInstance(MainActivity.this).readFromCache(strings[0]);
                    if (bitmap == null) {
                        try {
                            URL url = new URL(strings[0]);
                            bitmap = BitmapFactory.decodeStream(url.openStream());
                            DiskLruCacheHelper.getInstance(MainActivity.this).writeToCache(strings[0], bitmap);
                            return bitmap;
                        } catch (MalformedURLException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    return bitmap;
                }

                @Override
                protected void onPostExecute(Bitmap bitmap) {
                    super.onPostExecute(bitmap);
                    mImageView.setImageBitmap(bitmap);
                }
            }.execute("http://file3.u148.net/2011/4/images/1302139153715.jpg");
        }
    }
 

 

運行過後,我們可以到相應的緩存區域查看是否有文件生成

 

\

多級緩存處理

 

上面提到了內存緩存和文件緩存,可以將這兩者一起使用,形成二級緩存,第一層是內存緩存,如果內存緩存中沒有,則從文件緩存中讀取,之後如果本地也沒有相關緩存文件,再從網絡獲取。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved