-
-
[原创]微信Duilib的List优化和代价
-
发表于:
2024-7-13 11:19
2788
-
Duilib
Duilib是Windows上一个小巧的C++界面库
微信和企业微信PC端的界面都是基于Duilib魔改
企业微信Duilib魔改出bug:[原创]企业微信转发卡bug的漏洞分析
微信Duilib魔改List达到十倍甚至百倍的优化,可惜没开源,好奇分析了一下
RecyclerView
数据较多但界面有限的问题,比如100个好友最多显示10个
原生Duilib是界面上准备100个位子,10个可见,90个不可见
微信Duilib则是界面上准备10个位子,轮流给10个可见的好友用
上面一个位子空出来,回收成最后一个位子,这就是RecyclerView
继承关系
1、原生Duilib的List是:
CListUI -> CVerticalLayoutUI -> CContainerUI
1 2 3 | class UILIB_API CListUI : public CVerticalLayoutUI
class UILIB_API CVerticalLayoutUI : public CContainerUI
|
2、微信Duilib的List是:
mmListView -> mmVirtualViewBase -> CVerticalLayoutUI -> CContainerUI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int __thiscall sub_AE21A0( int this )
sub_AEB0E0( this );
* this = &mmListView::`vftable';
char *__thiscall sub_AEB0E0( char * this )
sub_1EEFFA0( this );
* this = &mmVirtualViewBase::`vftable';
char *__thiscall sub_1EEFFA0( char * this )
sub_1ED3C30( this );
* this = &DuiLib::CVerticalLayoutUI::`vftable';
char *__thiscall sub_1ED3C30( char * this )
sub_1EC8CC0( this );
* this = &DuiLib::CContainerUI::`vftable';
|
微信Duilib比原生Duilib多了一层mmVirtualViewBase,实际上就是RecyclerView
因为mmListView和CListUI都继承CVerticalLayoutUI ,布局文件XML可以无缝替换
1 2 | < List >...</ List >
< mmListView >...</ mmListView >
|
此外,mmGridView和mmGroupTileView也是继承mmVirtualViewBase
1 2 3 4 5 6 7 | int __thiscall sub_ADBA00( int this )
sub_AEB0E0( this );
* this = &mmGridView::`vftable';
char *__thiscall sub_AEB0E0( char * this )
sub_1EEFFA0( this );
* this = &mmVirtualViewBase::`vftable';
|
1 2 3 4 5 6 7 | int __thiscall sub_ADDB90( int this )
sub_AEB0E0( this );
* this = &mmGroupTileView::`vftable';
char *__thiscall sub_AEB0E0( char * this )
sub_1EEFFA0( this );
* this = &mmVirtualViewBase::`vftable';
|
界面滚动
1、原生Duilib只是更新坐标
1 2 3 4 5 6 7 8 9 | void CContainerUI::SetScrollPos(SIZE szPos, bool bMsg)
for ( int it2 = 0; it2 < m_items.GetSize(); it2++ )
CControlUI* pControl = static_cast <CControlUI*>(m_items[it2]);
RECT rcPos = pControl->GetPos();
rcPos.left -= cx;
rcPos.right -= cx;
rcPos.top -= cy;
rcPos.bottom -= cy;
pControl->SetPos(rcPos);
|
2、微信Duilib则是更新坐标和回收复用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | int __thiscall sub_AEC570( char * this , int offset)
while ( index < GetSize(v4) )
v8 = sub_1EB5090(v7, index);
v9 = (*(*v8 + 88))(v8);
(*(*v8 + 92))(v8, *v9 - 0.0, v9[1] - offset, v9[2] - 0.0, v9[3] - offset);
(*(v10 + 784))(v2, 1);
int __thiscall sub_AE3C70( float * this , char a2)
if ( rcItem.bottom < rcList.top )
|
列表变动
1、原生Duilib是修改计数
1 2 3 4 5 6 7 8 9 10 11 | CStdPtrArray m_items;
class CStdPtrArray
{
LPVOID * m_ppVoid;
int m_nCount;
int m_nAllocated;
}
bool CStdPtrArray::Add( LPVOID pData)
++m_nCount
|
2、微信Duilib是修改指针
元素变动
数据上可以直观看到回收复用
细节1:界面显示6个,第7个预备着
细节2:第1个回收,直接复用为第8个
也就是列表下面总有一个准备好的元素
异类元素
如果元素类型都一样,可以直接回收复用
但如果元素类型不一样,插入异类元素,就得特殊处理
微信Duilib的处理方法是:创建异类元素(黄色),保留原始元素(红色)
简单粗暴的方法是把多种元素整合成复合元素,代价就是控件空闲率更高
目前微信Duilib控件空闲率比较高:(1843-496)/1843=73%
而企业微信Duilib控件空闲率更高:(3210-761)/3210=76%
带来代价
有意思的是,微信Duilib的优化也带来了代价
RecyclerView需要先空出位置,才能加入新元素
这就导致滚动的偏移上限不能超过一个元素的大小
微信Duilib需要处理的次数也就变多,用时间换空间
架构模式
RecyclerView解决的问题是数据多界面小
解决这个问题的的办法就是把数据和界面分离开
对应的架构模式就可以从MVC变成MVI,安全性更好
微信用的是MVI:if(model.status == SELECTED)
企业微信用的是MVC:if(view.status == SELECTED)
当界面view出漏洞时,MVI不依赖view的数据而变得更安全
相互借鉴
1、电脑端借鉴移动端
微信Duilib里面有字符串:"Recyclebin::addActiveView"
搜索了一下发现是移动端Android源码里面ListView的内部类RecycleBin
ListView的优化版RecyclerView在微信Duilib里面没搜到,改名成mmVirtualViewBase
2、移动端借鉴电脑端
再搜索VirtualView,发现了阿里Android的Tangram框架提供VirtualView
VirtualView的核心思想是:编写xml样式文件,在客户端解析直接绘制出界面
这个设计思想和电脑端的Duilib一样:DirectUI,在主界面上直接绘制出所有控件
参考资料
实战 | 认识 RecyclerView
图解 RecyclerView 的缓存机制
RecyclerView | Android Developers
ListView源码阅读:RecycleBin缓存机制以及二次onLayout_activeview引用
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2024-7-27 16:13
被GhHei编辑
,原因: