1. 查找卡顿原因
从上面的现象来看,应该是主线程执行了耗时操作引起了卡顿,因为正常滑动是没问题的,只有在刷新数据的时候才会出现卡顿。至于什么情况下会引起卡顿,之前在自定义 View 部分已有详细讲过,这里就不在啰嗦。我们猜想可能是耗时引起的卡顿,但也不能 100% 确定,况且我们也并不知道是哪个方法引起的,因此我们只能借助一些常用工具来分析分析,我们打开 Android Device Monitor 。
图:打开 Android Device Monitor
图:查找耗时方法
2. RxJava 线程切换
我们找到了是高斯模糊处理耗时导致了界面卡顿,那现在我们把高斯模糊算法处理放入子线程中去,处理完后再次切换到主线程,这里采用 RxJava 来实现。
Observable.just(resource.getBitmap())
.map(bitmap -> {
// 高斯模糊
Bitmap blurBitmap = ImageUtil.doBlur(resource.getBitmap(), 100, false);
blurBitmapCache.put(path, blurBitmap);
return blurBitmap;
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(blurBitmap -> {
if (blurBitmap != null) {
recommendBgIv.setImageBitmap(blurBitmap);
}
});
3. 高斯模糊算法分析
把耗时操作放到子线程中去处理,的确解决了界面卡顿问题。但这其实是治标不治本,我们发现图片加载处理异常缓慢,内存久高不下有时可能会导致内存溢出。接下来我们来分析一下高斯模糊的算法实现:
看上面这几张图,我们通过怎样的操作才能把第一张图处理成下面这两张图?其实就是模糊化,怎么才能做到模糊化?我们来看下高斯模糊算法的处理过程。再上两张图:
所谓"模糊",可以理解成每一个像素都取周边像素的平均值。上图中,2是中间点,周边点都是1。"中间点"取"周围点"的平均值,就会变成1。在数值上,这是一种"平滑化"。在图形上,就相当于产生"模糊"效果,"中间点"失去细节。
为了得到不同的模糊效果,高斯模糊引入了权重的概念。上面分别是原图、模糊半径3像素、模糊半径10像素的效果。模糊半径越大,图像就越模糊。从数值角度看,就是数值越平滑。接下来的问题就是,既然每个点都要取周边像素的平均值,那么应该如何分配权重呢?如果使用简单平均,显然不是很合理,因为图像都是连续的,越靠近的点关系越密切,越远离的点关系越疏远。因此,加权平均更合理,距离越近的点权重越大,距离越远的点权重越小。对于这种处理思想,很显然正太分布函数刚好满足我们的需求。但图片是二维的,因此我们需要根据一维的正太分布函数,推导出二维的正太分布函数:
图:二维正太分布函数
图:权重处理
if (radius < 1) {//模糊半径小于1
return (null);
}
int w = bitmap.getWidth();
int h = bitmap.getHeight();
// 通过 getPixels 获得图片的像素数组
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
int r[]