【開源畢設】一款精美的家校互動APP分享——愛吖校推 [你關注的,我們才推](持續開源更新3)附高效動態壓縮Bitmap


一、寫在前面

  愛吖校推如同它的名字一樣,是一款校園類信息推送交流平台,這么多的家校互動類軟件,你選擇了我,這是我的幸運。
從第一次在博客園上寫博客到現在,我一次一次地提高博文的質量和代碼的可讀性,都是為了你們,因為有你們,才有我。 我從一個一個的demo到從0開始做這個app,一路歷經艱難險阻,期待你與我進行心靈交流。因為我也曾遇到各種棘手的問題,到處詢問不到答案, 那個時候的我,也許正如現在的你。而我,也還在這條道路上默默前行。

  前面兩期地址:【開源畢設】一款精美的家校互動APP分享——愛吖校推 [你關注的,我們才推](持續開源更新)

         【開源畢設】一款精美的家校互動APP分享——愛吖校推 [你關注的,我們才推](持續開源更新2)

  小伙伴們,寶寶又來了,是的,愛吖校推又實現了新功能,這次給大家帶來的是什么呢?這次是客服系統,推送系統和圖片發送以及動態更新的功能更新,現在APP支持圖片壓縮上傳,采用eventbus動態刷新UI,並且同一個圈子的人能收到相應人員發送的消息動態啦!!對,不僅是android,php服務器也相應得到了更新,還在猶豫什么?快下載看看吧~

二、同樣的有用之處

  

  1)通過本項目你可以拿到你想要的自定義控件效果;

  2)android純前端程序員也能了解到如何用php編寫api接口,讓項目數據動起來;

  3)有意轉后台的程序員可以學到更多邏輯方面有用的知識;

  4)對,如果你是初學者,你可能會得到一位前行良友;

  5)玩什么游戲,安心擼代碼吧!

  6)對,這不只是android,這不只是php,這還有文檔,沒錯,就是開源!!!

  7)圖片占用內存泄漏?這里有手把手教你封裝的bitmapUtil。

三、上個效果圖唄

      

四、下一步要做的

  1)加入微信小視頻功能

  2)優化演示圖中出現的內存泄漏

  3) 減小資源體積

五、幫大家安利一下開發必過的坑——圖片常常導致OOM

  1)大家一定都知道,手機相機拍照的圖片都是幾千像素的,而我們的手機屏幕卻一般是720的,並且一張大圖,動則幾M,上傳起來用戶流量着實受不了,那么壓縮資源體積就變得相當麻煩了,這里就給大家講解一下壓縮上傳的正確姿勢。

 

  2)我相信作為coder的你,一定知道Bitmap是引起OOM的罪魁禍首之一,所以我們一定為了節約內存,一般都會在服務器上緩存一個縮略圖,這樣不但可以提升下載速度,減少用戶流量,還達到了很好的節約內存的目的。

  要是我們能把bitmap設置為imageView的大小,根據要顯示的ImageView來壓縮Bitmap那肯定最好了。

  根據這樣的思路,我們肯定得首先算出imageView的寬高,這個很簡單;直接imageView.getWidth()和imageView.getHeight()方法就可以達到目的。

  3)如果你操作圖片,你一定知道BitmapFatory,因為我們通常使用它來操作圖片。

     BitmapFactory這個類提供了多個解析方法(decodeByteArray,decodeFile,decodeResource等)用來創建bitmap對象,其中sd卡圖片用decodeFile,傳入path路徑和Options就可以了,而網絡圖片我們通常使用decodeStream方法,資源文件采用decodeResource;

    然而這些方法,都會為bitmap分配內存,圖片太大一定會導致OOM的,所以我們需要先進行壓縮,使用BitmapFatory.Options。

  4)BitmapFactory.Options碎碎念

    它有一個inJustDecodeBounds屬性,當這個屬性為true的時候,調用上面三個方法返回的就不是一個完整的bitmap對象,而是null。因為它禁止這些方法為bitmap分配內存,當時設置這個屬性為true的時候,Options的outWidth,outHeight和outMimeType屬性就會被復制。這樣我們就可以在加載圖片之前獲取到圖片的長寬和MIME類型。就等於不讀取這個圖片,卻獲取到了它的參數,的確很6。

    說到這里,必須說到一個很6的屬性了,inSampleSize,可以理解為壓縮比率,設置好這個比率,就能調用上面的decodeXXXX方法獲得縮略圖了,如果圖片大小都一致,那還可以定死它,可我們的圖片卻大小不一,那我們應該如何獲得正確的inSampleSize值呢?可以通過下面的方法,動態計算。

 1   /**
 2      * @description 計算圖片的壓縮比率
 3      *
 4      * @param options 參數
 5      * @param reqWidth 目標的寬度
 6      * @param reqHeight 目標的高度
 7      * @return
 8      */
 9     private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
10         // 源圖片的高度和寬度
11         final int height = options.outHeight;
12         final int width = options.outWidth;
13         int inSampleSize = 1;
14         if (height > reqHeight || width > reqWidth) {
15             final int halfHeight = height / 2;
16             final int halfWidth = width / 2;
17             // Calculate the largest inSampleSize value that is a power of 2 and keeps both
18             // height and width larger than the requested height and width.
19             while ((halfHeight / inSampleSize) > reqHeight
20                     && (halfWidth / inSampleSize) > reqWidth) {
21                 inSampleSize *= 2;
22             }
23         }
24         return inSampleSize;
25     }

  然而,事實卻不如我們想象那么美好,inSampleSize官方注釋告訴我們一個必須注意的點:因為inSampleSize只能是2的整數次冪,意味着如果上面我們算出來inSampleSize為6的話,這時候只能向下取得整數次冪,就是4。這樣設計的原因很可能是為了漸變bitmap壓縮,畢竟按照2的次方進行壓縮會比較高效和方便。

  那遇上這樣的問題,肯定是無法達到我們想要的效果的,比如我們計算出來的inSampleSize是15,向下取就成了8,明顯差距太大。那有沒有一種方法達到我們的效果呢?

  答案是肯定的!

  5)再次壓縮

  別忽略了Bitmap有這么一個方法:createScaleBitmap!!

  這個方法可以給我們按照要求拉伸/縮小一個bitmap,我們可以通過這個方法把我們之前得到的較大的縮略圖進行縮小,讓其完全符合我們的需求。

  

 1 /**
 2      * @description 通過傳入的bitmap,進行壓縮,得到符合標准的bitmap
 3      *
 4      * @param src
 5      * @param dstWidth
 6      * @param dstHeight
 7      * @return
 8      */
 9     private static Bitmap createScaleBitmap(Bitmap src, int dstWidth, int dstHeight, int inSampleSize) {
10         //如果inSampleSize是2的倍數,也就說這個src已經是我們想要的縮略圖了,直接返回即可。
11         if (inSampleSize % 2 == 0) {
12             return src;
13         }
14         // 如果是放大圖片,filter決定是否平滑,如果是縮小圖片,filter無影響,我們這里是縮小圖片,所以直接設置為false
15         Bitmap dst = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, false);
16         if (src != dst) { // 如果沒有縮放,那么不回收
17             src.recycle(); // 釋放Bitmap的native像素數組
18         }
19         return dst;
20     }

  或許你會問,為啥要先從inSampleSize產生一個縮略圖A,而不直接通過createScaseBitmap()方法縮放呢?

  因為如果要從原始的圖片直接縮放的話,就需要把原始圖片直接放入內存 中,這將十分危險!!!先通過inSmapleSize得到一個較大的縮略圖,它會比原圖小很多,直接加載到內存中再進行拉伸/縮小就比較安全了!

  6)完整代碼:

  1 package com.example.nanchen.aiyaschoolpush.utils;
  2 
  3 import android.content.res.Resources;
  4 import android.graphics.Bitmap;
  5 import android.graphics.BitmapFactory;
  6 
  7 /**
  8  *
  9  * bitmap相關操作工具類
 10  *
 11  * @author nanchen
 12  * @fileName AiYaSchoolPush
 13  * @packageName com.example.nanchen.aiyaschoolpush.utils
 14  * @date 2016/11/28  09:45
 15  */
 16 
 17 public class BitmapUtil {
 18 
 19     /**
 20      * @description 計算圖片的壓縮比率
 21      *
 22      * @param options 參數
 23      * @param reqWidth 目標的寬度
 24      * @param reqHeight 目標的高度
 25      * @return
 26      */
 27     private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
 28         // 源圖片的高度和寬度
 29         final int height = options.outHeight;
 30         final int width = options.outWidth;
 31         int inSampleSize = 1;
 32         if (height > reqHeight || width > reqWidth) {
 33             final int halfHeight = height / 2;
 34             final int halfWidth = width / 2;
 35             // Calculate the largest inSampleSize value that is a power of 2 and keeps both
 36             // height and width larger than the requested height and width.
 37             while ((halfHeight / inSampleSize) > reqHeight
 38                     && (halfWidth / inSampleSize) > reqWidth) {
 39                 inSampleSize *= 2;
 40             }
 41         }
 42         return inSampleSize;
 43     }
 44 
 45     /**
 46      * @description 從Resources中加載圖片
 47      *
 48      * @param res
 49      * @param resId
 50      * @param reqWidth
 51      * @param reqHeight
 52      * @return
 53      */
 54     public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
 55         final BitmapFactory.Options options = new BitmapFactory.Options();
 56         options.inJustDecodeBounds = true; // 設置成了true,不占用內存,只獲取bitmap寬高
 57         BitmapFactory.decodeResource(res, resId, options); // 讀取圖片長款
 58         options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 調用上面定義的方法計算inSampleSize值
 59         // 使用獲取到的inSampleSize值再次解析圖片
 60         options.inJustDecodeBounds = false;
 61         Bitmap src = BitmapFactory.decodeResource(res, resId, options); // 載入一個稍大的縮略圖
 62         return createScaleBitmap(src, reqWidth, reqHeight, options.inSampleSize); // 進一步得到目標大小的縮略圖
 63     }
 64 
 65     /**
 66      * @description 通過傳入的bitmap,進行壓縮,得到符合標准的bitmap
 67      *
 68      * @param src
 69      * @param dstWidth
 70      * @param dstHeight
 71      * @return
 72      */
 73     private static Bitmap createScaleBitmap(Bitmap src, int dstWidth, int dstHeight, int inSampleSize) {
 74         //如果inSampleSize是2的倍數,也就說這個src已經是我們想要的縮略圖了,直接返回即可。
 75         if (inSampleSize % 2 == 0) {
 76             return src;
 77         }
 78         // 如果是放大圖片,filter決定是否平滑,如果是縮小圖片,filter無影響,我們這里是縮小圖片,所以直接設置為false
 79         Bitmap dst = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, false);
 80         if (src != dst) { // 如果沒有縮放,那么不回收
 81             src.recycle(); // 釋放Bitmap的native像素數組
 82         }
 83         return dst;
 84     }
 85 
 86     /**
 87      * @description 從SD卡上加載圖片
 88      *
 89      * @param pathName
 90      * @param reqWidth
 91      * @param reqHeight
 92      * @return
 93      */
 94     public static Bitmap decodeSampledBitmapFromFile(String pathName, int reqWidth, int reqHeight) {
 95         final BitmapFactory.Options options = new BitmapFactory.Options();
 96         options.inJustDecodeBounds = true;
 97         BitmapFactory.decodeFile(pathName, options);
 98         options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
 99         options.inJustDecodeBounds = false;
100         Bitmap src = BitmapFactory.decodeFile(pathName, options);
101         return createScaleBitmap(src, reqWidth, reqHeight, options.inSampleSize);
102     }
103 }

六、寫在最后

  好噠,第三期就介紹到這里啦,喜歡的話就去github下載下來體驗吧~(PS:樓主服務器目前是采用xampp搭建的本地服務器,外網暫無法訪問,需要體驗效果請自行搭建服務器,見樓主前面博客:【定有驚喜】android程序員如何做自己的API接口?php與android的良好交互(附環境搭建),讓前端數據動起來~

  github地址:https://github.com/nanchen2251/AiYaSchoolPush

  開源不易,別忘了點點推薦,點點star,轉載請珍惜作者的勞動成果,謝謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM