最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android 性能優(yōu)化系列之bitmap圖片優(yōu)化

 更新時間:2021年11月06日 17:25:43   作者:涂程  
在日常開發(fā)的APP,大部分時候需要想用戶展示圖片信息,圖片最終對應(yīng)Android中的Bitmap對象。而對于APP端來說Bitmap又是一個比較麻煩的問題,主要表現(xiàn)在Bitmap是非常占用內(nèi)存的對象,處理不當(dāng)將導(dǎo)致APP運行卡頓甚至出現(xiàn)OOM

背景

Android開發(fā)中,加載圖片過多、過大很容易引起OutOfMemoryError異常,即我們常見的內(nèi)存溢出。因為Android對單個應(yīng)用施加內(nèi)存限制,默認分配的內(nèi)存只有幾M(具體視不同系統(tǒng)而定)。而載入的圖片如果是JPG之類的壓縮格式(JPG支持最高級別的壓縮,不過該壓縮是有損的),在內(nèi)存中展開會占用大量的內(nèi)存空間,也就容易形成內(nèi)存溢出。那么高效的加載Bitmap是很重要的事情。Bitmap在Android中指的是一張圖片,圖片的格式有.jpg .jpg .webp 等常見的格式。

如何選擇圖片格式

一個原則: 在保證圖片視覺不失真前提下,盡可能的縮小體積

Android目前常用的圖片格式有jpg,jpeg和webp

  • png:無損壓縮圖片格式,支持Alpha通道,Android切圖素材多采用此格式
  • jpeg:有損壓縮圖片格式,不支持背景透明,適用于照片等色彩豐富的大圖壓縮,不適合logo
  • webp:是一種同時提供了有損壓縮和無損壓縮的圖片格式,派生自視頻編碼格式VP8,從谷歌官網(wǎng)來看,無損webp平均比jpg小26%,有損的webp平均比jpeg小25%~34%,無損webp支持Alpha通道,有損webp在一定的條件下同樣支持,有損webp在Android4.0(API 14)之后支持,無損和透明在Android4.3(API18)之后支持

采用webp能夠在保持圖片清晰度的情況下,可以有效減小圖片所占有的磁盤空間大小

圖片壓縮

圖片壓縮可以從三個方面去考慮:

1.質(zhì)量

質(zhì)量壓縮并不會改變圖片在內(nèi)存中的大小,僅僅會減小圖片所占用的磁盤空間的大小,因為質(zhì)量壓縮不會改變圖片的分辨率,而圖片在內(nèi)存中的大小是根據(jù)widthheight一個像素的所占用的字節(jié)數(shù)計算的,寬高沒變,在內(nèi)存中占用的大小自然不會變,質(zhì)量壓縮的原理是通過改變圖片的位深和透明度來減小圖片占用的磁盤空間大小,所以不適合作為縮略圖,可以用于想保持圖片質(zhì)量的同時減小圖片所占用的磁盤空間大小。另外,由于jpg是無損壓縮,所以設(shè)置quality無效,

  /**
 * 質(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();
    }
}

2.采樣率

采樣率壓縮是通過設(shè)置BitmapFactory.Options.inSampleSize,來減小圖片的分辨率,進而減小圖片所占用的磁盤空間和內(nèi)存大小。

設(shè)置的inSampleSize會導(dǎo)致壓縮的圖片的寬高都為1/inSampleSize,整體大小變?yōu)樵紙D片的inSampleSize平方分之一,當(dāng)然,這些有些注意點:

  • inSampleSize小于等于1會按照1處理
  • inSampleSize只能設(shè)置為2的平方,不是2的平方則最終會減小到最近的2的平方數(shù),如設(shè)置7會按4進行壓縮,設(shè)置15會按8進行壓縮。
/**
 * 
 * @param inSampleSize  可以根據(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中,不會將整張圖片讀到內(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();
    }
}

3.縮放

通過減少圖片的像素來降低圖片的磁盤空間大小和內(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ù)寬、高進行縮放
        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);
    }

    /**
     * 計算縮放系數(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庫,在jni層使用哈夫曼算法去壓縮圖片

Android的圖片引擎使用的是閹割版的skia引擎,去掉了圖片壓縮中的哈夫曼算法

void write_JPEG_file(uint8_t *data, int w, int h, jint q, const char *path) {
//    3.1、創(chuàng)建jpeg壓縮對象
    jpeg_compress_struct jcs;
    //錯誤回調(diào)
    jpeg_error_mgr error;
    jcs.err = jpeg_std_error(&error);
    //創(chuàng)建壓縮對象
    jpeg_create_compress(&jcs);
//    3.2、指定存儲文件  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、開始壓縮
    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對象
    fclose(f);
    jpeg_destroy_compress(&jcs);
}

因為涉及到j(luò)ni部分,暫時只貼一下使用的代碼,后面會寫一些jni部分的博客與大家分享。

  • 設(shè)置圖片可以復(fù)用

圖片復(fù)用主要就是指的復(fù)用內(nèi)存塊,不需要在重新給這個bitmap申請一塊新的內(nèi)存,避免了一次內(nèi)存的分配和回收,從而改善了運行效率。

需要注意的是inBitmap只能在3.0以后使用。2.3上,bitmap的數(shù)據(jù)是存儲在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來緩存bitmap,后面來了新的bitmap,可以從cache中按照api版本找到最適合重用的bitmap,來重用它的內(nèi)存區(qū)域。

   BitmapFactory.Options options = new BitmapFactory.Options();
        // 只解碼出 outxxx參數(shù) 比如 寬、高
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(resources,id,options);
        //根據(jù)寬、高進行縮放
        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中有一個LruCache是基于最記最少使用算法實現(xiàn)的一個線程安全的數(shù)據(jù)緩存類,當(dāng)超出設(shè)定的緩存容量時,優(yōu)先淘汰最近最少使用的數(shù)據(jù)LruCache的LRU緩存策略是利用LinkedHashMap來實現(xiàn)的,并通過封裝get/put等相關(guān)方法來實現(xiàn)控制緩存大小以及淘汰元素,但不支持為null的key和value。 我們可以使用JakeWharton提供的一個開源庫github.com/JakeWharton… 來實現(xiàn)我們圖片緩存的邏輯

省略了內(nèi)存和磁盤的部分。

到此這篇關(guān)于Android 性能優(yōu)化系列之bitmap圖片優(yōu)化的文章就介紹到這了,更多相關(guān)Android 性能優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • android實現(xiàn)App活動定時自動跳轉(zhuǎn)效果

    android實現(xiàn)App活動定時自動跳轉(zhuǎn)效果

    本篇文章主要介紹了android實現(xiàn)App活動定時自動跳轉(zhuǎn)效果,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • Android短信驗證服務(wù)分享

    Android短信驗證服務(wù)分享

    這篇文章主要為大家詳細介紹了Android短信驗證服務(wù),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • Android延時操作的三種方法

    Android延時操作的三種方法

    這篇文章主要為大家詳細介紹了Android延時操作的三種方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • Android提高之多方向抽屜實現(xiàn)方法

    Android提高之多方向抽屜實現(xiàn)方法

    這篇文章主要介紹了Android的多方向抽屜實現(xiàn)方法,有一定的實用價值,需要的朋友可以參考下
    2014-08-08
  • Android實現(xiàn)水波紋外擴效果的實例代碼

    Android實現(xiàn)水波紋外擴效果的實例代碼

    微信曾經(jīng)推出了一個查找附近好友的功能,大致功能是這樣的:屏幕上有一個按鈕,長按按鈕的時候,會有一圈圈水波紋的動畫向外擴散,松手后,動畫結(jié)束
    2018-05-05
  • Android 以任意比例裁剪圖片代碼分享

    Android 以任意比例裁剪圖片代碼分享

    這篇文章主要介紹了Android 以任意比例裁剪圖片的相關(guān)資料,非貨不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-11-11
  • Android加載View中Background詳解

    Android加載View中Background詳解

    本文講解的是Android什么時候進行View中Background的加載,十分的詳盡,十分全面細致,附上所有代碼,這里推薦給大家,希望大家能夠喜歡。
    2015-03-03
  • Android播放多張圖片形成的一個動畫示例

    Android播放多張圖片形成的一個動畫示例

    這篇文章主要介紹了Android播放多張圖片形成的一個動畫實現(xiàn)方法,結(jié)合實例形式分析了Android逐幀播放動畫圖片及ImageView控件的相關(guān)使用技巧,需要的朋友可以參考下
    2016-10-10
  • 查看apk簽名信息方法

    查看apk簽名信息方法

    用shell寫了一個查看apk簽名的腳本,下面與大家分享下如何查看apk簽名信息,感興趣的朋友可以了解下哈
    2013-06-06
  • android Launcher AppWidget添加步驟介紹

    android Launcher AppWidget添加步驟介紹

    大家好,本篇文章主要講的是android Launcher AppWidget添加步驟介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01

最新評論

桐庐县| 安乡县| 黄石市| 修水县| 津南区| 惠安县| 当阳市| 滨海县| 南漳县| 英德市| 高清| 栾川县| 潞西市| 武威市| 禄劝| 庆阳市| 康马县| 望谟县| 张掖市| 临泉县| 晋江市| 囊谦县| 乐昌市| 万安县| 康平县| 渭源县| 政和县| 东源县| 开阳县| 荣成市| 太湖县| 昌江| 蓬溪县| 武穴市| 郧西县| 定南县| 周至县| 清流县| 衡阳县| 板桥市| 永城市|