您好,登錄后才能下訂單哦!
這篇文章主要講解了“Android的bitmap圖片優(yōu)化方法是什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Android的bitmap圖片優(yōu)化方法是什么”吧!
Android開發(fā)中,加載圖片過(guò)多、過(guò)大很容易引起OutOfMemoryError異常,即我們常見(jiàn)的內(nèi)存溢出。因?yàn)锳ndroid對(duì)單個(gè)應(yīng)用施加內(nèi)存限制,默認(rèn)分配的內(nèi)存只有幾M(具體視不同系統(tǒng)而定)。而載入的圖片如果是JPG之類的壓縮格式(JPG支持最高級(jí)別的壓縮,不過(guò)該壓縮是有損的),在內(nèi)存中展開會(huì)占用大量的內(nèi)存空間,也就容易形成內(nèi)存溢出。那么高效的加載Bitmap是很重要的事情。Bitmap在Android中指的是一張圖片,圖片的格式有.jpg .jpg .webp 等常見(jiàn)的格式。
一個(gè)原則: 在保證圖片視覺(jué)不失真前提下,盡可能的縮小體積
Android目前常用的圖片格式有jpg,jpeg和webp
png:無(wú)損壓縮圖片格式,支持Alpha通道,Android切圖素材多采用此格式
jpeg:有損壓縮圖片格式,不支持背景透明,適用于照片等色彩豐富的大圖壓縮,不適合logo
webp:是一種同時(shí)提供了有損壓縮和無(wú)損壓縮的圖片格式,派生自視頻編碼格式VP8,從谷歌官網(wǎng)來(lái)看,無(wú)損webp平均比jpg小26%,有損的webp平均比jpeg小25%~34%,無(wú)損webp支持Alpha通道,有損webp在一定的條件下同樣支持,有損webp在Android4.0(API 14)之后支持,無(wú)損和透明在Android4.3(API18)之后支持
采用webp能夠在保持圖片清晰度的情況下,可以有效減小圖片所占有的磁盤空間大小
圖片壓縮可以從三個(gè)方面去考慮:
質(zhì)量壓縮并不會(huì)改變圖片在內(nèi)存中的大小,僅僅會(huì)減小圖片所占用的磁盤空間的大小,因?yàn)橘|(zhì)量壓縮不會(huì)改變圖片的分辨率,而圖片在內(nèi)存中的大小是根據(jù)widthheight一個(gè)像素的所占用的字節(jié)數(shù)計(jì)算的,寬高沒(méi)變,在內(nèi)存中占用的大小自然不會(huì)變,質(zhì)量壓縮的原理是通過(guò)改變圖片的位深和透明度來(lái)減小圖片占用的磁盤空間大小,所以不適合作為縮略圖,可以用于想保持圖片質(zhì)量的同時(shí)減小圖片所占用的磁盤空間大小。另外,由于jpg是無(wú)損壓縮,所以設(shè)置quality無(wú)效,
/** * 質(zhì)量壓縮 * * @param format 圖片格式 jpeg,png,webp * @param quality 圖片的質(zhì)量,0-100,數(shù)值越小質(zhì)量越差 */ public static void compress(Bitmap.CompressFormat format, int quality) { File sdFile = Environment.getExternalStorageDirectory(); File originFile = new File(sdFile, "originImg.jpg"); Bitmap originBitmap = BitmapFactory.decodeFile(originFile.getAbsolutePath()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); originBitmap.compress(format, quality, bos); try { FileOutputStream fos = new FileOutputStream(new File(sdFile, "resultImg.jpg")); fos.write(bos.toByteArray()); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
采樣率壓縮是通過(guò)設(shè)置BitmapFactory.Options.inSampleSize,來(lái)減小圖片的分辨率,進(jìn)而減小圖片所占用的磁盤空間和內(nèi)存大小。
設(shè)置的inSampleSize會(huì)導(dǎo)致壓縮的圖片的寬高都為1/inSampleSize,整體大小變?yōu)樵紙D片的inSampleSize平方分之一,當(dāng)然,這些有些注意點(diǎn):
inSampleSize小于等于1會(huì)按照1處理
inSampleSize只能設(shè)置為2的平方,不是2的平方則最終會(huì)減小到最近的2的平方數(shù),如設(shè)置7會(huì)按4進(jìn)行壓縮,設(shè)置15會(huì)按8進(jìn)行壓縮。
/** * * @param inSampleSize 可以根據(jù)需求計(jì)算出合理的inSampleSize */ public static void compress(int inSampleSize) { File sdFile = Environment.getExternalStorageDirectory(); File originFile = new File(sdFile, "originImg.jpg"); BitmapFactory.Options options = new BitmapFactory.Options(); //設(shè)置此參數(shù)是僅僅讀取圖片的寬高到options中,不會(huì)將整張圖片讀到內(nèi)存中,防止oom options.inJustDecodeBounds = true; Bitmap emptyBitmap = BitmapFactory.decodeFile(originFile.getAbsolutePath(), options); options.inJustDecodeBounds = false; options.inSampleSize = inSampleSize; Bitmap resultBitmap = BitmapFactory.decodeFile(originFile.getAbsolutePath(), options); ByteArrayOutputStream bos = new ByteArrayOutputStream(); resultBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos); try { FileOutputStream fos = new FileOutputStream(new File(sdFile, "resultImg.jpg")); fos.write(bos.toByteArray()); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
通過(guò)減少圖片的像素來(lái)降低圖片的磁盤空間大小和內(nèi)存大小,可以用于緩存縮略圖
/** * 縮放bitmap * @param context * @param id * @param maxW * @param maxH * @return */ public static Bitmap resizeBitmap(Context context,int id,int maxW,int maxH,boolean hasAlpha,Bitmap reusable){ Resources resources = context.getResources(); BitmapFactory.Options options = new BitmapFactory.Options(); // 只解碼出 outxxx參數(shù) 比如 寬、高 options.inJustDecodeBounds = true; BitmapFactory.decodeResource(resources,id,options); //根據(jù)寬、高進(jìn)行縮放 int w = options.outWidth; int h = options.outHeight; //設(shè)置縮放系數(shù) options.inSampleSize = calcuteInSampleSize(w,h,maxW,maxH); if (!hasAlpha){ options.inPreferredConfig = Bitmap.Config.RGB_565; } options.inJustDecodeBounds = false; //設(shè)置成能復(fù)用 options.inMutable=true; options.inBitmap=reusable; return BitmapFactory.decodeResource(resources,id,options); } /** * 計(jì)算縮放系數(shù) * @param w * @param h * @param maxW * @param maxH * @return 縮放的系數(shù) */ private static int calcuteInSampleSize(int w,int h,int maxW,int maxH) { int inSampleSize = 1; if (w > maxW && h > maxH){ inSampleSize = 2; //循環(huán) 使寬、高小于 最大的寬、高 while (w /inSampleSize > maxW && h / inSampleSize > maxH){ inSampleSize *= 2; } } return inSampleSize; } }
使用JPEG庫(kù),在jni層使用哈夫曼算法去壓縮圖片
Android的圖片引擎使用的是閹割版的skia引擎,去掉了圖片壓縮中的哈夫曼算法
void write_JPEG_file(uint8_t *data, int w, int h, jint q, const char *path) { // 3.1、創(chuàng)建jpeg壓縮對(duì)象 jpeg_compress_struct jcs; //錯(cuò)誤回調(diào) jpeg_error_mgr error; jcs.err = jpeg_std_error(&error); //創(chuàng)建壓縮對(duì)象 jpeg_create_compress(&jcs); // 3.2、指定存儲(chǔ)文件 write binary FILE *f = fopen(path, "wb"); jpeg_stdio_dest(&jcs, f); // 3.3、設(shè)置壓縮參數(shù) jcs.image_width = w; jcs.image_height = h; //bgr jcs.input_components = 3; jcs.in_color_space = JCS_RGB; jpeg_set_defaults(&jcs); //開啟哈夫曼功能 jcs.optimize_coding = true; jpeg_set_quality(&jcs, q, 1); // 3.4、開始?jí)嚎s jpeg_start_compress(&jcs, 1); // 3.5、循環(huán)寫入每一行數(shù)據(jù) int row_stride = w * 3;//一行的字節(jié)數(shù) JSAMPROW row[1]; while (jcs.next_scanline < jcs.image_height) { //取一行數(shù)據(jù) uint8_t *pixels = data + jcs.next_scanline * row_stride; row[0]=pixels; jpeg_write_scanlines(&jcs,row,1); } // 3.6、壓縮完成 jpeg_finish_compress(&jcs); // 3.7、釋放jpeg對(duì)象 fclose(f); jpeg_destroy_compress(&jcs); }
因?yàn)樯婕暗絡(luò)ni部分,暫時(shí)只貼一下使用的代碼,后面會(huì)寫一些jni部分的博客與大家分享。
設(shè)置圖片可以復(fù)用
圖片復(fù)用主要就是指的復(fù)用內(nèi)存塊,不需要在重新給這個(gè)bitmap申請(qǐng)一塊新的內(nèi)存,避免了一次內(nèi)存的分配和回收,從而改善了運(yùn)行效率。
需要注意的是inBitmap只能在3.0以后使用。2.3上,bitmap的數(shù)據(jù)是存儲(chǔ)在native的內(nèi)存區(qū)域,并不是在Dalvik的內(nèi)存堆上。
使用inBitmap,在4.4之前,只能重用相同大小的bitmap的內(nèi)存區(qū)域,而4.4之后你可以重用任何bitmap的內(nèi)存區(qū)域,只要這塊內(nèi)存比將要分配內(nèi)存的bitmap大就可以。這里最好的方法就是使用LRUCache來(lái)緩存bitmap,后面來(lái)了新的bitmap,可以從cache中按照api版本找到最適合重用的bitmap,來(lái)重用它的內(nèi)存區(qū)域。
BitmapFactory.Options options = new BitmapFactory.Options(); // 只解碼出 outxxx參數(shù) 比如 寬、高 options.inJustDecodeBounds = true; BitmapFactory.decodeResource(resources,id,options); //根據(jù)寬、高進(jìn)行縮放 int w = options.outWidth; int h = options.outHeight; //設(shè)置縮放系數(shù) options.inSampleSize = calcuteInSampleSize(w,h,maxW,maxH); if (!hasAlpha){ options.inPreferredConfig = Bitmap.Config.RGB_565; } options.inJustDecodeBounds = false; //設(shè)置成能復(fù)用 options.inMutable=true; options.inBitmap=reusable;
使用圖片緩存
android中有一個(gè)LruCache是基于最記最少使用算法實(shí)現(xiàn)的一個(gè)線程安全的數(shù)據(jù)緩存類,當(dāng)超出設(shè)定的緩存容量時(shí),優(yōu)先淘汰最近最少使用的數(shù)據(jù)LruCache的LRU緩存策略是利用LinkedHashMap來(lái)實(shí)現(xiàn)的,并通過(guò)封裝get/put等相關(guān)方法來(lái)實(shí)現(xiàn)控制緩存大小以及淘汰元素,但不支持為null的key和value。 我們可以使用JakeWharton提供的一個(gè)開源庫(kù)github.com/JakeWharton… 來(lái)實(shí)現(xiàn)我們圖片緩存的邏輯
省略了內(nèi)存和磁盤的部分。
感謝各位的閱讀,以上就是“Android的bitmap圖片優(yōu)化方法是什么”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Android的bitmap圖片優(yōu)化方法是什么這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。