记录Fragment的显示时长

Posted -SOLO-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记录Fragment的显示时长相关的知识,希望对你有一定的参考价值。

序言

最近在埋点,需要记录fragment的显示时长。想法很简单,使用我们熟知的fragment的生命周期即可。主需要在onResume记录开始时间,在onPase记录结束时间。相减就可以完成fragment显示时间的记录。但是这是最理想的情况,是在一个activity中只有一个fragment。 fragment不涉及hideshow等方法的情况下。

然后我们实际情况却很复杂,涉及到ViewPager中使用fragment会预加载fragment,此时会有多个fragment处于onResume状态但是真正显示的fragment只有一个,也涉及到hideshow方法的影响,hide是通过将Fragment的View设置为GONE来实现的。会调用onHiddenChanged方法。而不会调用onPause方法。

因此要正确记录Fragment的显示时长,还是很烦的。需要处理各种情况。下面分情况讨论。
在这里插入图片描述

android.support.v4.app.Fragment

下面介绍使用了android.support.v4.app.Fragment事的方法。至于Android X有更好的处理办法。

ViewPager的影响

测试代码如下

package com.vincent.testfragment;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;





/**
 * Created by zhuguohui
 * Date: 2021/6/30
 * Time: 10:16
 * Desc:
 */
public class TestFragment extends Fragment {

    private String title;

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        Log.i("zzz",title+"执行 setUserVisibleHint:isVisibleToUser="+isVisibleToUser);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("zzz",title+"执行 onCreate");
    }


    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable  ViewGroup container, @Nullable  Bundle savedInstanceState) {
        Log.i("zzz",title+"执行 onCreateView");
        View view= inflater.inflate(R.layout.test_fragment,container,false);
        TextView textView= view.findViewById(R.id.tv_title);
        textView.setText(title);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.i("zzz",title+"执行 onViewCreated");
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.i("zzz",title+"执行 onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.i("zzz",title+"执行 onResume");
    }


    @Override
    public void onPause() {
        super.onPause();
        Log.i("zzz",title+"执行 onPause");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.i("zzz",title+"执行 onStop");
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        Log.i("zzz",title+"执行 onHiddenChanged");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("zzz",title+"执行 onDestroy");
    }
}

package com.vincent.testfragment.adapter;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.util.Log;


import com.vincent.testfragment.TestFragment;

import java.util.List;


/**
 * Created by zhuguohui
 * Date: 2021/6/30
 * Time: 10:16
 * Desc:
 */
public class TestAdapter extends FragmentStatePagerAdapter {

    List<String> titles;

    public TestAdapter(FragmentManager fm, List<String> titles) {
        super(fm);
        this.titles = titles;
    }


    @Override
    public Fragment getItem(int position) {
        Log.i("zzz", "new " + titles.get(position));
        TestFragment testFragment = new TestFragment();
        testFragment.setTitle(titles.get(position));
        return testFragment;
    }

    @Override
    public int getCount() {
        return titles.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }
}

使用TabLayout+ViewPager测试,代码很简单就不贴出来。

测试日志

初始化ViewPager 打印的日志

2021-06-30 11:05:58.162 21249-21249/? W/nt.testfragmen: Accessing hidden method Landroid/app/LoadedApk;-><init>(Landroid/app/ActivityThread;Landroid/content/pm/ApplicationInfo;Landroid/content/res/CompatibilityInfo;Ljava/lang/ClassLoader;ZZZ)V (greylist-max-o, linking, denied)
2021-06-30 11:05:58.486 21249-21249/com.vincent.testfragment I/zzz: new1个fragment
2021-06-30 11:05:58.486 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 setUserVisibleHint:isVisibleToUser=false
2021-06-30 11:05:58.486 21249-21249/com.vincent.testfragment I/zzz: new2个fragment
2021-06-30 11:05:58.486 21249-21249/com.vincent.testfragment I/zzz:2个fragment执行 setUserVisibleHint:isVisibleToUser=false
2021-06-30 11:05:58.486 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 setUserVisibleHint:isVisibleToUser=true
2021-06-30 11:05:58.486 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 onCreate
2021-06-30 11:05:58.486 21249-21249/com.vincent.testfragment I/zzz:2个fragment执行 onCreate
2021-06-30 11:05:58.487 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 onCreateView
2021-06-30 11:05:58.490 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 onViewCreated
2021-06-30 11:05:58.491 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 onStart
2021-06-30 11:05:58.491 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 onResume
2021-06-30 11:05:58.491 21249-21249/com.vincent.testfragment I/zzz:2个fragment执行 onCreateView
2021-06-30 11:05:58.493 21249-21249/com.vincent.testfragment I/zzz:2个fragment执行 onViewCreated
2021-06-30 11:05:58.493 21249-21249/com.vincent.testfragment I/zzz:2个fragment执行 onStart
2021-06-30 11:05:58.493 21249-21249/com.vincent.testfragment I/zzz:2个fragment执行 onResume


点击第6个fragment执行的日志

2021-06-30 11:07:32.429 21249-21249/com.vincent.testfragment I/zzz: new6个fragment
2021-06-30 11:07:32.430 21249-21249/com.vincent.testfragment I/zzz:6个fragment执行 setUserVisibleHint:isVisibleToUser=false
2021-06-30 11:07:32.430 21249-21249/com.vincent.testfragment I/zzz: new5个fragment
2021-06-30 11:07:32.430 21249-21249/com.vincent.testfragment I/zzz:5个fragment执行 setUserVisibleHint:isVisibleToUser=false
2021-06-30 11:07:32.430 21249-21249/com.vincent.testfragment I/zzz: new7个fragment
2021-06-30 11:07:32.430 21249-21249/com.vincent.testfragment I/zzz:7个fragment执行 setUserVisibleHint:isVisibleToUser=false
2021-06-30 11:07:32.430 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 setUserVisibleHint:isVisibleToUser=false
2021-06-30 11:07:32.430 21249-21249/com.vincent.testfragment I/zzz:6个fragment执行 setUserVisibleHint:isVisibleToUser=true
2021-06-30 11:07:32.430 21249-21249/com.vincent.testfragment I/zzz:6个fragment执行 onCreate
2021-06-30 11:07:32.431 21249-21249/com.vincent.testfragment I/zzz:5个fragment执行 onCreate
2021-06-30 11:07:32.431 21249-21249/com.vincent.testfragment I/zzz:7个fragment执行 onCreate
2021-06-30 11:07:32.432 21249-21249/com.vincent.testfragment I/zzz:6个fragment执行 onCreateView
2021-06-30 11:07:32.437 21249-21249/com.vincent.testfragment I/zzz:6个fragment执行 onViewCreated
2021-06-30 11:07:32.437 21249-21249/com.vincent.testfragment I/zzz:6个fragment执行 onStart
2021-06-30 11:07:32.437 21249-21249/com.vincent.testfragment I/zzz:6个fragment执行 onResume
2021-06-30 11:07:32.437 21249-21249/com.vincent.testfragment I/zzz:5个fragment执行 onCreateView
2021-06-30 11:07:32.440 21249-21249/com.vincent.testfragment I/zzz:5个fragment执行 onViewCreated
2021-06-30 11:07:32.440 21249-21249/com.vincent.testfragment I/zzz:7个fragment执行 onCreateView
2021-06-30 11:07:32.443 21249-21249/com.vincent.testfragment I/zzz:7个fragment执行 onViewCreated
2021-06-30 11:07:32.443 21249-21249/com.vincent.testfragment I/zzz:5个fragment执行 onStart
2021-06-30 11:07:32.443 21249-21249/com.vincent.testfragment I/zzz:5个fragment执行 onResume
2021-06-30 11:07:32.443 21249-21249/com.vincent.testfragment I/zzz:7个fragment执行 onStart
2021-06-30 11:07:32.443 21249-21249/com.vincent.testfragment I/zzz:7个fragment执行 onResume
2021-06-30 11:07:33.099 21249-21249/com.vincent.testfragment I/zzz:2个fragment执行 onPause
2021-06-30 11:07:33.099 21249-21249/com.vincent.testfragment I/zzz:2个fragment执行 onStop
2021-06-30 11:07:33.102 21249-21249/com.vincent.testfragment I/zzz:2个fragment执行 onDestroy
2021-06-30 11:07:33.102 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 onPause
2021-06-30 11:07:33.103 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 onStop
2021-06-30 11:07:33.103 21249-21249/com.vincent.testfragment I/zzz:1个fragment执行 onDestroy

分析

在ViewPager中使用Fragment 。onResume方法需要加以判断,onReusme方法会有两种方式被调用。一种是Fragment所在的Activity从后台到前台,一种ViewPager预加载。而此时能判断fragment是否显示就需要通过setUserVisibleHint方法中传递过来的参数。只有在fragment显示的时候,并且调用onResume方法的时候。才去调用相关的逻辑代码。当然在setUserVisibleHint方法中如果参数是true也可以判断。不过这个方法第一次调用的时候比onCreate还要早,此时fragment中需要的参数可能还不完整。建议保留到onResume方法。

show hide的影响

使用如下的代码来显示或隐藏fragment时,fragment中的onPauseonResume等方法不会被调用。
只会调用onHiddenChanged方法。需要在这个方法中接受变量,并保存。用于标识当前的fragment是否可见。

  		FragmentTransaction transaction=fm.beginTransaction();
        transaction.hide(fragemnt);
        transaction.commit();

特别需要注意的是onHiddenChanged方法 默认是空实现,而且该方法只有直接被**FragmentTransaction **影响到的fragment才会调用。如果你是fragment中还有子fragment,需要自己将调用传递下去。对于一般的使用了ViewPager的fragment。我自己写了一个工具类来实现。

/**
 * Created by zhuguohui
 * Date: 2021/6/29
 * Time: 15:26
 * Desc:用于从viewpager中获取正在显示的fragment
 */
public class ViewPagerUtil {

    public static Fragment getCurrentFragment(ViewPager viewPager) {
        if (viewPager == null) {
            return null;
        }
        try {
            Field field = viewPager.getClass().getDeclaredField("mItems");
            field.setAccessible(true);
            ArrayList arrayList = (ArrayList) field.get(viewPager);
            for (int i 

以上是关于记录Fragment的显示时长的主要内容,如果未能解决你的问题,请参考以下文章

ViewPager开关没有显示片段之间

Asynctask结果显示重新创建片段后

Android 片段生命周期

在 Fragment 中使用 WebView

从片段中的相机拍照

如果 SQLite 为空或有记录,则更改片段中的视图