之前在调试flash 11在ICS上的使用效果时(这个版本使用的是surfaceView,市场上下的,基本上都是用nativeWindow了),发现一个bug,bug的现象如图1所示(访问http://www.qiaqiafood.com,页面主体是一个flash):
图1:错误的图片
图2:正常的图片
Webpage加载时,没有问题,但当将网页上下拖动后,就出现如图1所示的黑色区域,且图像显示的层叠关系也不正常。
该问题则追踪过程不细说,也花了好久。对这个问题:
1. 通过dump framebuffer的数据:
$adb shell cat/dev/graphics/fb0 > framebuffer.bin查看结果,framebuffer中确实是花的,和屏幕上表现的一致,这不意外
2. 接上hierarchyviewer,分析framebuffer,却发现这是好的
3. 通过screencap -p /sdcard/screenshot.png 发现也是好的
有现象和重现方法,接下来分析原因了。
从现象上看,这个问题是与画图,也就是surfaceFlinger有关的。查看surfaceFlinger的状态:
#dumpsys SurfaceFlinger
重点关注下BrowserActivity:
+ Layer 0x95cc0(com.android.browser/com.android.browser.BrowserActivity) z= 21025, pos=(0,0),size=(1024, 600), isOpaque=0, needsDithering=0, invalidate=0, alpha=0xff,flags=0x00000000, tr=[1.00, 0.00][0.00, 1.00] client=0x918a8, identity=40 format= 1, activeBuffer=[1024x 600:1024, 1], transform-hint=0x00, queued-frames=0 mBufferCount=2, mSynchronousMode=1, default-size=[1024x600],mPixelFormat=1, mTexName=3 current: {crop=[0,0,-1,-1], transform=0x00, current=1} next : {crop=[0,0,-1,-1],transform=0x00, FIFO(0)={}} [00] state=FREE ,crop=[0,0,-1,-1], transform=0x00, timestamp=16875976105982, 0x919b0 [1024x600:1024, 1] >[01] state=QUEUED ,crop=[0,0,-1,-1], transform=0x00, timestamp=16877388086666, 0x8b6a0 [1024x600:1024, 1] Region transparentRegion (this=0x95e60, count=1) [ 0, 211, 1014, 715] Region transparentRegionScreen (this=0x95cf4, count=1) [ 0, 211, 1014, 715] RegionvisibleRegionScreen (this=0x95cd0, count=2) [ 0, 0, 1024, 211] [1014, 211, 1024, 552]仔细分析输出内容,可以发现一个问题:browser的区域是有一个透明区域:
[ 0,211, 1014, 715]
这个区域把整个activity(对应的surface),划分为两个可见区域:
[ 0, 0, 1024, 211]
[1014, 211, 1024, 552]
Scroll一下browser的页面,再查看BrowserActivity的状态,两次一样,这个透明区域的位置坐标竟然没有随着scroll跟着改变!
顺着surfaceView的创建和render过程,发现问题了:
Android将surfaceView(一般的Game, video及browserplugin会使用的一种view)在其layout的区域,会设成transparent(也就是挖洞), 但是其设置只是在attachWindow的时候做的。这样造成的问题是:transparent的区域是固定的,当一个页面scroll的时候,不会更新这个transparent区域的boundary,最终给hwComposer合成的boundary,和scroll之后,surface的内容对不上,造成hwcomposer合错。
知道问题后,解决方法也可以知道了。
常规做法:让每次layout的遍历viewHierarchy时都做transparent的重置, 但这个影响面太大且较复杂(还没找到可行的修改方法)
workaround做法:给hwComposer的composer区域,不考虑transparent区域即可 (目前测试,还没看到会造成系统有什么不好的的影响),让它把surface的区域全部拿去计算(理论上效率可能有影响,但没观测出来影响)
修改方法,注释掉setGeometry@LayerBase.cpp中对visibleRegionScreen的赋值:
hwcl->visibleRegionScreen.rects = reinterpret_cast<hwc_rect_t const *>( visibleRegionScreen.getArray( &hwcl->visibleRegionScreen.numRects));至于hierarchyviewer和screencap得到的screenshot为何是好的呢?这里可以看看screenshot的实现(在surfaceFlinger中),这个screenshot和framebuffer的合成还不是一回事,screenshot没有用到hwcomposer,只是通过openGL将各个layer直接draw到FBO中,这里就没有挖洞的区域了,实际计算的区域和workaround后差不太多了。
另外还有两个问题:
1. 在scroll 窗口和初次加载flash(会比网页慢), flash内容盖住navigation bar的问题, 如图
图3:surfaceView overlap BrowserActivity
2. 网页Scroll的时候,flash内容的scroll和browser内容不同步的问题,不同步的区域会留有黑框。
覆盖的问题,由于surfaceView默认是放在browser的surface前面的,这个无解,要解只能将surfaceView放在borwser的后面(前面说过,由于surface有实现挖洞(transparent)的功能,实际上放在后面也是可以解决的)
而不同步的问题,参看今年的google IO关于webview的介绍,这个在surfaceView上应该是无解了,Google的解决方法,是使用Hardware Accelerated,通过clipLayer/mediaLayer的方式 (openGL的纹理贴图)。相信webview插件往后都不会用surfaceView实现了吧。