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