在之前的文章中,我们介绍了模糊图像方法。我们提到要将blur
方法放在布局阶段。这样能确保只有在布局变化时模糊操作才会被调用,而不是在onDraw()
中调用。 为什么不能在onDraw()
中调用?这篇文章会从测试的角度来解释这一问题。
我们之前提到过一个非常有用的测试类:TimingLogger
类,可用于性能测试并发现瓶颈。日志在debug中非常有用,但是由于需要将结果输出为字符串,并且需要一些I/O操作来输出结果会间接影响测试结果,所不太适合性能调优。TimingLogger
会在测试代码段执行之前创建一个TimingLogger
对象,并且在代码执行过程中调用TimingLogger
类的addSplit()
方法来添加测试断点。addSplit()
方法是非常轻量级的,并且不会在代码执行中输出结果。当代码执行之后,我们可以用dumpToLog()
方法输出测试结果。这样一来,只会在代码运行的过程中使用addSplit()
方法添加测试断点,而不会去执行创建Timinglogger
对象和输出测试结果这两个操作,由此提高了性能测试的准确性。
当我们添加TimingLogger
的特性时,代码将如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
; html-script: false ] private static final String TAG = "Blurring"; private void blur(Bitmap bkg, View view, float radius) { TimingLogger tl = new TimingLogger(TAG, "blur"); Bitmap overlay = Bitmap.createBitmap( view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888); tl.addSplit("Bitmap.createBitmap()"); Canvas canvas = new Canvas(overlay); tl.addSplit("new Canvas()"); canvas.drawBitmap(bkg, -view.getLeft(), -view.getTop(), null); tl.addSplit("canvas.drawBitmap()"); RenderScript rs = RenderScript.create(this); tl.addSplit("RenderScript.create()"); Allocation overlayAlloc = Allocation.createFromBitmap( rs, overlay); tl.addSplit("Allocation.createFromBitmap()"); ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create( rs, overlayAlloc.getElement()); tl.addSplit("ScriptIntrinsicBlur.create()"); blur.setInput(overlayAlloc); tl.addSplit("blur.setInput()"); blur.setRadius(radius); tl.addSplit("blur.setRadius()"); blur.forEach(overlayAlloc); tl.addSplit("blur.forEach()"); overlayAlloc.copyTo(overlay); tl.addSplit("overlayAlloc.copyTo()"); view.setBackground(new BitmapDrawable( getResources(), overlay)); tl.addSplit("view.setBackground()"); rs.destroy(); tl.addSplit("rs.destroy()"); tl.dumpToLog(); } |
如果我们运行这一代码段,在logcat
中将不会显示任何结果。这是由于TimingLogger
类的一个特性:只会在详细日志(Verbose Logging level)中显示运行结果。因此我们需要执行以下adb
指令来显示特别标签日志。
1 2 |
; html-script: false ] adb shell setprop log.tag.Blurring VERBOSE |
当再次运行代码后,将会显示来自TimingLogger
的输出结果(数据来自Nexus 5 Android 4.4.2)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
; html-script: false ] blur: begin blur: 0 ms, Bitmap.createBitmap() blur: 0 ms, new Canvas() blur: 1 ms, canvas.drawBitmap() blur: 4 ms, RenderScript.create() blur: 11 ms, Allocation.createFromBitmap() blur: 0 ms, ScriptIntrinsicBlur.create() blur: 0 ms, blur.setInput() blur: 0 ms, blur.setRadius() blur: 0 ms, blur.forEach() blur: 26 ms, overlayAlloc.copyTo() blur: 0 ms, view.setBackground() blur: 5 ms, rs.destroy() blur: end, 47 ms |
这里总共需要的时间为47ms,似乎并不是太久。但当相比RenderScript在Adreno 330 GPU(Nexus 5 Snapdragon 800 Soc)执行的结果来看,这已经是很好的结果了。现实的问题是,当把模糊操作加入到onDraw()
方法中时,会毁坏动画甚连向下滑动ListView
的动画都无法做到。出现这个问题的原因是,模糊操作对于每一帧都需要47ms,z这会将帧速率(frame rate)降到20 fps(帧/秒)。如果考虑到动画和绘制需要的时间,实际的帧速率会更低。如果在效率更低的设备上情况将会更糟糕。
大多游戏需要30-60fps,所以如果在onDraw()
中使用模糊操作,程序画面将不会平滑的展现出来。在之后的系列中,我们将介绍更多的可行的在动画过程中优化模糊操作的方法。
现在我们可以测量模糊操作的性能,在下一篇文章中我们将会比较在纯Java环境中,RenderScript和模糊的性能。
本文的源代码可以点击这里。