一、寫在前面
愛吖校推如同它的名字一樣,是一款校園類信息推送交流平台,這么多的家校互動類軟件,你選擇了我,這是我的幸運。
從第一次在博客園上寫博客到現在,我一次一次地提高博文的質量和代碼的可讀性,都是為了你們,因為有你們,才有我。 我從一個一個的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,轉載請珍惜作者的勞動成果,謝謝。