View的继承关系及统计工具
效果
下面一棵树是View的统计数据,有用信息有下面几个:
- View的继承关系。
- 各View的直接使用频次。
用途:
- 结合LayoutInspector,可直接看出一个View的继承关系,是属于哪一个Layout,无需翻代码,节省大量时间
数据收集方案
数据产生
基于以下假设:
- 每个控件被创建出来必然会调用构造函数
- 每个控件必然会调到View的构造函数
- Eventlog不会被限流
方案:
在View的构造函数中android.view.View#View(Context)
的最后添加:
Class cls = this.getClass();
StringBuilder sb = new StringBuilder();
if (_pn == null) {
_pn = context.getPackageName();
}
while (cls.hashCode() != HC) {
sb.append(" ").append(cls.getName());
cls = a.getSuperclass();
}
EventLog.writeEvent(99998888, (_pn != null ? _pn : "") + ":" + sb);
其中HC
的定义为View
类的hashCode,private static final int HC = View.class.hashCode();
,_pn
为包名。
引入这两个变量目的是减少性能损耗,因为这个函数会被很频繁调用。
通过while
循环不断寻找父类,直到View
(基类),用Stringbuilder
记录,并用特殊Tag值写入EventLog。
运行时控制台输出
通过adb logcat -b events |grep 99998888
打印
06-27 15:49:52.033 16129 16129 I [99998888]: com.sankuai.meituan: com.sankuai.waimai.mach.widget.e android.widget.TextView
06-27 15:49:52.033 16129 16129 I [99998888]: com.sankuai.meituan: com.sankuai.waimai.mach.widget.MachViewGroup android.widget.FrameLayout android.view.ViewGroup
06-27 15:49:52.033 16129 16129 I [99998888]: com.sankuai.meituan: com.sankuai.waimai.mach.widget.e android.widget.TextView
06-27 15:49:52.033 16129 16129 I [99998888]: com.sankuai.meituan: com.sankuai.waimai.mach.widget.MachViewGroup android.widget.FrameLayout android.view.ViewGroup
06-27 15:49:52.033 16129 16129 I [99998888]: com.sankuai.meituan: com.sankuai.waimai.mach.widget.e android.widget.TextView
数据收集加工
使用python脚本编写,核心是文本处理和利用tree的数据结构记录数据。
- 原始日志:
06-27 15:49:52.035 25090 25090 I [99998888]: com.huawei.browser: com.airbnb.lottie.LottieAnimationView androidx.appcompat.widget.AppCompatImageView android.widget.ImageView
- 文本处理:
com.huawei.browser: com.airbnb.lottie.LottieAnimationView androidx.appcompat.widget.AppCompatImageView android.widget.ImageView
- 信息分割:
pkgName : com.huawei.browser
继承关系:com.airbnb.lottie.LottieAnimationView ---> androidx.appcompat.widget.AppCompatImageView ---> android.widget.ImageView
- 数据结构构建:
建立包名映射Tree的dict,分包名进行数据收集。并建立一个不区分包名的Tree
for 类名 in range(继承关系list):
if 类名 not in tree:
创建treeNode
找到treeNode更新频次数据
-
定时打印并导出到文件
-
最终输出
all:
0[View]
├── 657[]
├── 5[android.view.SurfaceView]
│ ├── 1[com.taobao.android.detail.core.detail.kit.view.widget.base.chronometer.ChronometerView]
│ └── 0[com.tencent.mapsdk.internal.sx]
│ └── 1[com.tencent.mapsdk.internal.ta]
├── 0[android.view.TextureView]
│ ├── 1[com.autohome.mediaplayer.widget.videoview.TextureRenderView]
│ ├── 0[com.kwai.framework.player.ui.TextureViewCompat]
│ │ └── 3[com.kwai.framework.player.ui.TextureViewProxy]
│ ├── 0[com.meituan.android.edfu.edfupreviewer.surface.a]
│ │ └── 1[com.meituan.android.edfu.edfupreviewer.surface.c]
├── 0[android.view.ViewGroup]
│ ├── 1531[android.widget.FrameLayout]
│ │ ├── 5[android.widget.HorizontalScrollView]
│ │ │ ├── 0[com.autohome.commonlib.view.tabbar.AHWrapperHorizontalScrollView]
│ │ │ │ └── 0[com.autohome.commonlib.view.tabbar.AHBaseSlidingTabBar]
│ │ │ │ └── 0[com.autohome.commonlib.view.tabbar.AHSlidingTabBar]
│ │ │ │ └── 1[com.autohome.commonlib.view.tabbar.AHViewPagerTabBar]
│ │ ├── 1[android.widget.ScrollView]
│ │ │ ├── 1[com.facebook.react.views.scroll.ReactScrollView]
│ │ │ ├── 2[com.facebook.react.views.scroll.e]
│ │ │ ├── 1[com.jd.lib.productdetail.view.PDScrollView]
│ │ │ └── 1[com.taobao.tao.flexbox.layoutmanager.view.ObservableScrollView]
com.taobao.taobao:
0[View]
├── 71[]
├── 2[android.support.v7.widget.ViewStubCompat]
├── 0[android.view.SurfaceView]
│ └── 1[com.taobao.android.detail.core.detail.kit.view.widget.base.chronometer.ChronometerView]
├── 0[android.view.TextureView]
│ └── 1[com.taobao.avplayer.player.DWTextureView]
├── 0[android.view.ViewGroup]
│ ├── 0[android.support.v4.view.ViewPager]
│ │ ├── 1[com.taobao.android.searchbaseframe.xsl.page.rtl.RtlViewPager]
│ │ ├── 0[com.taobao.tao.flexbox.layoutmanager.view.edgepan.EdgePanViewPager]
│ │ │ └── 3[com.taobao.tao.flexbox.layoutmanager.view.NestViewPager]
│ │ └── 1[com.taobao.tao.topmultitab.view.HomeViewPager]
│ ├── 0[android.support.v7.widget.AbsActionBarView]
│ │ └── 1[android.support.v7.widget.ActionBarContextView]
│ ├── 1[android.support.v7.widget.ActionBarOverlayLayout]
│ ├── 0[android.support.v7.widget.LinearLayoutCompat]
│ │ └── 1[android.support.v7.widget.ActionMenuView]
│ ├── 0[android.support.v7.widget.RecyclerView]
│ │ ├── 1[com.taobao.android.dinamicx.view.DXNativeRecyclerView]
│ │ │ └── 2[com.taobao.android.dinamicx.view.DXNativeAutoLoopRecyclerView]
│ │ ├── 0[com.taobao.nestedscroll.recyclerview.AbstractRecyclerView]
│ │ │ ├── 0[com.taobao.nestedscroll.recyclerview.ChildRecyclerView]
│ │ │ │ └── 1[com.taobao.tao.recommend4.recyclerview.HomeChildRecyclerView]
│ │ │ └── 0[com.taobao.nestedscroll.recyclerview.ParentRecyclerView]
│ │ │ └── 1[com.taobao.android.cart.widget.CartRecyclerView]
...