《我思吃少你的胰脏》| 你的性命或正如我之复短命

by admin on 2018年10月11日

前言

是因为日益明确的精细化运营需要,网易乐得从上年起来构建大数目平台,<<无埋点数量搜集SDK>>因此立项,用于为那个数量平台提供全量,完整,准确的客户端数据.
  <<无埋点多少搜集SDK>>Android端从着手,到经验重构,逐步全面及现行曾经产生抢一年的年华了.期间从开源社区与同行中拿走了有颇有意义的艺参考,因此于这个SDK趋于完美的今日,我们吧设想将随即并在技术上的追究经验跟博分享出来.

  1. 4月16-18日,QCon京2017中外软件开发大会达到发生同事表示Android/IOS两端进行统一之技能分享,欢迎大家眼前失去交流
  2. 俺们会慢慢整理一些术文章到之简书账号“动端数据搜集及剖析”

前关于Android端的<<无埋点数据收集SDK>>使用的艺,写了一如既往篇文章<<Android
AOP之字节码插桩>>,这个是Android端进行全收集之起点,我们便用此法轻松将到各种"Hook"点之.
  本篇文章虽然随着说一下有关收集SDK内部采逻辑的有的关键技术.


当回国的机上,我看了了是夏在日本口碑爆棚的纯爱电影《我思念吃少你的胰脏》,电影改编自同名小说。甜美的画面,单纯的情义,娓娓道来的故事,却有力地描述着女主人公樱良沉重的人生哲学。故事情节让自身当黑暗的机里,感动之一律拿鼻涕、一把眼泪……(提示:以下内容包含大量剧透)

目录

一、概述
1.1 SDK数据收集能力现状
1.2 关键技术点概述
老二、View的唯一标识(ID)
2.1 调研
2.2 利用ViewTree构建ViewID
2.3 ViewPath的生成
2.4 ViewPath的优化
老三、页面的细分
3.1 合理划分页面的重中之重
3.2 Android中之页面
3.3 页面名组成
季、无需埋点轻松收集定制的业务数据
4.1 配置示范
4.2 无埋点采访流程
4.3 数据路径(DataPath)
五、结语


男性主人翁春树是高中老师,因图书馆要迁移而被校长委任整理书籍。在整书籍的过程遭到他回想从了高中时期与女校友樱良短暂却乐的时刻:

一、概述

按照有首先简要介绍一下我们的采方案即得以搜集至何以数据,然后对本文重点介绍的其三只技术点展开概述.

图片来自网络

1.1 SDK数据搜集能力现状

目前咱们的SDK进行多少搜集时中心发生个别独能力:

a. 通用数据全量收集
  通用数据据的凡同业务无关的用户作为数据,无论是电商利用还是社区利用,接入SDK后通用数据的采访及还是无差的,这些通用数据大约有:

事件 描述
冷启动事件 App第一次启动时的,版本号、设备ID、渠道、内存使用情况,磁盘使用情况等信息
前后台事件 App进入前台或者后台
页面事件 页面(Activity或Fragment)显示(Show)/隐藏(Hide)
控件点击事件 某个控件(包括页面上控件和弹窗中控件)被用户点击
列表浏览事件[可选] 某个列表的哪些条目被用户浏览了
位置事件[可选] 上报用户地理位置信息
其它事件 省略描述

b. 业务有关数据需求通过发配置进行无埋点定制收集
  除了上述通用数据,与具体事情有关的数码搜集。拿网易贵金属的首页举个例子:

希冀1-1 无埋点采访工作数据示例

假要需要在用户点击上图吉利框区域时,把“粤贵银”这个交易品的ID(或者下方显示的指数等,只要在内存中在的多少都得以)一由报上来。
  对于这个种需求,数据收集SDK做到了无需埋点切莫靠开发周期,通过线达发一些部署信息,即可即经常开展多少搜集。具体原理第四节省讲述。

女主人公:樱良—开朗、坚毅、善良

1.2关键技术点概述

a. View的绝无仅有标识(ID),(详见本文第二节省)
  当我们搜集控件数据时撞的首先个问题即是:如何把界面上的其他一个View与另View区分开来.

按部就班:某个Button被点击了
我们于上报数据的时光要将这Button和另外所有控件(比如另一个Button,另一个ImageView等)区分开来,这样马上长长的反馈的多少才会表示"就是好Button被点击了瞬间".

眼看便用吗界面及之各级一个控件生成一个唯一的ID.
此ID除了有区分性,还用用于一致性一致性凡同一个View无论界面布局如何动态变化,或者说反复登同一页面,此ID需要保持不变.

b. 页面的分割,(详见本文第三节省)
  除了Activity有些Fragment也得用作页面,这即要求:

  • 以Fragment show/hide时呈报有关页面事件.
  • 页面Fragment中出的用户交互事件为需要归于此Fragment页面,即点击某个View需要上报页面Fragment的消息(从View中怎么抱Fragment信息?)

c. 无需埋点轻松收集定制的政工数据,(详见本文第四节)
  如前方所陈述,默认情况下多少收集SDK会收集全量的用户交互数据,对于定制的事体收集需求,数据收集SDK也落成了无需代码埋点,通过线达发一些布局进行马上收集


阳主人翁:春树—腼腆、忠实、热心

亚、View的唯一标识(ID)

图片来自网络

2.1 调研

用于区分界面上每个View的ID? Android系统是否提供于了咱们是ID?

真正,Android系统提供了一个ID,view.getId()即可获一个int型的id用于区分View,但是这个ID因为以下简单个因也并无可知满足我们的需要.

  1. 发生一定有view是NO_ID,比如以布局文件中莫指定id,或者直接在代码里面new出来view,view.getId()返回的全部都是NO_ID
  2. 其一ID是匪安宁的,由于这个ID其实就是历次编译产生的R文件中的int常量,因此和一个按钮,两个本子编译出来的ID很可能时时不等同的.

从而,我们只能协调动手构建我们的ID喽,怎么构建?答案是利用所属Page+ViewTree构建ViewID.

生存里产生无数意想不到,你的命可能正如我之又少

人事树于不知不觉中发觉了同班同学樱良的《共患病记录》,里面著录着樱良于查获患后的各个一样上的醒悟。春树翻开来读了几首后,即惊讶而怕。而樱良在收看春树手捧在它的日志阅读的当儿,内心是慌乱的,她战战兢兢以后春树每次见到其,都见面感到同情、遗憾。她不思量让同情,她惦记与好人无异过祥和最终之时候。所以,她挑笑着报春树一切。她作无所谓的金科玉律,反而为春树也更敢于地面对她底病状。

图片来自网络

樱良问春树:“你莫呀期待呢?”
春树摇头。樱良很怪:“你没害,那尔便又应当出想啊!“
春树的沉默让樱良着急起来:无须看你从未患,你尽管见面产生过多时间。生活里会发出不少意料之外—车祸、命案、天灾,你的命可能比自己之还短,所以你重新当一味全力地过好各级一样天!

樱良被春树看她形容下之人生愿望清单,缠在春树帮助其实现。春树暂时没使自己去落实的企,他情愿先陪在樱良实现其拥有的要。于是他们一起环游、一起吃鲜的拉面、一起玩耍真心话很冒险……春树后来想起,那是外人生里极其简便易行、最欢喜的相同段落时。

2.2 利用ViewTree构建ViewID

每当Android的概念里,每个Window(ActivityWindow/DialogWindow/PopupWindow等)上面还生在雷同蔸ViewTree.而屏幕中看到底各种控件(ImageView/Button等)都是立即棵ViewTree上之节点.
  有Android开发环境的同窗才待开辟AndroidDeviceMonitor-dump view
hierarchy 就可以看到ViewTree的眉宇,如下图:

图2-1 ViewTree概念图

用,我们萌生出一个想方设法:

下Page+ViewTree中的职位构建ViewID.

View以ViewTree中的位置要用有限点来确定:

  • 纵向的深浅
  • 横向的index

考虑这点儿个元素后,我们定义一个ViewPath:

ViewPath:当前view到ViewTree根节点的等同长路线,用于在ViewTree中唯一定位当前view。路径中之每个节点包含两片信息,即节点View类型信息,以及节点View在兄弟中之index。

正如图,是一个简的ViewTree模型(简单到深度只发生零星重叠,每层只有两三个控件)

图2-2 ViewTree模型图

比如事先为的概念,上图中控件1,2,3,4底ViewPath如下

控件1ViewPath: RootView/LinearLayout[0]   index为1表示此节点是兄弟节点中第一个控件
控件4ViewPath: RootView/LinearLayout[0]/ChildView1[0]
控件2ViewPath: RootView/RelativeLayout[1]
控件3ViewPath: RootView/LinearLayout[2]

上述被起的ViewPath中,每个节点(除了首节点)有点儿有些情节:

  • LinearLayout,RelativeLayout,ChildView1等ViewType信息(节点View的类型
  • “[]”内的index信息,此index指示此节点是哥俩节点的第几单

当即是头的ViewPath,用ViewPath定位view,有有限点特别要:

  • 一致性: 同一个view的ViewPath在ViewTree的动态变化中应维持无变换
  • 区分度: 不同view的ViewPath应该例外

据这最初的ViewPath定义在实践中还免可知于一致性与区分度上满足我们的需要,后面会对ViewPath进行优化。

而如果积极对旁人好,才会到至广大冤家啊

性欲树于次里是一个沉默寡言的同学,平时以于教室后,没有丁会面小心到他。直到他以及乐天善良之樱良出游之事务在班受到传唱,同学等才开对客谈论纷纷。然而刚刚开的猜想逐渐在年级里传成了拉,而且越传越离谱。

图片源于网络

立刻,樱良以患再次住院。春树去帮忙樱良补课,把年级里有的工作各个告诉其,表情甚不得已。樱良却笑说,同学等就此说这些话语都是因平时春树太沉默了。他只要将他心里话说于旁人听别人才会掌握呀。春树摇头:”我说了她们也未会见了解的。“

”他们会理解的!“樱良鼓励春树去交朋友,”除非你先主动对别人好,别人才见面以及你做恋人啊!

2.3 ViewPath的生成

方我们是因为构建ViewID的要求引出了ViewPath的定义,那么当彼此事件(例如:按钮点击)发生时,我们安颇成者控件的ViewPath?
  如达到同首文章<<Android
AOP之字节码插桩>>所述,当用户点击某个按钮时,我们插入OnClickListener.OnClick方法被的如下代码用会见于调用:

Monitor.onViewClick(view);    

面,入参view即为即吃点击的view,获取这view的ViewPath伪代码如下:

  public static ViewPath getPath(View view) {
    do {
      //1. 构造ViewPath中于view对应的节点:ViewType[index]
      ViewType=view.getClass().getSimpleName();
      index=view在兄弟节点中的index;
      ViewPath节点=ViewType[index];
    }while ((view=view.getParent())instanceof View);//2. 将view指向上一级的节点
  }

结构出的ViewPath如下面例子所示:

DecorView/LinearLayout[0]/FrameLayout[0]/ActionBarOverlayLayout[0]/ContentFrameLayout[0]/FrameLayout[0]/LinearLayout[0]/ViewPager[0]/ButtonFragment[0]/AppCompatButton[0]

抱歉,我能哭也?

身就会玩是那么的波荡起伏,难以预料结局。樱良的身体一天天地恶化,好不容易在气象平稳之小日子里申请出院,却在赶去跟春树约定地点的旅途让杀人凶手残忍杀害。春树满心欢喜地等正樱良,要知这次拘留樱花的路都是他安排的,他若受樱良同不行全面的旅行记忆。然而,最终等来之倒是樱良遇害的消息。

图表源于网络

杀丰富一段时间,春树都躺在屋子里,被悲伤地心态淹没在他与樱良的回想里。不了解多长时间过去了,春树才缓和了某些满心之悲壮。他错过矣樱良家,给它送去了极度得意的樱花。樱良于住院中告诉过春树,她允许春树在其相差后关禁闭它们底日记,而且才被春树看。

春树从樱良的妈妈那里要来了日记,他密切地看了过多属于他们的回顾。春树第一糟糕知道,原好、友情、成长是美好的,也是悲痛欲绝的,痛到外不能自已。他小心地问樱良的慈母:”对不起,我好哭也?“

图来源网络

事实上樱良与春树做朋友是损公肥私的,她惦记让一个非是亲人之同龄人分担其的恐怖、孤独和无助。但樱良以是善之,她由同开始就是注意到了教室后排沉默寡言的春树,她感念让情欲树了得重新愉悦。

自打她们变成朋友之后,樱良开朗的性情、乐观的生活态度一点一滴地改在春树。

情树学会了关怀其、学会了为它失去安排等同庙旅行、甚至到了第一独对象。

长大成人后,春树甚至为樱良当了同名高中老师,默默地拉那些跟他一样害羞腼腆的同桌。

樱良则弱了,但她底生当春树的成人里取得了持续……

要是您未曾生病,希望而能够过好生命里之每一样龙,尽全力去贯彻有想。

若你患病了,愿君发出胆成为樱良,活在马上,善待身边的诸一个口。

2.4 ViewPath的优化

a. 一致性优化1
情景:

于图2-2
ViewTree模型图中,如果如下图被所著,在控件2和3遭遇动态插入一个FrameLayout呢?

祈求2-3 Android界面动态性变化情景1

这时候本原始ViewPath的定义,我们来探控件3底ViewPath发生了安变化?

ViewTree动态变化前: RootView/LinearLayout[2]
ViewTree动态变化后: RootView/LinearLayout[3]

优化:

ViewPath节点中index的意义从“兄弟节点的第几只”优化为:“相同类别兄弟节点的第几个”

优化后,发生图2-3所示界面布局动态变化时,控件3底ViewPath变化为:

ViewTree动态变化前: RootView/LinearLayout[1]   index为1表示此节点是兄弟节点中第二个LinearLayout
ViewTree动态变化后: RootView/LinearLayout[1]

可以看出,此处优化使控件3底ViewPath在ViewTree动态插入除了LinearLayout之外其它任何项目时还维持前后一致。

b. 一致性优化2
情景:

在祈求2-2
ViewTree模型图中,如果像下图中所展示,在控件2和3受到动态插入一个LinearLayout时,控件3的ViewPath能否继续维持前后一致?

随上述场景,控件3ViewPath的变如下:

ViewTree动态变化前: RootView/LinearLayout[1]   index为1表示此节点是兄弟节点中第二个LinearLayout
ViewTree动态变化后: RootView/LinearLayout[2]   前面插入一个LinearLayout导致此节点变为兄弟节点中第三个LinearLayout了

问题
上述现象指的实在是一个题目:ViewTree中同种类兄弟节点动态变化(插入/移除/移位)影响ViewPath的一致性

  • ViewPath节点中的index,在和种类(ViewType相同,例如都是LinearLayout)兄弟节点动态加入/删除时,当前节点的index无法以变更前后保持一致。
  • “一致性优化1”中之优化可对抗不同档次兄弟节点的熏陶,却对同类型兄弟节点的震慑无可奈何

从ViewPath的定义及难以找到在与种兄弟节点动态变化前后保持一致的法,但我们可分析产生这个种界面动态变化的景象:

  1. 使用Fragment的动态布局
      Android界面的动态布局有状况中,使用Fragment实现界面动态变化的效率与潜移默化控件数量或比充分的(相对于直接addView())
  2. ListView(等而复用View)中同类型的itemViews。
      此种植情景则尚无起在一个itemView前动态插入一个itemView,但是由于itemView的复用,导致itemView展示的情及当父节点listView内之index的应和关系动态变化,因此为属此类。

2受到所说“ListView等可复用View”造成的题目后会出优化,此处针对1遭之景讨论。1遭场景发生时只要下图:

图2-4 使用Fragment造成界面动态性的现象

上图中FragmentA,FragmentB,FragmentC的顶层视图控件全部是LinearLayout同类型),此时这三个Fragment加入的依次用促成ViewPath在这里各种不一样,从而致使ViewPath在动态变化前后未克保持一致(如前:ViewTree动态变化前后控件3ViewPath的浮动所示)。
优化:

当ViewPath节点中,使用Fragment的名替换ViewType

优化后,发生图2-4所示界面布局动态变化时,控件3之ViewPath变化为:

ViewTree动态变化前: RootView/FragmentB[0]   index为0表示此节点是兄弟节点中第一个FragmentB
ViewTree动态变化后: RootView/FragmentB[0]  

一旦达到,此次优化使得,在顶层视图ViewType相同的Fragment动态增长/删除到ViewTree时,ViewPath在转移前后保持一致。

c. 对可复用View的优化
情景
  为最经常使用的ListView为条例,假设有一ListView满屏不过展示3只章,那么这ListView可能只有3个支行控件(ItemView),而这个ListView上滑动之后好显示100项内容
  这3独ItemView与100起内容是同等针对性大多之对应关系,而且映射并无可靠规律。
  这,我们盼望ViewPath可以区分这100码显示的始末条目,而不只区分3单ItemView

地方情景中的题目可用下图表达:

图2-5 可复用View的ViewPath区分性优化

苟齐图备受,内容条目1暨4还是为此itemView1来表现的,按照之前的ViewPath定义,图2-5负逐一内容条目的ViewPath如下:

内容条目1: ListView/ItemView[0]   index为0表示此节点是兄弟节点中第一个ItemView
内容条目4: ListView/ItemView[0]   
内容条目2: ListView/ItemView[1]  
内容条目3: ListView/ItemView[2]  

得视内容条目1与4之ViewPath区分无起。此种植问题可以总结也:

著内容以及ViewTree中之控件不是各个对应之情导致基于ViewTree的ViewPath区分度不够

  • 然复用View,比如:ListView,RecyclerView,Spinner等,呈现出来子View的数据及实际子View的数据未必相同
  • ViewPager设置缓存页面数为1,第二页显示时,第二个页面顶级View其实是ViewPager的第一单ChildView。此种情况吗会导致显示内容(第二页)与ViewTree中之控件(第一个ChildView)不对应的情况。

故而我们对ViewPath作如下优化:

ViewPath节点的index取内容之第几宗,而非第几单ItemView。

优化:
优化后图2-5遇逐一内容条目的ViewPath如下:

内容条目1: ListView/ItemView[0]   index为0表示此节点是ListView显示的第一个内容条目
内容条目4: ListView/ItemView[3]   
内容条目2: ListView/ItemView[1]  
内容条目3: ListView/ItemView[2]  

可见,之前ViewPath无法区分的内容条目1和4现在好区分别了。各种可复用View取内容的第几项之代码方法如下:

ListView,Spinner等AdapterView------------ListView.getPositionForView(itemView)
RecyclerView------------------------------------RecyclerView.getChildAdapterPosition(itemView)
ViewPager----------------------------------------ViewPager.getCurrentItem()

d. ViewPath起点优化
  ViewPath从ContentView为起点,而非DecorView

  • DecorView : Window上之根视图,ViewTree中的一干二净,最顶层视图
  • ContentView:
    客户端程序员定义的有视图的父节点,如Actvity中常见的setContentView(view)

一个实在被的ViewPath如下:

DecorView/LinearLayout[0]/FrameLayout[0]/ActionBarOverlayLayout[0]/ContentFrameLayout[0]/FrameLayout[0]/LinearLayout[0]/ViewPager[0]/ButtonFragment[0]/AppCompatButton[0]

上面的“ContentFrameLayout[0]”以此节点代表的就是ContentView,程序员在xml或者代码里面构建的View都当ContentView中。

从DecorView到“ContentFrameLayout[0]”的立刻同段Path是Android系统Framework层决定的,理论及该是一模一样的,但是由于碎片化等原因可能ViewPath的立即无异截发生变化.在实践中,我们吧意识确有一些Rom发生了此类情况,但是比率很小.
  为了挡住这种可能导致与一个View在不同装备及生ViewPath不同的情况,ViewPath的起点定义在ContentView比较好.如上面的ViewPath可优化为:

ContentView/FrameLayout[0]/LinearLayout[0]/ViewPager[0]/ButtonFragment[0]/AppCompatButton[0]#mybutton

做法:
  构造每一个ViewPath节点时得以取view.getId(),看看id的packageId部分是无是系统的(系统资源id以16进制的0x01,0x00方始),如果是,生成ViewPath时屏蔽这段就是可.


其三、页面的撤并

3.1 合理划分页面的要紧

页面在Android中对应于Activity和组成部分Fragment(比如很多app首页多tab的设计,若每个tab是使用Fragment实现的,那么这种tab一般作为一个页面).页面的分开很关键,因为少碰:

  1. 对页面,需要得到Show/Hide两独会,在这时机上报页面Show/Hide事件,非页面则未待
  2. 页面的剪切关系正用户交互事件之所属,例如,按钮点击事件上报格式如下:
事件名称 所属页面 ViewPath 其他属性
ButtonClicked MainActivity XXX 省略

报表中之"所属页面"代表此次按钮点击事件产生在MainActivity中.将互动事件归属于页面这样对后无论是进行路径分析还是统计控件点击量分布且发坏可怜的好处.

3.2 Android中之页面

Android中日常需当作页面的有Activity和Fragment(对于如全屏Dialog或者全屏的View暂未考虑).对于Activity,上省中涉及的简单碰还非常易办到.

a. Activity页面

  1. 从今Application.ActivityLifecycleCallbacks的onActivityResumed/onActivityPaused这简单个回调方法就好分别收获Activity页面Show/Hide的会,并在这儿机上报相应页面事件
  2. 互动归属的Activity页面可以经Context轻松获取,例如上篇文章<<Android
    AOP之字节码插桩>>涉及,当按钮点击时,会触发我们插桩的代码:

Monitor.onViewClick(view)

适合参view即为咱点击的view,通过view.getContext()我们一般就是可取得这个View所属的Activity,伪代码如下:

//从View中利用context获取所属Activity的名字
public static String getActivityName(View view) {
    Context context = view.getContext();
    if (context instanceof Activity) {
      //context本身是Activity的实例
      return context.getClass().getSimpleName().;
    } else if (context instanceof ContextWrapper) {
      //Activity有可能被系统"装饰",看看context.base是不是Activity
      Activity activity = getActivityFromContextWrapper((ContextWrapper) context);
      if (activity != null) {
        return activity.getClass().getSimpleName();
      } else {
        //如果从view.getContext()拿不到Activity的信息(比如view的context是Application),则返回当前栈顶Activity的名字
        return currentActivityName;
      }
    }
    return "";
  }

b. fragment页面
  相对于Activity,将某些Fragment看作页面的逻辑就要有点复杂一些了.这里面涉及下面几乎单问题:

  • 争Fragment可以要当作页面?
      这是要人工决策的,机器做不了之决定.
      时咱们这人工干预是付出用户研究集体,所有Fragment截图等信息全都显示在平台上,由用研同事选择得用作页面的那些,用研选择的结果用自动化配置到SDK中
  • 争获得Fragment页面的Show/Hide页面事件?
      由于fragment使用状况比较多样,单单靠OnResume/OnPause两个回调表示fragment
    Show/Hide是匪精确的,比如:
    场景一
      首页一个Activity承载多独Fragment
    Tab的状况,此时tab间切换并无会见触发Fragment的OnResume/OnPause.点的回调函数是onHiddenChanged(boolean
    hidden)

    场景二:
      一个ViewPager承载多独页面的Fragment时
        a.当第一单Fragment1显示时,虽然第二单Fragment2这时无亮,但是Fragment2的OnResume却与实施,处于resumed的状态.
        b.ViewPager页面切换OnResume/OnPause/onHiddenChanged均未沾,触发的回调是setUserVisibleHint
      这时判定Fragment Show/Hide应该据此setUserVisibleHint,而非OnResume/OnPause
      如前方同一首文章XXX,所述,我们通过插桩的方Hook到了fragment的如下生命周期函数用于包装成Show/Hide事件:

onResume()
onPause()
onHiddenChanged(boolean hidden)
setUserVisibleHint(boolean isVisibleToUser)

采取就几乎单回调包装成适用于各种气象的FragmentShow/Hide事件之伪代码如下:

//此回调发生,则证明是场景一中使用情景,
  onHiddenChanged(boolean hidden) {
    hidden == true ------FragmentShow
    hidden == false------FragmentHide
  }
//场景二中ViewPager页面切换时触发Fragment的此回调,
  setUserVisibleHint(boolean isVisibleToUser) {
    if (fragment.isResumed()) {//只有resumed状态的fragment适用此情景
      isVisibleToUser == true ------FragmentShow
      isVisibleToUser == false------FragmentHide
    }
  }
//上述使用情景之外的一般场景
  OnResume/OnPause{
 //fragment没有被hide,并且UserVisibleHint为可见的情景
    if (!fragment.isHidden() && fragment.getUserVisibleHint()) {
      OnResume ------ FragmentShow
      OnPause  ------ FragmentHide
    }
  }
  • 如何将Fragment内部的交互归属到Fragment页面,也就是说如何以互发生时从view实例拿到Fragment页面的名(像之前将到Activity页面名字如出一辙)?
      view可以通过context拿到Activity的音信,但是却从未门路以到fragment的援。那么,当有View交互发生,我们以欲取得Fragment页面名字的情下,我们只好优先用Fragment页面名写副是View的性被。
      做法大致如下:
        a.
    按照前一模一样篇文章xxx里面的方式,在Fragment.OnCreateView方法的结尾插桩,将到return的view(即为者Fragment的顶层视图)
        b. 判断这个Fragment是否给指定为Fragment页面,如果是,下一样步
        c.遍历以Fragment的顶层视图为根本节点的ViewTree,
    Fragment名设置及者ViewTree的各级一个view上。设置方法如下所示:

view.setTag(0xff000001, fragmentName);

注意:View类有一定量只名吧setTag的措施

public void setTag(final Object tag)

斯道,类中用同样Object对象存储tag,protected Object mTag =
null;。listAdapter中常用于安装holder。我们这里用之不是这个,切莫见面吃此所以法冲突

public void setTag(int key, final Object tag)

这个道,类里生同稀疏数组存储tag,private SparseArray<Object>
mKeyedTags;
  tag的key官方推荐资源id,因此我们得以选用类似0xff000001之类的app用非顶的资源id进行tag存储以避免冲突
    d. 当用动用Fragment名时,如下调用即可获:

view.getTag(0xff000001)

3.3 页面名组成

眼前说了将相互事件(比如点击事件)归属到某个一个页面的方法是:

于互动事件中设置一个字段,值也页面名称。

页面可以是Activity或者Activity承载的Fragment,我们的页面名称组成如下:

Activity类名[Activity别名][Fragment类名][Fragment别名]

证明如下:

  1. “[]”内的有是可选的,可能发或无。另外,各个部分之间发生分隔符分割。
  2. 页面名成中,Activity的叙述(类名/别名)是首先层,Fragment的讲述(类名/别名)是亚交汇
  3. 别名的起是以化解就依赖类名无法精确区分页面的少数情况,比如:
    于某某电商利用中,“商品详情页”(同一个Activity)用于展示各种货品(iphone,电视等),如果需要将“不同商品之商品详情页“区分成不同页面来统计pv等指标来说,需要设置别名,如:

商品详情页#iphone
商品详情页#电视

对此别名的安装,需要程序员在工作代码里面(如Activity.OnCreate,Fragment.onCreate等)显式设置.


季、无需埋点轻松收集定制的工作数据

4.1 配置示范

前涉嫌过,数据搜集SDK可以透过部署下发即经常募集定制的数,那么以Android端这个是怎好的吗?
首先,看一下下的布置样例:

//第一部分:描述
PageName:MainActivity
ViewPath:DecorView/.../ViewPager[0]/ButtonFragment[0]/AppCompatButton[0]
EventType:ViewClick
//第二部分:数据路径(当描述符合时,按照此路径取数据)
DataPath:this.context.demoList[5]

上面例子翻译成多少需求就是:

1. 当页面(MainActivity)
2. 中的控件(DecorView/.../ViewPager[0]/ButtonFragment[0]/AppCompatButton[0])
3. 发生点击事件(ViewClick)时
4. 按照路径(this.context.demoList[5])取出数据
5. 并附加到点击事件上面一起上报

遵循此描述,我们尚好描述如下等等各种数码需求:

当(某页面)发生事件(Show)时,按照路径(xxx)取出数据,并附加到页面Show事件上面一起上报

总下描述的有,如下:

第一层 第二层 含义
描述部分 页面 限定页面
ViewPath 限定按钮
EventType 限定时机(点击/前台/PageShow)
数据路径 一种DSL,指示目标数据在内存中的位置(可理解为“引用路径”)

4.2 无埋点采访流程

上节著了用于无埋点定制业务数据搜集之部署,那么SDK收到这样的同等份配置如何最终将想如果的数额搜集上来吗?

  • 步骤一:发生原始事件。比如点击时采访,当点击时见面沾我们插桩的代码,并转移原始的点击事件

Monitor.onViewClick(view)
  • 步骤二:相当配置
    当onViewClick方法被相当下发的布信息,看看Page,ViewPath是否跟时view匹配,EventType是否以及目前风波类匹配,若匹配则进行下同样步
    注:ViewPath的匹配可以来纯粹匹配同歪曲匹配,精确匹配时一个ViewPath精确匹配唯一一个控件.模糊匹配时一个ViewPath可匹配多个控件,例如可以用用一个ViewPath模糊匹配一个列表中之具备久目.
  • 步骤三:按照数据路径(DataPath)逐级反射用到目标数据,并将找到的数码附在原始之点击事件上进展报告。

4.3 数据路径(DataPath)

上述手续三拓展多少收集主要是遵循DataPath的叙说进行(例如示例中干的"this.context.demoList[5]"),DataPath是平等种植我们用来收集定制数据要定义的平栽DSL.含义如下:

a. 含义

DataPath:
指向设集之靶子数据的相同长引用路径,解析是路并逐级反射最终将到对象数据.

DataPath写法被的片段重点字(符):

关键字(符) 含义
. 表示对象所属关系,如:a.b 表示实例a中的字段b
.() 表示公有方法调用,如:a.b() 表示调用实例a中的方法b.注意:方法入参可以是DataPath指向的Object
[] 数组/线性表的index. 注意:此index可以是常量数字,也可以是一个DataPath指向的数字
this DataPath字符串的起点,表示起点为当前实例(当前View)
item DataPath字符串的起点,表示起点为当前View父节点中AdapterView adapter中当前条目. 常用于列表中的数据获取
parent DataPath节点中的关键字,用于表示当前view的parentView.效果同view.getParent(),使用此关键字可减少视图引用中的反射
childAt(x) DataPath节点中的关键字,用于表示当前view的第x个childView.效果同view.getChildAt(x),使用此关键字可减少视图引用中的反射

b. 应用示范
  下面用鲜只例子说明什么由DataPath找到对象数据.

图4-1 DataPath示例

示范1:列表数据获得
  上图备受显得是一个列表,红框中凡是列表的率先个条目.那么,如果我们怀念如果在列表中条目点击时,将列表展示的交易品ID(或者合作方ID)等非以界面及出示如果又有让内存中的数据从点击事件反映.此处DataPath该怎么形容?

item.productId

DataPath解释:

  1. 起点定为"item",则意味今后ListView(或者RecylerView)绑定的Adapter中时数码item为起点取数据.
    假设此ListView绑定的Adapter如下:

public class DemoAdapter extends BaseAdapter {
  private ArrayList<DataItem> mDataItems;
  ......
}

尽管此处”item”代表的虽是mDataItems[x] (x表示目前吃点击条目的itemId)

2."productId"是model类DataItem中代表"交易品ID"的字段名称.

由此DataPath获取数据:

  1. 当第x章被点击时,如果发现有相当的配备,对于起点为”item”的DataPath,先经view.getParent找到上层ListView实例,然后经listView.getAdapter()获得绑定的Adapter实例,最后经过Adapter.getItem(ListView.getPositionForView(itemView))得到数码遭到第x独item,即mDataItems[x]
  2. 映获取mDataItems[x]遭之productId字段,即可取得第x独章的"交易品ID",将这ID跟随第x条款的点击事件进展上报即可.

实例2:界面数据获得
  同样时图4-1所展示,加入我们纪念以列表中条目点击时,将条目中显的”最新价格”跟随点击事件反映.此处DataPath该怎么形容?
  红框所示ViewTree子树如下:

祈求4-2 列表Item ViewTree子树结构

只要齐图,选中部分凡是列表的ItemView(RelativeLayout),可见”最新价格”是由于index为2之TextView所展示,由是而得,列表中条目点击获取”最新价格”数据的DataPath如下:

this.childAt(2).mText

DataPath解释:

  1. 起点为"this",表示手上给点击的view实例(图4-2中让入选的RelativeLayout)
  2. “childAt(2)”表示RelativeLayout.getChildAt(2),得到图4-2中index为2的TextView
  3. “mText”
    表示取出步骤2饱受取得TextView实例的mText字段(TextView控件显示的仿内容存储在mText字段内)
  4. 拿取出的界面上出示的”最新价格”数据增长到原有点击事件受到,一起齐报.

c. DataPath注意事项:
1.混淆.
  由于DataPath本质上讲述的常内存中的"引用路径",并且按照DataPath取数据时用了反光的点子,因此DataPath应该描述的是张冠李戴后的"引用路径".
  虽然DataPath可能蒙受混淆的熏陶,但是

* 用于存储数据的model类通常是不被混淆的.如我们之前的item关键字直接将起点设置为列表条目的model类对象,不受混淆影响.
* 通过关键字parent/childAt(x)可以在视图的引用中不受混淆影响
* 接口的方法通常不受混淆影响.因此在DataPath中多用接口方法调用

据此开发以布置DataPath时应竭尽用上述未给张冠李戴影响之字段及措施.但是,如果实在用了模糊了之字段怎么办.我们的方案是:

数码报警

按部就班版本1齐部署的DataPath
“a.b”,在提升新版本2后不复适用,则新版本2按照"a.b"收集时拿募集不至,产生报警音到后台.后大接受大量之种信息会提醒开为新本子配置适用新本子的DataPath.

2.代码变化造成引用路径变化,从而致使之前安排的DataPath失效.
  和代码中埋点相比,线达布置进行募集数据和代码的变是互的,无关的.这就算闹或造成原有代码修改导致DataPath失效.其实如果客户端架构设计合理,功能迭代更多是当展开代码的壮大,而休改,这种导致DataPath失效的事态应该会大大降低的.
  但是无论如何:

布的DataPath摆脱无了跟本的相关性

对此种植问题我们照例是经前提到的"数据报警"进行监察和避免的.


五、结语

综上,本文介绍了数据收集逻辑中3单比较根本之接触(ViewID/Page/DataPath),结合上平等首文章的(AOP原理),Android端无埋点数据收集技术上比主要之接触全以总收尾毕.
  当然实现SDK过程遭到中过许多比好玩的技能问题,后续也会见陆续展开整理.

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图