在之前的文章中,我們介紹了模糊影象方法。我們提到要將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和模糊的效能。
本文的原始碼可以點選這裡。