-
前言
前段时间QQ更新后发现下面的Tab栏添加了动态高斯模糊效果,众所周知,高斯模糊这玩意儿比较耗时,动态的模糊效果在安卓的APP中比较少见。在自己猜测了几种做法之后想知道QQ是怎么实现的,于是反编译了一下QQ的apk。
鉴于我的逆向基础门都没入,属于只会用一个jadx查查16进制id这种,这里就不班门弄斧介绍了,感兴趣的可以自己去搜搜类似的文章看看。不过这里不得不说QQ的措施做得真好,它里面的所有控件id,资源id,layout的命名混淆后大部分是name,想通过查找id来寻找代码文件对我来说基本不可能,曾经反编译过网易云音乐的app,它就没有做这项措施,可以轻易的通过uiautomatorviewer
工具查找到id后定位到具体的代码文件。下面可以看看最后的效果。 -
效果
gif图录制时会变糊成一团,下面再附一张图片
废话说完,下面就看看如何做到的吧
1.定位
首先可以知道,这个模糊的效果不是自定义view
就是拿到具体视图模糊后给Tab
当背景,这里可以使用uiautomatorviewer
看看这个页面的层级和视图,如下图
可以很清晰的看到在最下面的TabWidget
下面还有个自定义View
,但是id被混淆成了name,因而定位代码就基本不可能了(仅对我而说)。
2.反编译
既然知道这是个自定义View
,那么我们就可以试着反编译APK去查找代码了,因为混淆后的自定义view的类名是不会变的。从上图可以看到该View的位于com.tencent.mobileqq
下面,于是试着在这个包名下面寻找一下代码。
这里用最简单的jadx打开QQ的apk后就可以看到QQ混淆后的代码了。几十个包名这里就不上图演示了,鉴于对QQ团队的代码素养的信任,在mobileqq下面直接锁定的widget这个包,然后同样鉴于对QQ团队代码命名素养的信任,我着重寻找类似blur
或者gauss
这样的字眼,果然找到了两个文件QQBlur
和QQBlurView
找到后就是苦力活了,因为代码是混淆的,需要把相关的代码倒腾出来再去分析。这里让我最蛋疼的就是这个QQBlur
的代码了。
public class QQBlur$1 implements Runnable{
private int a = -1;
/* renamed from: a */
final /* synthetic */ StackBlurManager f541a;
final /* synthetic */ azlc this$0;
public QQBlur$1(azlc azlc, StackBlurManager stackBlurManager) {
this.this$0 = azlc;
this.f541a = stackBlurManager;
}
public void run() {
if (!this.this$0.f531b) {
long elapsedRealtime = SystemClock.elapsedRealtime();
if (!(this.a == -1 || this.a == azlc.a)) {
this.this$0.a(this.a, azlc.a);
}
this.a = azlc.a;
int i = azlc.a;
Bitmap process = this.f541a.process(this.this$0.f531b);
if (process != null) {
this.this$0.f519a = process;
} else {
QLog.e("QQBlur", 1, "run: outBitmap is null. OOM ?");
}
long elapsedRealtime2 = SystemClock.elapsedRealtime();
this.this$0.f531b;
this.this$0.f = (elapsedRealtime2 - elapsedRealtime) + this.this$0.f;
View a = this.this$0.f531b;
if (a != null && this.this$0.f) {
a.postInvalidate();
}
}
}
这里可以看到这个f531b
(其实在混淆后这个命名是b,前面的531是jadx软件为了和其他b命名区分自己添加的)既可以是boolean
也是process
方法的int类型,下面还变成了View
。
花了一个周末的时间对整个代码进行了逻辑分析和重新命名后,后面展示的代码就是根据我自己的理解重新命名的的类和变量名,如果有想看原混淆代码的可以自己去反编译,或者看我上传的一份。
3.代码分析
先看看QQBlurView
这个类的代码,下面代码为了方便查看和理解,是经过我自己的重新命名后的,想看混淆过的源码传送门这边走QQBlurView
。
@TargetApi(19)
public class QQBlurView extends View {
//因为这个模糊效果只支持19以上,所以这个drawable是低于19的情况下显示的tab背景
private Drawable mDefaultDrawable;
private BlurPreDraw mBlurPreDraw = new BlurPreDraw(this);
public QQBlurManager mManager = new QQBlurManager();
private boolean mEnableBlur = true;
......
protected void onDraw(Can