在android程式開發中我們經常見到需要上傳圖片的情境,在這裡有個技術點,需要把圖片壓縮處理,然後再進行上傳。這樣可以減少流量的消耗,提高圖片的上傳速度等問題。
關於android如何壓縮,網上的資料也是很多,但大多數都是程式碼片段,講解壓縮步驟,而沒有一個實用的工具類庫。那麼如何將壓縮演算法封裝成一個工具 + 生產力庫呢?其中會遇到些什麼問題,比如:
1.需要壓縮的圖片有多少
2.壓縮後的圖片是覆蓋還是儲存到另外的目錄
3.如果是另存目錄需要將原始圖片刪除嗎
4.如果改變壓縮後的圖片的尺寸大小是按照原圖的比例縮小還是直接指定大小
5.如果原圖有旋轉問題,需不需要進行修正
6.對於多圖壓縮是並發還是線性處理
7.能不能使用service來進行壓縮處理,是local(本地)還是remote(遠程)的方式來啟動service
8.如果需要壓縮的圖片非常多,如何使用線程池來處理
基於以上幾點的思考,本人打算寫個系列文章來一步一步解決這些問題(忘大家持續關注),將Service,多線程的使用及壓縮演算法集合到一個項目中。這樣不僅在實際應用中還是作為學習資料來講都是比較好的。最終我會將這個系列中涉及的代碼及迭代的過程開源到github,歡迎大家star,歡迎遞交bug。
當然有些朋友可能會說實際應用中一次上傳的圖片數量不會太多吧,考慮這些問題是不是有點多慮了,好吧,如果您真是這麼認為的那麼可以忽略本系列文章。
實際需求中基本都會是按照原圖的寬高比進行壓縮,直接指定尺寸大小的比較少見,所以本系列文章也是針對這種等比率壓縮來進行的。
總之,對圖片進行壓縮,大家主要關注兩點:
1.對圖片的尺寸大小進行縮放來達到壓縮的目的
2.對圖片進行品質壓縮
對圖片的尺寸大小進行縮放來達到壓縮的目的
針對這種情況及圖片旋轉問題,大家可以參考我的 android處理拍照旋轉問題及帶來的對記憶體佔用的思考 這篇文章。
只是大家需要注意的是,這裡需要按照原始圖片的寬高比(srcRatio)來計算最終輸出圖片的寬高(actualOutWidth,actualOutHeight),最後通過actualOutWidth,actualOutHeight來計算採樣值sampleSize。
核心代碼如下:
LGImageCompressor.javaBitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeFile(srcImagePath, options);//根據原始圖片的寬高比和期望的輸出圖片的寬高比計算最終輸出的圖片的寬和高float srcWidth = options.outWidth;float srcHeight = options.outHeight;float maxWidth = outWidth;//期望輸出的圖片寬度float maxHeight = outHeight;//期望輸出的圖片高度float srcRatio = srcWidth / srcHeight;float outRatio = maxWidth / maxHeight;float actualOutWidth = srcWidth;//最終輸出的圖片寬度float actualOutHeight = srcHeight;//最終輸出的圖片高度if (srcWidth > maxWidth || srcHeight > maxHeight) {if (srcRatio < outRatio) {actualOutHeight = maxHeight;actualOutWidth = actualOutHeight * srcRatio;} else if (srcRatio > outRatio) {actualOutWidth = maxWidth;actualOutHeight = actualOutWidth / srcRatio;} else {actualOutWidth = maxWidth;actualOutHeight = maxHeight;}}//計算sampleSizeoptions.inSampleSize = computSampleSize(options, actualOutWidth, actualOutHeight);
為了方便大家理解以上代碼,舉個極端例子:
假如原始圖片寬為srcWidth=40,高為srcHeight=20。期望輸出的寬為maxWidth=300,高為maxHeight=10。 那麼srcRatio=40:20=2,outRatio=300:10=30. 顯然srcRatio<outRatio,那麼我們的實際最終輸出圖片的尺寸應該以maxHeight(10)為準即actualOutHeight = maxHeight,最後根據原圖的比率來計算actualOutWidth=actualOutHeight*srcRatio = 10*40/20=20,最後得到的actualOutWidth=20. 最終輸出圖片的寬高比為20:10=2,和原始圖片寬高比相同。其它情況類似,這裡不做詳解了。
對圖片進行品質壓縮
針對這種情況,android的Bitmap類中API介面有compress方法
public boolean compress(CompressFormat format, int quality, OutputStream stream)
三個參數的理解應該不難,大家可以查看官方doc文檔。compress方法主要通過quality來控制輸入到stream中的像素品質。
這針對希望輸出的圖片佔用的空間不大於一定的值這種情境會比較合適,因為我們可以通過迴圈判斷壓縮後的大小是否大於定值,如果滿足則減少quality繼續執行compress操作。核心代碼如下:
//進行有損壓縮ByteArrayOutputStream baos = new ByteArrayOutputStream();int options_ = 100;actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);//品質壓縮方法,把壓縮後的資料存放到baos中 (100表示不壓縮,0表示壓縮到最小)int baosLength = baos.toByteArray().length;while (baosLength / 1024 > maxFileSize) {//迴圈判斷如果壓縮後圖片是否大於maxMemmorrySize,大於繼續壓縮baos.reset();//重設baos即讓下一次的寫入覆蓋之前的內容options_ = Math.max(0, options_ - 10);//圖片品質每次減少10actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);//將壓縮後的圖片儲存到baos中baosLength = baos.toByteArray().length;if (options_ == 0)//如果圖片的品質已降到最低則,不再進行壓縮break;}
壓縮一個超大圖是要費時間的,所以大家應該考慮將壓縮放到後台線程中執行,如果沒有高並發的需求使用AsyncTask就能解決問題。
核心代碼:
private class CompressTask extends AsyncTask<String, Void, String> {@Overrideprotected String doInBackground(String... params) {return compressImage();//執行壓縮操作}@Overrideprotected void onPreExecute() {if (compressListener != null) {compressListener.onCompressStart();//監聽回調(開始壓縮)}}@Overrideprotected void onPostExecute(String imageOutPath) {if (compressListener != null) {compressListener.onCompressEnd(imageOutPath);//監聽回調(壓縮結束)}}}
經過適當的封裝代碼可以通過在Activity中的執行
LGImgCompressor.getInstance(this).withListener(this).starCompress(Uri.fromFile(imageFile).toString(),outWidth,outHeight,maxFileSize);
來啟動壓縮任務
寫在最後
為了達到最佳的壓縮結果,可以將上面兩種方案同時進行。如果壓縮消耗的時間很長,需要將壓縮過程放入後台線程中執行。
本人寫了個簡單的demo程式,實現的功能有:
1.開啟網路攝影機拍攝照片
2.指定照片的儲存位置
3.壓縮照片到指定目錄下
4.使用AsyncTask執行壓縮操作
5.顯示壓縮後的照片及其相關資訊到前台activity
由於這個版本是使用AsyncTask非同步任務來執行compress的,而AsyncTask由於android版本分裂問題有些版本是多線程的,有些版本是單線程的,也是醉了,總之此版本適用於一次壓縮任務不是很多的情況,如果需要處理資料很大的壓縮任務,需要考慮用線程池來處理。
另外,如何結合使用service和多線程會在下篇文章具體說明。
demo開源github地址如下:
LGImageCompressor
以上所述是小編給大家介紹的Android圖片壓縮上傳之基礎篇的相關知識,希望對大家有所協助,如果大家想瞭解更多資訊敬請關注云棲社區網站!