Fragment时长统计那些事
注:本文同步发布于微信公众号:stringwu的互联网杂谈 frament时长统计那些事
页面停留时长作为应用统计的北极星指标里的重要指标之一,统计用户在某个页面的停留时长则变得很重要。而Fragment作为Android中页面的重要组成部分,其停留时长的统计就显得非常重要。目前业界能搜索到的方案,主要有两种方案:
- 业务继承于某一个特定的
Fragment; - 直接通过
Fragment的生命周期方法来统计页面的时长;
方案一对于业务的侵入性过高,业务只有接入特定的Fragment,才能统计其时长。方案二适用面太小,只适于于没有预加载的场景,如果存在预加载行为,则统计出来的时长是不准的。
本文主要根据笔者对Fragment的理解,从对业务侵入性和兼容性角度出来,.
1 Fragment简介
Activity是 Android的界面组成元素,一个Activity就是一个页面。而Fragment则允许将Activity拆分成多个完全独立封装的可重用的组件,从而构建出灵活的UI界面。目前市场上的多个TAB的UI一般都是通过Fragment去组装完成的,如某应用渠道的TAB:
具体的Fragment的简介可参数官方文档官方文档 ,本文不再详细介绍;
2 Fragment的生命周期
Fragmennt不能单独使用,始终需要依赖于Activity,因此,尽管Fragment拥有自己的生命周期,但还是会受到Activity的生命周期的影响,如Activity被 销毁后,Fragment也会跟着销毁。Fragment是 Activity的“寄生虫”。
Fragment的生命周期可参考图:
一般在实际应用过程中,只需要对Fragment的关键生命周期方法进行复写就可以:
- onCreateView : 首次绘制
Fragment时会调用这个方法,需要从些方法中返回Fragment的根View; - onActivityCreated :
Fragment所在的Activity启动完成时回调; - onResume : 当前
Fragment变成可交互状态时回调; - onPause : 用户离开
Fragment的回调方法;
甚至于只需要复写onCreateView就能完成一个Fragment的开发了。
3 Fragment时长统计
3.1 背景
Android中最常用的两种页面的形态:Activity和Fragment。Activity做为Android中最原始的页面,同一时刻只允许有一个Activity处于活跃状态(onResume),因此Activity页面的时长可以直接通过Activity的生命周期方法来进行统计:
完整的页面周期:
- onResume :页面开始时间;
- onPause: 页面结束时间;
而Fragment不一样,Fragment是可以存在预加载和多层嵌套的行为的,同一时刻会有多个Fragment执行了 onResume方法,但真正对于用户可交互的可能就只有一个(多层嵌套时会有多个),如果单纯的使用Fragment的生命周期方法来统计Fragment的页面时长显然会造成统计不准。因此需要对Fragment的页面时长寻找独立的统计解决方案。
本文不讨论对业务侵入性比较大的方案,如自定义的Fragment等方式,只讨论对业务侵入性最小的方案。
3.2 方案
备注:本文讨论的方案默认是使用support包或者androix的Fragment来进行统计的;
Fragment其本身是有生命周期的, 因此整个方案会基于Fragment的生命周期来做进一步的处理:
- 注册Fragment的生命周期的监听
- 通过Fragment的
getUserVisibleHint方法来判断页面的可见性 - 维持两种Fragment的List:当前可见的Fragment List 和 已执行onResume方法的Fragment List;
3.2.1 生命周期的监听
Fragment的生命周期方法通过在Activity的onResume方法里注册一个Fragment`的生命周期的回调方法:
//在Activity onResume时调用
public void onActivityResume(Activity activity) {
FragmentActivity fragmentActivity;
if (!(activity instanceof FragmentActivity)) {
return;
}
fragmentActivity = (FragmentActivity) activity;
//拿到当前Activity的fragment管理
FragmentManager manager = fragmentActivity.getSupportFragmentManager();
manager.registerFragmentLifecycleCallbacks(fragmentcallback, true);
}
//fragmentcallback
fragemntcallback = new FragmentManager.FragmentLifecycleCallbacks(){
....
@Override
public void onFragmentPreAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) {
super.onFragmentPreAttached(fm, f, context);
}
@Override
public void onFragmentResumed(@NonNull FragmentManager fm, @NonNull Fragment f) {
super.onFragmentResumed(fm, f);
onFragmentResume(f);
}
@Override
public void onFragmentPaused(@NonNull FragmentManager fm, @NonNull Fragment f) {
super.onFragmentPaused(fm, f);
onFragmentPause(f);
}
....
}
3.2.2 可见性判断
Fragment可见性判断主要是通过getUserVisibleHint方法来判断是否可见的。
// 可见性判断方法
boolean curState = fragment.getUserVisibleHint();
如果Fragment没有嵌套的情况,则直接通过其本身的getUserVisibleHint方法就能判断当前页面的可见性,但如果Fragment又嵌入Fragmnent,则只有其本身的getUserVisibleHint方法来判断当前页面的可见性是不够的,会出现外层的Fragment不可见了,但内部的Fragment还是可见的,这显然是不符合逻辑的;如:
整个页面由四个一级的Fragment组成,其中标签为THREAD的fragment嵌入了三个子的Fragment;
如果点击外层的FOUR tab,则 标签为EIRST的 fragment的可见性是不会发生变化的(仍是可见的),但实际上,该fragment已经不可见了。
因此我们不能简单在通过该Fragment的可见性来判断其页面的真实可见性,需要结合外层Fragment的可见性来判断页面的真实可见性:
//完成的页面可见性方法判断。
private boolean isFragmentVisible(Fragment fragment) {
boolean curState = fragment.getUserVisibleHint();
//本身不可见就直接返回不可见了
if (!curState) {
return false;
}
//本身可见情况下,判断父fragment的情况
Fragment parentFragment = fragment.getParentFragment();
boolean parentState = true;
while (parentFragment != null) {
parentState = parentFragment.getUserVisibleHint();
if (!parentState) {
break;
}
parentFragment = parentFragment.getParentFragment();
}
return parentState;
}
这个可见性方法生效的前提是子Fragment使用的FragmentManager是通过外层的fragment.getChildFragmentManager()拿到的。这样子Fragment的关系里才会获取到其 ParentFragment
3.2.3 Fragment遍历
通过 Fragment的生命周期方法的监听和页面可见性的判断,内部需要维持两个List:
- 执行了
onResume方法的Maps:mFragmentResumeMap;//key:fragment;value:执行onResume时间; - 页面可见的Maps:mFragmentStartTimes;// key:fragment;value:页面开始可见时间
内部通过去遍历这两个Maps来判断页面的事件(进入或者退出)。在有Fragment执行onResume或者onPause时去触发遍历的操作。
遍历的伪代码为:
//可见的list应该不只有一个,可能有多个
//先遍历FragmentResumeMap,判断哪个Fragment变成可见了,加入到可见的List列表里;
//然后遍历可见List里哪个变成不可见了,然后就开始上报当前结束的列表;
//onPause时,要移除掉当前的onResume的List;
Set<Fragment> mResumeFragmentSet = mFragmentResumeMap.keySet();
//新的可见Fragment:由不可见变成可见
HashMap<Fragment, Long> newVisibleFragment = new HashMap<>();
for (Fragment fragment : mResumeFragmentSet) {
if (!isFragmentVisible(fragment)) {
continue;
}
if (mFragmentStartTimes.containsKey(fragment)) {
continue;
}
Long time = SystemClock.elapsedRealtime();
//记录下当前Fragment开始可见时间
newVisibleFragment.put(fragment, time);
mFragmentStartTimes.put(fragment, time);
}
//对新的可见Fragment newVisibleFragment 执行页面进入事件的回调;
....
//判断由可见变为不可见的Fragment
Set<Fragment> mVisibleSet = mFragmentStartTimes.keySet();
HashMap<Fragment, Long> unVisibleFragment = new HashMap<>();
for (Fragment fragment : mVisibleSet) {
if (isFragmentVisible(fragment)) {
continue;
}
unVisibleFragment.put(fragment, SystemClock.elapsedRealtime());
}
//对于从可见变成不可见的fragment 执行页面退出的回调;
....
4 总结
本文通过监听Fragment的生命周期和页面可见性的判断逻辑,提出了一个对于业务侵入性很小的Fragment页面时长的统计方法。Fragment时长的精准统计方案通过在内部的逻辑来兼容Fragment存在的预加载行为和多层嵌套的使用功能达到精准统计的功能。
Fragment时长统计那些事的更多相关文章
- java 多线程执行时长统计
ExecutorService——shutdown方法和awaitTermination方法 shutdown方法:平滑的关闭ExecutorService,当此方法被调用时,ExecutorServ ...
- windows 7 下,如何统计某文件夹下 视频总时长
由于项目需要,我需要给系统加权限,这真是一个让人头疼的问题,如果要每个业务方法都加上权限判断逻辑,那真的不敢想象是多么大的工作量,日后有变动的话,我会不会发疯? 所以我必须利用之前学到的AOP编程,在 ...
- 使用opencv统计视频库的总时长
统计视频库里的视频文件的总时长 废话不多说,直接上代码: /* * ================================================================== ...
- 统计 flv视频总时长
在学习孟媛的视频课程.网上能下载的是flv格式.那我在学习之前,我要统计一下这个课程的数量,他会用多长时间,这样方便我在学习过程中不断的回顾,进行时间管理.我大概就可以统计出来这个视频多长时间可以学完 ...
- 使用mediainfo工具统计每个视频文件(媒体文件)播放时长
需求 1.运营那边需要统计大量视频文件的播放时长,并汇总记录到excel表中,问我有什么方法搞定 这边搜索了很多统计媒体文件时长的,主要有以下几种 1.使用java获取 2.使用python获取 3. ...
- Hexo添加字数统计、阅读时长
统计插件 配置 NexT 主题默认已经集成了文章[字数统计].[阅读时长]统计功能,如果我们需要使用,只需要在主题配置文件 _config.yml 中打开 wordcount 统计功能即可.如下所示: ...
- bash 统计在线时长最长的十个玩/统计一天内一直处于不活跃状态的玩家的百分比
1.某游戏的客户端每隔5分钟会向服务端报告一次玩家的账户积分,如果两次报告的时间间隔不大于5分钟,认为该玩家在这5分钟内在线,假设报告数据的格式如下: IP Dat ...
- TTL 机制排毒,线上k8s的Job已经通过API 增加了Job的TTL 时长,且成功响应,为什么系统还是清理了Job?
TTL 机制排毒,线上k8s的Job已经通过API 增加了Job的TTL 时长,且成功响应,为什么系统还是清理了Job? 面试官:"已完成 Job 的 TTL 机制了解嘛?简单说说TTL存在 ...
- js用img代替ajax js心跳 向服务器定时传送参数 主要计算用户在线时长
html: <!doctype html><html><head><meta charset="utf-8"><title&g ...
随机推荐
- 2020-05-24:ZK分布式锁有几种实现方式?各自的优缺点是什么?
福哥答案2020-05-24: Zk分布式锁有两种实现方式一种比较简单,应对并发量不是很大的情况.获得锁:创建一个临时节点,比如/lock,如果成功获得锁,如果失败没获得锁,返回false释放锁:删除 ...
- 分析现有 WPF / Windows Forms 程序能否顺利迁移到 .NET Core 3.0
本文转自 https://blog.csdn.net/WPwalter/article/details/82859449 使用 .NET Core 3.0 Desktop API Analyzer 分 ...
- 用前端姿势玩docker【五】快速构建中类Unix系统与Windows系统的差异化处理
目录 用前端姿势玩docker[一]Docker通俗理解常用功能汇总与操作埋坑 用前端姿势玩docker[二]dockerfile定制镜像初体验 用前端姿势玩docker[三]基于nvm的前端环境构建 ...
- 【建议收藏】swoft的最佳实践
这是一篇使用 swoft 两个月后的总结文章!,后续会陆续更新的 这是 web-api 开发的总结,如果使用 websocket 等服务的可能不适用,本章节会对一些规范.习惯,或者优化进行一些说明 一 ...
- C#/VB.NET 比较两个Word文档差异
本文以C#和VB.NET代码为例,来介绍如何对比两个Word文档差异.程序中使用最新版的Spire.Doc for .NET 版本8.8.2.编辑代码前,先在VS程序中添加引用Spire.Doc.dl ...
- js实现将时分秒转化成毫秒,将秒转化成时分秒
// 时间转为毫秒 timeToSec(time) { var hour = time.split('[0] var min = time.split('[1] var sec = time.spli ...
- SpringBoot + SpringCloud Hystrix 实现服务熔断
什么是Hystrix 在分布式系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是依赖服务,有的时候某些依赖服务出现故障也是很常见的. Hystrix是Netflix公司开源的一个项目,它提 ...
- 如何使用python移除/删除非空文件夹?
移除/删除非空文件夹/目录的最有效方法是什么? 1.标准库参考:shutil.rmtree. 根据设计,rmtree在包含只读文件的文件夹树上失败.如果要删除文件夹,不管它是否包含只读文件,请使用 i ...
- Java后台服务慢优化杂谈
Java后台服务慢优化杂谈 前言 你是否遇到过这样的场景,当我们点击页面某个按钮后,页面一直loading,要等待好几分钟才出结果的画面,有时直接502或504,作为一个后台开发,看到自己开发的系统是 ...
- 开源搜索引擎排名第一,Elasticsearch是如何做到的?
一.引言 随着移动互联网.物联网.云计算等信息技术蓬勃发展,数据量呈爆炸式增长.如今我们可以轻易得从海量数据里找到想要的信息,离不开搜索引擎技术的帮助. 作为开源搜索引擎领域排名第一的 Elast ...