Android Fragment懒加载机制分析与详解

news2024/12/26 0:18:46

一、介绍

        Fragment是Android3.0以后引进,称为碎片。它与Activity非常相似,用一个Activity中描述一些行为或者一部分用户界面,使用多个Fragment可以在一个单独的Activity中建立多个UI面板,也可以在多个Activity中使用Fragment。

        Fragment的引入主要是处理UI展示,所以在理解的时候,可以将它理解成布局控件,常见的fragment搭配有:

1.引入布局,作为布局中控件,通过fragmentmanager来控制

2.导航内容:Navigation组件

3.作为viewpage的子view:viewpage

二、功能介绍

1.作为布局

        作为布局控件,加载到布局中展示,通过我们先把fragment开发完毕,然后在Activity中加入即可。

通过fragmentManage提交进去。

getSupportFragmentManager().beginTransaction().add(R.id.fragment,fragments).commitNow();

这种做法是最常见的,但是如何判断当前fragment的是否已添加进去呢?通过isAdd()方法。

注意:

很多人在使用这种方法,特别是延迟处理和耗时处理的回调,发现当前fragment被销毁了,如果处理这些可以通过isAdd()来判断一下。避免对象销毁,业务还在进行。

2.ViewPager

        Viewpage是我们比较常见的,fragment作为viewItem使用,但是viewpage的滑动功能,导致会对fragment进行提前加载到内存的机制,如果不提前添加进行,在滑动计算是一个不好控制的局面。

        正常的Activity都是当前窗口只有一个,如果是fragment可能出现多个,这就会导致提前加载会引起View的提前曝光,数据提前请求等,这和所见即所得还有有区别的。这个就需要我们去理解这个懒加载机制,当页面真的添加,并展示出来才会处理。这里面只是介绍了懒加载与viewpage存在的问题,为什么会,接下我们会核心的去介绍懒加载。

3.导航Navigation

关于导航的使用,可以查看我的以前文章:

Android JetPack底部导航Navigation 组件的介绍与使用_android 导航_蜗牛、Z的博客-CSDN博客

三、懒加载

想知道懒加载,先要了解fragment的一些特性。

1.require:

在获取变量的时候,不能调用这个方法,否则会抛异常

会主动给你检查一下,对空指针直接抛异常,这就会导致在复杂的场景下,应用的兼容性更差,特别是在kotlin语言中,不懂的以为都是调用一样,其实不然。

2.页面可见

2.1添加:

fragment是作为view控件来使用的,虽然 看似与Activity有着一样的方法与功能,但是不能单独存在,它自身是没有窗口,依托Activity来完成。

选配:Viewpage+FragmentPagerAdapter

这里面的adapter选取了FragmentPagerAdapter,FragmentPagerAdapter对PagrAdapter进行了扩展,在页面的提交和保活做了支持。

FragmentPagerAdapter的创建:

创建的时候behavior有两种

public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;

第一种,behavior=0在页面创建通过调用setUserVisibleHint

第一种,behavior=1在页面创建通过调用setMaxLifecycle(fragment, State.STARTED)

正常,我们通过setUserVisibleHint来判断页面,那么在创建adapter的时候,behavior默认是0.

fragment:

在fragment中,我们需要处理

override fun setUserVisibleHint(isVisibleToUser: Boolean) 

的回调。

FragmentPagerAdapter

2.2源码分析

由于fragment在viewpage中相当于view,viewpage又是一个支持滑动的,所以viewpage提供了一个

1.public void setOffscreenPageLimit(int limit)

的方法,默认至少需要提前初始化一个。

所以,在初始化的时候,回调的

override fun setUserVisibleHint(isVisibleToUser: Boolean)

默认都是false。只有在adapter

public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object)

中才会去确定做回调

当adapter回调时,这时,我们的fragment在ViewGroup中处于可见状态。

至于setUserVisibleHint(isVisibleToUser: Boolean)回调多少次false,取决于

setOffscreenPageLimit(int limit)的大小。

2.初始化规则是:

从选中位置开始,左右两边初始化limit个,如果左右两边的数量小于limit

左边:Math.min(currentIndex,limit),右边Math.min(count-currentIndex,limit)

最多一次初始化=limit*2+1;最后会落在currentIndex的fragment上。

懒加载核心

第一种:behavior=0

setUserVisibleHint(isVisibleToUser: Boolean),在fragment创建的时候,都会收到回调为false。

懒加载的核心是所见即所得,显然isVisibleToUser为false不是我们想要的结果。所以我们应该在fragment记录上一次的状态,如果状态不一致,可以肯定当前页面没有发生变化,页面没变化,直接不处理任何数据逻辑。即,当前页面是不可见。

    override fun UserVisibleHint(isVisibleToUser: Boolean) {
        if (visibleToUser == isVisibleToUser)
            return
        MyLog.log("lazy", "===${isVisibleToUser}")
        visibleToUser = isVisibleToUser
        arguments?.let {
            MyLog.log("lazy", "${it.get("key")}===${isVisibleToUser}")
        }

    }

所以日志输出,只有当前选中的页面,其他都被拒了

    override fun initView() {
        adapter= MyViewPageAdapter(list,supportFragmentManager)
        bind.myviewpage.offscreenPageLimit=2

        bind.myviewpage.adapter=adapter
        bind.myviewpage.currentItem=5

    }

这样,当 UserVisibleHint(isVisibleToUser: Boolean)回调为true,当前fragment是可见的。

第二种:behavior=1

当behavior=1是,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT

Adapter走的LifecycleRegistry进行监听,状态也是通过lifecycle进行回调。

所以:

在fragment的源码中也有进行解释。

fragment

在adapter中,如果当前fragment可见,回到的状态是:RESUME

所以:我们只要进行监听:

     (lifecycle as LifecycleRegistry).addObserver(object : LifecycleEventObserver{
            override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
              
            }
        })

从日志可以看出Event

  1. CREATE,START:当页面被创建,首先回调的是这两个状态
  2. RESUME:当前页面可见
  3. PAUSE:从可见到不可见
  4. STOP:页面被销毁

所以,当你采用behavior=1时,只需要判断RESUME这个即可。

参考Demo:

abstract class BaseLazyFragment<V : ViewDataBinding> : Fragment() {

    var visibleToUser: Boolean = false
    lateinit var bind: V
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        bind = DataBindingUtil.inflate(inflater, getLayoutResId(), container, false)
        return bind.root
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let { initData(it) }
        (lifecycle as LifecycleRegistry).addObserver(object : LifecycleEventObserver{
            override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                onStateChangeds(source,event)
            }
        })

    }

    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        UserVisibleHint(isVisibleToUser)
    }

    @LayoutRes
    abstract fun getLayoutResId(): Int

    abstract fun UserVisibleHint(isVisibleToUser: Boolean)

    abstract fun initData(bund: Bundle)

    abstract fun  onStateChangeds(source: LifecycleOwner, event: Lifecycle.Event)

}

class MyLazyChildFragment : BaseLazyFragment<MyLazyViewBind>() {
    override fun getLayoutResId(): Int {
        return R.layout.fragment_lazy_test
    }

    var key = ""
    var mEvent: Lifecycle.Event? = null


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        activity?.runOnUiThread {
            arguments?.let {
                bind.textTitle.text = "当前页面序号${it.getString("key")}"

            }
        }

    }


    override fun initData(bund: Bundle) {

        bund?.let {
            key = it.getString("key", "")
        }
    }


    override fun UserVisibleHint(isVisibleToUser: Boolean) {
        if (visibleToUser == isVisibleToUser)
            return
        visibleToUser = isVisibleToUser
        arguments?.let {
            MyLog.log("lazy", "${it.get("key")}===${isVisibleToUser}")
        }

    }

    override fun onStateChangeds(source: LifecycleOwner, event: Lifecycle.Event) {
        MyLog.log("lazy", "${key}===Event=${event.name}")
        if (mEvent == event)
            return
        mEvent = event
        if (mEvent == Lifecycle.Event.ON_RESUME) {
            MyLog.log("lazy", "${key}===Event=${event.name},可见")
        }else{
            MyLog.log("lazy", "${key}===Event=${event.name},不可见")
        }
    }

    companion object {
        @Synchronized
        fun getInstance(index: Int): Fragment {
            var fragment = MyLazyChildFragment()
            var bundle = Bundle()
            bundle.putString("key", "${index}")
            fragment.arguments = bundle
            return fragment
        }
    }


}

Adapter:

class MyViewPageAdapter : FragmentPagerAdapter {
    var list = mutableListOf<Fragment>()

    constructor(list: MutableList<Fragment>, fm: FragmentManager) : super(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

        this.list = list
    }

    override fun getCount(): Int {
        return list?.size
    }

    override fun getItem(p0: Int): Fragment {
        return list?.get(p0)
    }
}

Page的初始化:

    override fun initView() {
        adapter= MyViewPageAdapter(list,supportFragmentManager)
        bind.myviewpage.offscreenPageLimit=1

        bind.myviewpage.adapter=adapter
        bind.myviewpage.currentItem=5

    }

注意:

        Demo的代码是kotlin与DataBinding写法,在代码demo整理的时候需要小心,不会这两种语法的可以查看我的文章。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/455959.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux I/O复用函数的使用情况和select接口的介绍

I/O 复用使得程序能同时监听多个文件描述符&#xff0c;这对于提高程序的性能至关重要。通常&#xff0c; 网络程序在下列情况下需要使用 I/O 复用技术&#xff1a; 1.TCP服务器同时要处理监听套接字和连接套接字 2.服务器同时要处理TCP请求和UDP请求。 3.程序同时要处理多个套…

档案库房温湿度管理暂行规定

档案馆温湿度管理暂行规定 来源&#xff1a;本站 日期&#xff1a;2021-8-27 浏览量&#xff1a;1067 档案馆温湿度管理暂行规定 (1985年11月23日国家档案局 国档发〔1985〕42号通知印发) 档案馆是永久保管档案的基地&#xff0c;档案馆建筑是档案馆工作的基础…

用Flutter你得了解的七个问题

Flutter是Google推出的一款用于构建高性能、高保真度移动应用程序、Web和桌面应用程序的开源UI工具包。Flutter使用自己的渲染引擎绘制UI&#xff0c;为用户提供更快的性能和更好的体验。 Flutter使用Dart语言&#xff0c;具有强大的类型、效率和易学能力&#xff0c;基本上你…

前端实战(三):element-ui开关组件的二次封装

目录 二次封装 Switch 开关 原始效果 设计效果 实现步骤 在日常开发过程中&#xff0c;大多数项目主要以 vue 为主&#xff0c;并且现在很多公司仍在使用着 vue。但在使用element-ui组件时通常会遇到一些问题&#xff1a;如组件样式与设计不符合、组件不存在某个功能等等&a…

5年测试经验,自动化都不会?月薪11K都难拿....

我接触了太多测试同行&#xff0c;由于多数同行之前一直做手工测试&#xff0c;现在很迫切希望做自动化测试&#xff0c;其中不乏工作5年以上的同行。 我从事软件自动化测试已经近十年&#xff0c;接触过底层服务端、API 、Web、APP、H5 等等&#xff0c;对自动化算是比较了解…

Spring种存取Bean的5种注解

存取Bean的五种注解 存储Bean对象两种方式1.添加一行bean2.使用注解的方式(5大注解)Controller(控制器存储)Service(服务存储)Repository(仓库存储)Component(组件存储)Configuration(配置存储)方法注解 Bean 获取Bean对象(三种)1.属性注入2.setter注入3.构造方法注入三种注入的…

自动驾驶TPM技术杂谈 ———— CCRT验收标准(测试项目)

文章目录 试验项目行车辅助跟车能力测试方法前车静止识别与响应前车低速识别与响应前车减速识别与响应前车切入识别与响应前车切出识别与响应跟随前车启停 单车道组合控制能力测试方法车道居中保持交通拥堵辅助高速驾驶辅助 换道辅助能力测试方法无干扰车换道有干扰车换道 泊车…

《面试1v1》java泛型

我是 javapub&#xff0c;一名 Markdown 程序员从&#x1f468;‍&#x1f4bb;&#xff0c;八股文种子选手。 面试官&#xff1a;小伙子,说实话,泛型这个机制一开始我也是一头雾水,搞不太明白它到底要解决什么问题。你能不能不那么书呆子,给我普普通通地讲一讲泛型? 候选人…

真正的进步,是创业者和员工的共同进步

再伟大的事业&#xff0c;也是由人一点一滴创造出来的。 人&#xff0c;是企业中最基础的存在&#xff0c;下层基础决定上层建筑&#xff0c;管理公司企业&#xff0c;也是人与人之间的交流问题。 创业十余年&#xff0c;与市场打交道&#xff0c;也与人打交道。 对外&#x…

ArduPilot Kakute F7 AIO DIYF450 之GPS配置

ArduPilot Kakute F7 AIO DIYF450 之GPS配置 1. 源由2. 步骤2.1 模块预测试2.2 物理连接2.3 UART配置2.4 Compass使能2.5 GPS使能2.6 校准Compass 3. GPS & Compass配置效果3.1 Mission Planner界面3.2 QGroundControl界面3.3 ArduPilot配置修改 4. 参考资料 1. 源由 之前…

选址-路径问题(Location-Routing Problem, LRP)

今天为大家介绍的是选址-路径问题(Location-Routing Problem, LRP)&#xff0c;首先上目录 目录 问题简介 基础模型、扩展问题及应用 算法 参考文献 1 问题简介 为了更好地了解这个问题&#xff0c;我们不妨当一波老板。 想象一下我们是经营一家口罩生产企业的老板&am…

RocketMQ基本概念

RocketMQ 一 引言 Message Queue&#xff08;消息 队列&#xff09;&#xff0c;从字⾯上理解&#xff1a;⾸先它是⼀个队列。先进先出的数据结构——队列。消息队列就是所谓的存放消息的队列。 消息队列解决的不是存放消息的队列的⽬的&#xff0c;解决的是通信问题&#x…

C语言实现顺序表--数据结构

魔王的介绍&#xff1a;&#x1f636;‍&#x1f32b;️一名双非本科大一小白。魔王的目标&#xff1a;&#x1f92f;努力赶上周围卷王的脚步。魔王的主页&#xff1a;&#x1f525;&#x1f525;&#x1f525;大魔王.&#x1f525;&#x1f525;&#x1f525; ❤️‍&#x1…

项目管理中引入PMO的应用研究——以H研究所为例

摘 要 本文从项目管理办公室&#xff08;PMO&#xff09;的基本内涵出发&#xff0c;探讨了PMO在以“项目”为主要工作组织方式的H研究所应用过程中发挥的作用、具有的优势、取得的成效与存在的不足&#xff0c;从而实现为企业培养专业的项目经理团队&#xff0c;为业务部门定…

Ubuntu20.04安装CUDA和CUDNN

CUDA是GPU深度学习的运行库&#xff0c;那么cuDNN就是训练加速工具&#xff0c;两者要相互配合使用&#xff0c;所以一般机器学习需要训练引擎(tensorflow-gpu) CUDA cuDNN使用。想不安装cuDNN是不可以的&#xff0c;而且cuDNN版本要和CUDA版本相互搭配。 1、前置工作 查看…

最新动态 | 大势智慧参加广东省应急测绘保障与安全生产演练

4月20日&#xff0c;2023年度广东省应急测绘保障与安全生产演练在台山市赤溪镇鱼塘湾举行。本次演练由广东自然资源厅主办&#xff0c;广东省国土资源测绘院、江门市自然资源局和台山市人民政府承办。在省市各指导单位与参演单位的多方协同与指挥下&#xff0c;应急测绘保障与安…

常用PLC学习资料下载地址

常见PLC的资料一般在官网都可以找到&#xff0c;今天整理一下&#xff0c;把西门子、三菱、欧姆龙、汇川四家品牌的官方下载地址直接贴出来供大家直接使用。 1、汇川技术官方网站 汇川技术 - 推进工业文明 共创美好生活 (inovance.com)https://www.inovance.com/2、汇川技术资料…

TCP重传、滑动窗口、流量控制、拥塞控制

目录 重传机制 #超时重传 SACK 方法 Duplicate SACK 滑动窗口 流量控制 窗口关闭 拥塞控制 慢启动 拥塞避免算法 拥塞发生 快速恢复 重传机制 TCP 实现可靠传输的方式之一&#xff0c;是通过序列号与确认应答。 在 TCP 中&#xff0c;当发送端的数据到达接收主机时…

rancher部署flink集群

rancher版本&#xff1a;v2.6.8 k8s版本&#xff1a;v1.22.13rke2r1 flink集群版本&#xff1a;1.15.0 flink安装模式&#xff1a;session cluster 写在前面&#xff1a;因为参照官网的说明安装过程中出现了很多问题&#xff0c;特记录于此&#xff0c;避免后续重复踩坑 目…

FE_CSS 精灵图技术 字体图标 CSS三角

一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度。 因此&#xff0c;为了有效地减少服务器接收和发送请求的次数&a…