【开源毕设】一款精美的家校互动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,转载请珍惜作者的劳动成果,谢谢。
作 者:
南 尘
出 处: http://www.cnblogs.com/liushilin/
关于作者:专注于移动前端的项目开发。如有问题或建议,请多多赐教!欢迎加入Android交流群:118116509
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章下部【推荐】或侧边【关注】。您的鼓励是作者坚持原创和持续写作的最大动力!
欢迎关注我的公众号,精讲面试、算法、Andrid、Java、Python,旨在打造全网最比心的公众号。