Android—Bitmap图片大小计算、压缩与三级缓存
Bitmap對象占用內存大小:?bitmap.getByteCount()
圖片所占內存大小計算方式:圖片長度 x 圖片寬度 x 一個像素點占用的字節數。
Android Bitmap使用的三種顏色格式:
- ALPHA_8–每個像素占1個字節,存儲透明度信息,沒有顏色信息。
- RGB_565–每個像素占2個字節存儲顏色信息,R 5位,G 6位,B 5位,能表示2^16種顏色。
- ARGB_8888–每個像素占4個字節存儲顏色信息,A R G B各一個字節,能表示2^24種顏色,還有一個字節存儲透明度信息。
BitmapFactory 類提供了幾種用于從各種來源創建 Bitmap 的解碼方法(decodeStream()、decodeByteArray()、decodeFile()、decodeResource()等)。根據您的圖片數據源選擇最合適的解碼方法。這些方法嘗試為構造的位圖分配內存,因此很容易導致 OutOfMemory 異常。每種類型的解碼方法都有額外的簽名,允許您通過 BitmapFactory.Options 類指定解碼選項。在解碼時將inJustDecodeBounds 屬性設置為 true 可避免內存分配,為位圖對象返回 null,但?outWidth、outHeight 和 outMimeType會被賦值。此方法可讓您在構造位圖并為其分配內存之前讀取圖片數據的尺寸和類型。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.id.image, options); //獲取res資源的圖片 int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;壓縮方式:
?質量壓縮:不改變圖片尺寸的情況下,通過損失顏色精度減少圖片的大小。圖片的長,寬,像素都不變,bitmap所占內存大小是不會變的。
bitmap.compress(Bitmap.CompressFormat format, //圖像的壓縮格式;int quality, //圖像壓縮率,0-100。 0 壓縮100%,100意味著不壓縮;OutputStream stream) ; //寫入壓縮數據的輸出流;- CompressFormat.JPEG
- CompressFormat.PNG,因為 PNG 格式是無損的,它無法再進行質量壓縮,quality這個參數就沒有作用了,會被忽略,所以最后圖片保存成的文件大小不會有變化;
- CompressFormat.WEBP,這個格式是 google 推出的圖片格式,它會比 JPEG 更加省空間。官方表示能節省 25%-34% 的空間;
采樣率壓縮:?降低圖像尺寸,改變圖片的存儲體積。
圖片尺寸的修改其實就是通過修改像素數,放大的過程稱之為上采樣,縮小的過程稱之為下采樣。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; bm = BitmapFactory.decodeFile(imageFilePath, options);設置inSampleSize的值(int類型)后,假如設為2,則寬和高都為原來的1/2,寬高都減少了,自然內存也降低了。
設置inPreferredConfig:
BitmapFactory.Options options2 = new BitmapFactory.Options(); options2.inPreferredConfig = Bitmap.Config.RGB_565;圖片大小直接縮小了一半,長度和寬度沒有變,相比argb_8888減少了一半的內存。
createScaledBitmap:
bm = Bitmap.createScaledBitmap(bit, 150, 150, true);將圖片壓縮成用戶所期望的長度和寬度,但是這里要說,如果用戶期望的長度和寬度和原圖長度寬度相差太多的話,圖片會很不清晰。
Matrix:
我們在自定義 View 控時隨處件可見 Matrix 的身影,主要用于坐標轉換映射,我們可以通過 Matrix 矩陣來控制視圖的變換。
Matrix matrix = new Matrix(); matrix.setScale(0.5f, 0.5f); bm = Bitmap.createBitmap(bit, 0, 0, bit.getWidth(),bit.getHeight(), matrix, true);內存緩存:LruCache算法
LruCache的算法核心 = LRU 算法 + LinkedHashMap數據結構?
- LRU(Least Recently Used)最近最少使用
- LinkedHashMap 哈希表+雙鏈表 用雙鏈表保證了HashMap的順序。
硬盤緩存:DiskLruCache
打開緩存
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) throws IOException緩存目錄;當前應用程序的版本號;同一個key可以對應多少個緩存文件,基本都是1;最多可以緩存多少字節的數據;
注:每當版本號改變,緩存路徑下存儲的所有數據都會被清除掉,因為DiskLruCache認為當應用程序有版本更新的時候,所有的數據都應該從網上重新獲取。
獲取editor對象
String key = turnToMD5(url); DiskLruCache.Editor editor = mDiskLruCache.edit(key);key會成為緩存文件的文件名,并且必須要和URL是一一對應的,而URL可能包含特殊字符,不能用作文件名,所以對URL進行MD5編碼,編碼后的字符串是唯一的,并且只會包含0-F字符,符合文件命名規則 。newOutputStream()方法需要傳一個index參數,這里傳入0就好。
轉為MD5方法:?
public String turnToMD5(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(); }獲得緩存地址對應的輸出流
OutputStream os = editor.newOutputStream(0); // 下載圖片到緩存的輸出流 downloadUrlToStream(imageUrl, outputStream);如果已經是已有的bitmap對象,通過前面學到的bitmap.compress方法,第三個參數輸出流就可以指定為os從而輸出到緩存路徑。
從網上下載的圖片:?
private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {HttpURLConnection urlConnection = null;BufferedOutputStream out = null;BufferedInputStream in = null;try {final URL url = new URL(urlString);urlConnection = (HttpURLConnection) url.openConnection();in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);out = new BufferedOutputStream(outputStream, 8 * 1024);int b;while ((b = in.read()) != -1) {out.write(b);}return true;} catch (final IOException e) {e.printStackTrace();} finally {if (urlConnection != null) {urlConnection.disconnect();}try {if (out != null) {out.close();}if (in != null) {in.close();}} catch (final IOException e) {e.printStackTrace();}}return false; }寫完緩存后,調用commit(),來提交緩存;調用abort(),放棄寫入的緩存。
editor.commit();editor.abort();讀取緩存:
借助DiskLruCache的get()方法實現的
public synchronized Snapshot get(String key) throws IOExceptionget()方法返回DiskLruCache.Snapshot對象,只需要調用它的getInputStream()方法就可以得到緩存文件的輸入流了。
try {String imageUrl = "https://..............";String key = hashKeyForDisk(imageUrl);DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);if (snapShot != null) {InputStream is = snapShot.getInputStream(0);Bitmap bitmap = BitmapFactory.decodeStream(is);mImage.setImageBitmap(bitmap);} } catch (IOException e) {e.printStackTrace(); }DiskLruCache對象其他方法:
- delete()? 這個方法用于將所有的緩存數據全部刪除
- close()? ?這個方法用于將DiskLruCache關閉掉,是和open()方法對應的一個方法。
- flush()? ? 這個方法用于將內存中的操作記錄同步到日志文件(也就是journal文件)當中。
- size()? ? ?這個方法會返回當前緩存路徑下所有緩存數據的總字節數,以byte為單位,如果應用程序中需要在界面上顯示當前緩存數據的總大小,就可以通過調用這個方法計算出來。
?
?
?
?
?
總結
以上是生活随笔為你收集整理的Android—Bitmap图片大小计算、压缩与三级缓存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 264,avs中Skip宏块与Direc
- 下一篇: 《C++字符串完全指南——第一部分:wi