如何應對產品形態與產品節奏相對確定情況下轉變為『在業務需求與產品形態高度不確定性的情況下,如何實現業務交付時間與交付質量的確定性』。我們希望通過混合架構(Native 業務容器 + Weex 2.0)作為未來交易終端架構的重要演進方向,在 Native 容器側充分發揮原生語言的性能優勢、常駐 App 的調控與管控能力、手勢識別與交互優勢來解決體驗問題。本專題《淘寶交易終端架構探索》是我們摸索出的部分實踐總結,歡迎大家一起交流進步。
第一篇:《Weex購物車長列表橫滑操作優化“編年史”》
第二篇:《淘寶頁面首幀優化的經驗和心得》
第三篇:《淘寶App交易鏈路終端混合場景體驗探索》
第四篇:《淘寶訂單列表
Fragment轉場動畫卡頓解決方案
》
第五篇:《探索淘寶購物車SurfaceView閃黑的解決方案》(本篇)
引言
購物車的技術架構一直是DX+Native,在業務快速迭代的今天,可能不滿足迭代效率,需要探索一種性能佳+迭代快的方式,由此開始探索購物車Weex化。
在進入主TabWeex購物車(SurfaceView)之后,我們發現切換到其他Tab(如:首頁),再回到購物車,兩次切換都能看到明顯的閃黑。
,時長00:09
SurfaceView閃黑的原理是什么?
SurfaceView繼承自 View 類,但與普通的 View 不同,它內部使用了一個獨立的繪圖表面(即 Surface),這個 Surface 可以在主 UI 線程之外的線程上進行控制和繪制。這樣做的好處是,即使繪制操作很復雜,也不會影響到 UI 線程的響應性和流暢性。
View視圖位于自繪視圖Render Surface下方,通過對自繪視圖的Render Surface挖洞來透出下面的View視圖。
Tab切換頁面的原理是FragmentManager的attach和detach,會觸發Fragment根布局的add/remove,最終導致SurfaceView從View樹上添加/移除,進而影響SurfaceView的可見性。
閃黑問題本質上是:SurfaceView的可見性發生變化時,會導致其內部的Surface被銷毀/重建。在沒有內容呈現的情況下,SurfaceView顯示為黑色。
什么時候SurfaceView可見性會變化?
SurfaceView被removeView,如:主Tab切換
Activity前后臺切換,如:打開詳情/回到手機Home
Activity銷毀,如:finish頁面
DX+Native購物車Tab切換為什么沒有閃的問題呢?
DX采用原生View繪制,Fragment頁面切換受VSync信號和MessageQueue的同步屏障控制,確保在執行完performTraversals()(即View的測量、布局和繪制三大流程)之后,才進行下一步操作,這樣可以保證UI的一致性。
方案
由于SurfaceView的特性,可見性變化的時候,就可能導致黑屏。業內有幾種解決辦法:
頁面切換不執行remove SurfaceView,只執行visible/gone
蓋View,遮住黑屏
Texture/Image->Surface切換
?Fragment不銷毀
概述
主Tab頁面切換,由FragmentManager的attach/detach改為show/hide,避免SurfaceView可見性變化
效果
Tab切換不會閃。
,時長00:07
方案細節
修改Fragment切換方式:從attach/detach改為show/hide
show/hide不會觸發購物車Fragment的生命周期回調,SurfaceView也不會從view樹中移除,因此切換不會閃。
回到主Tab的場景,如果采用
FragmentManagerattach/detach,會觸發Fragment生命周期回調(onDestroyView),內部會把SurfaceView從view樹上移除,從而觸發onWindowVisibilityChanged,最終導致Surface被destroy,而調用show/hide并不會觸發onWindowVisibilityChanged,所以不會導致Surface銷毀。
?截屏
概述
從購物車切換到其他Tab時,保存購物車的截屏,再次從其他Tab進入購物車的時候,先顯示截屏View,待SurfaceView開始渲染之后,再隱藏截屏View,顯示SurfaceView。
效果
Tab切換不會閃,但截屏有API系統版本限制(Android 8.0,API 26)
,時長00:07
方案細節
核心:SurfaceView從View樹中移除,內部會銷毀當次Surface,所以要想成功截屏必須在這個銷毀動作執行之前。
截屏:在Tab切換Preload階段/back鍵觸發時截圖,期間會卡主主線程(設置超時,如:100ms),在截屏完成之后(顯示截屏兜底圖),再切換Fragment
購物車切到其他Tab,Nav Preload階段截圖并顯示兜底圖
// 切換到非購物車Tab回調
private val mTabOtherCallback: ()-> Unit = {
screenShot()
}
// back鍵返回首頁
override fun onBackPress(): Boolean {
screenShot()
return false
}
// 偽代碼
private val screenShot() {
val bitmap = requestBitmap()
// 添加視圖bitmap
mScreenImageView?.setImageBitmap(bitMap)
mScreenImageView?.setBackgroundColor(if (isSuccess) Color.TRANSPARENT else Color.WHITE)
mScreenImageView?.visibility = View.VISIBLE
}
回到購物車,weex視圖開始繪制,移除兜底圖
override fun onWeexUiDisplayed() {
if (mScreenImageView?.visibility == View.VISIBLE) {
mScreenImageView?.visibility = View.GONE
}
}
3. PixelCopy.request存在版本問題,只能在android 8.0及以上版本生效
api限制問題
由于PixelCopy.request截圖存在系統版本限制,因此針對低于android8.0版本(如:Y67),做如下處理:
上述方案中購物車切換到其他Tab的時候,由顯示截圖改為顯示白色背景圖
顯示和隱藏兜底圖的時機不變
改動的實質是避免SurfaceView切換帶來的閃黑問題,添加了白色背景圖,從體感上沒有那么的突兀
api限制另一種解決思路
通過Weex提供獲取SurfaceView bitMap方式:
圖片未采樣,內存占用太高
圖片應該用的是ARGB,購物車沒有alpha透明度,可以直接用RGB,內存占用會更小一些
仍然存在耗時問題
列表滾動的時候,切換tab,需要先停止列表,然后再截圖
影響
性能損耗:從購物車切換到其他Tab,會執行PixelCopy.request截圖本身是耗時的,會卡主主線程,有一定的耗時
截圖對內存有輕微影響,但對Java Heap影響不大
如果購物車列表處于滑動/動畫/下拉/上拉等非靜止狀態,切換到其他Tab,再回到購物車,會導致截的圖不是最后一幀,視覺上會導致頁面抖動
Android低系統版本體驗有損,截圖降級為白色背景圖
?Image->Surface轉換
概述
從購物車去到其他Tab,先切換成ImageView,同步渲染完,再跳轉其他Tab;從其他Tab回到購物車,ImageView先上屏,再切換成SurfaceView。
效果
其他Tab進入購物車:iv->sv無白幀,很絲滑,購物車切到其他Tab:sv->iv有較多的白幀(待解決,參考了UC Hummer的方案)