效能優化04-圖片優化
效能優化04-圖片優化
一、圖片壓縮
圖片在APP中通常佔用很大的記憶體,所以經常需要進行圖片壓縮。
常用的圖片壓縮方式:尺寸壓縮、質量壓縮、格式轉換。
1.尺寸壓縮
將一張大圖載入進記憶體時,需要先進行尺寸壓縮,不然很容易導致oom。
尺寸壓縮既會影響圖片的儲存大小,也會影響圖片的記憶體大小。
尺寸壓縮使用 BitmapFactory.Options,載入2次:
- 第一次只載入邊框,獲取圖片尺寸;
- 設定壓縮比例,載入完整圖片。
/**
* 尺寸壓縮
* @param resources 資源
* @param id 資源id
* @param newWidth 壓縮後的寬度
* @param newHeight 壓縮後的高度
* @return 壓縮後的Btmap
*/
public static Bitmap sizeCompress(Resources resources, int id, float newWidth, float newHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
//開始讀入圖片,此時把options.inJustDecodeBounds 設回true了
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeResource(resources, id, options);
options.inJustDecodeBounds = false;
int w = options.outWidth;
int h = options.outHeight;
//縮放比。由於是固定比例縮放,只用高或者寬其中一個資料進行計算即可
int sacle = (int) (w / newWidth > h / newHeight ? w / newWidth : h / newHeight);
sacle = sacle <= 0 ? 1 : sacle;
options.inSampleSize = sacle;//設定縮放比例
//重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了
bitmap = BitmapFactory.decodeResource(resources, id, options);
return bitmap;
}
注意:壓縮比例可以設定任意值,但是實際壓縮比例一定是2的n次方。
2.質量壓縮
質量壓縮只會影響圖片的儲存大小,不會影響圖片的記憶體大小。另外,將質量壓縮後的圖片轉化為二進位制資料進行傳輸時,資料也變小了。比如微信分享圖片。
Bitmap.compress()是系統提供的質量壓縮工具。Bitmap.compress()使用不完整的Skia庫,對jpeg的處理基於libjpeg,對png則是基於libpng。 libjpeg庫壓縮圖片時,可設定哈夫曼編碼。但由於cpu的緣故,7.0之前沒開開啟,7.0以後才開啟。
/**
* 質量壓縮
* @param bitmap 原圖
* @param compressFormat 圖片型別:JPEG,PNG,WEBP;
* @param quality 質量
* @return 壓縮後的Bitmap
*/
public static Bitmap qualityCompress(Bitmap bitmap, Bitmap.CompressFormat compressFormat, int quality) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(compressFormat, quality, baos);
byte[] bytes = baos.toByteArray();
Bitmap bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
return bm;
}
另外,介紹一個第三方的質量壓縮框架LibJpeg-turbo。下載地址:https://libjpeg-turbo.org/。
3.格式轉換
安卓常用的圖片格式有JPEG,PNG和WEBP。
JPEG是一種針對照片視訊而廣泛使用的一種壓縮標準方法。
- 常用的.jpg檔案是有失真壓縮
- 不支援背景透明
- 適用於照片等色彩豐富的大圖壓縮
- 不適用於logo,線圖
PNG即行動式網路圖形(Portable Network Graphics,PNG)是一種無失真壓縮的點陣圖圖形格式,支援索引、灰度、RGB三種顏色方案以及Alpha通道等特性。Android開發中的切圖素材多為.png格式。
- 支援256色調色盤技術以產生小體積檔案
- 最高支援48位真彩色影像以及16位灰度影像。
- 支援Alpha通道的透明/半透明特性。
- 支援影像亮度的Gamma校準資訊。
- 支援儲存附加文字資訊,以保留影像名稱、作者、版權、創作時間、註釋等資訊。
- 使用無失真壓縮。
- 漸近顯示和流式讀寫,適合在網路傳輸中快速顯示預覽效果後再展示全貌。
- 使用CRC防止檔案出錯。
- 最新的PNG標準允許在一個檔案記憶體儲多幅影像。
WEBP是一種同時提供了有失真壓縮與無失真壓縮的圖片檔案格式,派生自視訊編碼格式VP8,是由Google在購買On2 Technologies後發展出來,以BSD授權條款釋出。Android 4.0+預設支援WebP,Android 4.2.1+開始支援無損WebP和帶alpha通道的WebP。
- 具有更優的影像資料壓縮演算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的影像質量
- 無損的WebP圖片比PNG小26%,有損的WebP圖片比JPEG小25-34%
- 相較編碼JPEG檔案,編碼同樣質量的WebP檔案也需要佔用更多的計算資源
- 具備了無損和有損的壓縮模式、Alpha 透明以及動畫的特性
- 在 JPEG 和 PNG 上的轉化效果都非常優秀、穩定和統一
- 無損WebP支援透明及alpha通道,有損在一定條件下同樣支援
如果不考慮相容性,那麼使用有損的WebP圖片,記憶體最省;使用無損的WebP圖片,效能最優。
另外,將PNG和JPEG轉換層WEBP格式,可以在AndroidStudio上,選中圖片,右鍵選擇Converting Images to Webp
來進行轉換。
如果考慮相容性,那麼使用JPG,記憶體更省;使用PNG,效果最好。PNG和JPEG的轉換有很多工具,這裡就不說了。
二、色彩模式
圖片在記憶體中的大小,由畫素和色彩模式決定。
色彩模式可以說是每個畫素所佔的位元組數,決定了圖片的精細度。常見的色彩模式:RGB_565、ARGB_4444、ARGB_8888。
ARGB的含義:A代表alpha 通道,即透明度;R代表Red,紅色;G代表Green,綠色;B代表Blue,藍色。
RGB_565:R佔5位,G佔6位,B佔5位,共16位,即2個位元組;
ARGB_4444:A佔4位,R佔4位,G佔4位,B佔4位,共16位,即2個位元組;
ARGB_8888:A佔8位,R佔8位,G佔8位,B佔8位,共32位,即4個位元組;
RGB_565和ARGB_4444的所佔記憶體小於ARGB_8888,精細度也低於ARGB_8888。不過從肉眼,很難看出三者的差別。
/**
* 設定圖片色彩模式
*
* @param bitmap 點陣圖
* @param config 色彩模式,常用的:RGB_565,ARGB_4444ARGB_8888。
* @return 返回圖片大小
*/
public static long setConfig(Bitmap bitmap, Bitmap.Config config) {
if (bitmap == null) {
return 0;
}
Bitmap newBitmap = bitmap.copy(config, true);
long size = newBitmap.getByteCount();
return size;
}
三、圖片快取
展示圖片時,經常需要重新載入圖片。對於重複載入的圖片,如果使用快取,就會提高效能。
1.記憶體快取
當介面重新整理時,就會重新載入圖片。如果重新建立一個Bitmap物件,就很耗效能,這個時候可以使用LruCache來快取Bitmap物件。
LruCache<String, Bitmap> lruCache;
public void test(View view) {
long maxMemory = Runtime.getRuntime().maxMemory();
int cacheSize = (int) (maxMemory / 8);
if (lruCache == null) {
lruCache = new LruCache<String, Bitmap>(cacheSize){
//必須重寫此方法,來測量Bitmap的大小
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
};
}
Bitmap bitmap = lruCache.get("T");
if (bitmap == null) {
bitmap = ToolBitmap.sizeCompress(getResources(), R.mipmap.wangyiyun_icon, 400, 400);
lruCache.put("T", bitmap);
}
ImageView img = findViewById(R.id.img);
img.setImageBitmap(bitmap);
}
LruCache使用了Lru演算法(最近使用的優先順序最高,最少使用的優先順序最低),通過LinkedHashMap來存取物件,每次取資料後,把它放在連結串列最後面;每次新增資料時,也是放在連結串列最後面,同時檢查快取大小,如果超過閥值,就移除連結串列最前面的資料。
2.檔案快取
當Activity重新啟動時,可能需要展示之前的圖片,這是時候就需要檔案快取,我們使用第三方的DiskLruCache。
Github地址:https://github.com/JakeWharton/DiskLruCache
使用參考:https://www.jianshu.com/p/0c56dc217917
3.Bitmap複用
Bitmap複用:不再使用的Bitamp可分配給其他圖片使用。
通過bitmap複用,減少頻繁申請記憶體帶來的效能問題。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;//必須設為true,否則不能被複用
//被複用的bitmap
Bitmap oldbitmap = BitmapFactory.decodeResource(getResources(),R.drawable.lance,options);
//複用oldbitmap
options.inBitmap = oldbitmap;
Bitmap newbitmap = BitmapFactory.decodeResource(getResources(),R.drawable.lance,options);
在4.4之前,Bitmap格式必須為jpg、png,inSampleSize為1才能複用,並且複用的bitmap與被複用的bitmap同等寬高。
在4.4以後:被複用的Bitmap的記憶體必須大於等於複用的bitmap的記憶體。
四、超大圖載入
超大圖載入的核心是BitmapRegionDecoder。BitmapRegionDecoder主要用於顯示圖片的某一塊矩形區域。
BitmapRegionDecoder的使用很簡單:
//isShareable為false會複製一張圖片,為true會共用
BitmapRegionDecoder Decoder = BitmapRegionDecodeBitmapRegionDecoder r.newInstance(is, false);
//獲取指定區域的bitmap。mRect為區域的矩陣,mOptions點陣圖片的配置
Bitmap mBitmap = mDecoder.decodeRegion(mRect, mOptions);
我們通常載入超大圖,都是使用自定義控制元件。自定義控制元件有兩個關鍵:
- 使用BitmapRegionDecoder載入圖片;
- 重寫事件監聽,實現圖片拖動;
下面貼出關鍵程式碼:
/**
* 設定圖片
*
* @param is 圖片的輸入流
*/
public void setImage(InputStream is) {
mOptions.inJustDecodeBounds = true;//載入邊框
BitmapFactory.decodeStream(is, null, mOptions);//獲取圖片寬高
mImageWidth = mOptions.outWidth;
mImageHeight = mOptions.outHeight;
mOptions.inMutable = true;//複用Bitmap TODO
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;//設定畫素格式
mOptions.inJustDecodeBounds = false;//載入圖片
try {
mDecoder = BitmapRegionDecoder.newInstance(is, false);//isShareable為false會複製一張圖片,為true會共用
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();//重新佈局
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mDecoder == null) {
return;
}
mViewWidth = getMeasuredWidth();//測量控制元件的寬
mViewHeight = getMeasuredHeight();//測量控制元件的高
mSacle = (float) mViewWidth / mImageWidth;
//獲取載入區域
mRect.left = 0;
mRect.top = 0;
mRect.right = mImageWidth;
mRect.bottom = (int) (mViewHeight / mSacle);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDecoder == null) {
return;
}
mOptions.inBitmap = mBitmap;//複用mBitmap
mBitmap = mDecoder.decodeRegion(mRect, mOptions);
Matrix matrix = new Matrix();//通過矩陣縮放
matrix.setScale(mSacle, mSacle);//設定水平和垂直方向的縮放
canvas.drawBitmap(mBitmap, matrix, null);
}
/**
* 重寫onScroll,調整圖片的矩陣顯示區域
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
mRect.offset(0, (int) distanceY);//垂直滑動圖片
if (mRect.bottom > mImageHeight) {
mRect.top = (int) (mImageHeight - mViewHeight / mSacle);
mRect.bottom = mImageHeight;
}
if (mRect.top < 0) {
mRect.top = 0;
mRect.bottom = (int) (mViewHeight / mSacle);
}
invalidate();//重繪
return false;
}
最後
程式碼地址:https://gitee.com/yanhuo2008/Common/blob/master/Tool/src/main/java/gsw/tool/ui/BigView.java
效能優化專題:https://www.jianshu.com/nb/25128595
喜歡請點贊,謝謝!
相關文章
- 前端效能優化 --- 圖片優化前端優化
- Android效能優化——圖片優化(二)Android優化
- iOS效能優化 - 網路圖片載入優化iOS優化
- 前端效能優化之路——圖片篇。前端優化
- 圖片優化優化
- [譯]Web 效能優化: 圖片優化讓網站大小減少 62%Web優化網站
- Android記憶體優化之圖片優化Android記憶體優化
- 【前端效能優化】vue效能優化前端優化Vue
- 【前端優化】js圖片懶載入及優化前端優化JS
- iOS 圖形效能優化iOS優化
- 前端效能優化(JS/CSS優化,SEO優化)前端優化JSCSS
- APP查詢圖片優化APP優化
- 淺探前端圖片優化前端優化
- 圖片視訊瀑布流長列表效能優化實踐優化
- 前端效能優化-圖片懶載入(防抖、節流)前端優化
- [效能優化]DateFormatter深度優化探索優化ORM
- 效能優化|Tomcat 服務優化優化Tomcat
- Android 效能優化 ---- 啟動優化Android優化
- Android效能優化----卡頓優化Android優化
- 效能優化優化
- 效能優化 (五) 長圖優化,仿微博載入長圖方式優化
- Retina屏下的圖片優化優化
- iOS圖片記憶體優化iOS記憶體優化
- 如何處理 Web 圖片優化?Web優化
- 科技軟文營銷圖片優化不可忽視,圖片優化需要了解這些優化
- Android效能優化之佈局優化Android優化
- Android效能優化(1)—webview優化篇Android優化WebView
- Oracle效能優化-SQL優化(案例一)Oracle優化SQL
- Oracle效能優化-SQL優化(案例二)Oracle優化SQL
- Oracle效能優化-SQL優化(案例三)Oracle優化SQL
- Oracle效能優化-SQL優化(案例四)Oracle優化SQL
- 效能優化(二) UI 繪製優化優化UI
- Android效能優化——效能優化的難題總結Android優化
- Javascript 效能優化JavaScript優化
- java效能優化Java優化
- react效能優化React優化
- Canvas效能優化Canvas優化
- UI效能優化UI優化