问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

懒加载的原理(懒加载的原理及实现)

发布网友 发布时间:2024-10-04 08:44

我来回答

1个回答

热心网友 时间:2024-10-21 12:45

如何实现图片懒加载?

懒加载也叫延迟加载,指的是在长网页中延迟加载图片的时机,当用户需要访问时,再去加载,这样可以提高网站的首屏加载速度,提升用户的体验,并且可以减少服务器的压力。它适用于图片很多,页面很长的电商网站的场景。懒加载的实现原理是,将页面上的图片的src属性设置为空字符串,将图片的真实路径保存在一个自定义属性中,当页面滚动的时候,进行判断,如果图片进入页面可视区域内,则从自定义属性中取出真实路径赋值给图片的src属性,以此来实现图片的延迟加载。

kotlin—lazy及其原理

lazy是属性委托的一种,是有kotlin标准库实现。它是属性懒加载的一种实现方式,在对属性使用时才对属性进行初始化,并且支持对属性初始化的操作时进行加锁,使属性的初始化在多线程环境下线程安全。lazy默认是线程安全的。

lazy既然是属性委托的一种,那么其语法也遵循属性委托的语法:

对应的lazy的语法为:

由于lazy为函数,其最后一个参数是函数,那么可以使用lamda的语法代替最后一个函数参数:

通过2中的语法,我们知道lazy是kotlin标准库中的重载函数,我们先从标准库lazy的函数的分析其原理:

lazy函数默认情况下是同步安全锁模式,其可以指定线程同步模式、线程公有模式、非线程安全模式,也在同步模式时指定使用的锁对象,lazy函数会创建懒加载类的实现类,通过懒加载类的实现类实现不同模式的懒加载。

我们依次分析SynchronizedLazyImpl、SafePublicationLazyImpl、UnsafeLazyImpl这三种模式是怎么实现懒加载的:

SynchronizedLazyImpl是同步模式的懒加载,它是lazy的默认实现,其在多线程环境下进行初始化是线程安全的,我们看看其源码实现:

同步模式的懒加载SynchronizedLazyImpl的实现原理其实是使用两个属性,一个是公有属性value—对外代表属性的值,一个是私有属性_value——是真正的值。value的get内部对_value进行初始化,如果_value已初始化则直接返回,如果没有初始化过则加锁并调用初始化函数把返回值赋值给_value。

SafePublicationLazyImpl是多线程环境下的公共线程安全模式,我们从其源码分析其原理:

公共线程安全模式SafePublicationLazyImpl与同步模式SynchronizedLazyImpl的区别在于,SafePublicationLazyImpl使用自旋锁进行初始化操作,而SynchronizedLazyImpl是要同步锁的方式进行初始化操作,其他与SynchronizedLazyImpl的实现一样。

SafePublicationLazyImpl是非线程安全的懒加载实现模式,在单线程下进行初始化是没啥问题,但是多线程下是进行初始化是不安全的,我们从其源码分析其原理:

SafePublicationLazyImpl与前面的两种模式的实现方式不一样就是初始化时没有加任何锁,其它是一样的。

上面分析了使用lazy函数之后返回了不同的懒加载实现类及各懒加载实现类的原理,所以lazy的语句最终语句的是:

valpropertyNameby[SynchronizedLazyImpl|SafePublicationLazyImpl|UnsafeLazyImpl]

但具体是怎么个懒加载实现类的value的get方法呢?——下面我们举例,然后通过编译后的字节码分析器实现原理:

举例:

编译后的字节码文件:

通过对生成的字节码的分析,lazy的原理:

注意lazy实现了懒加载,达到在使用时才进行初始化的目的,但是也为此增加了一个懒加载类,如果一个类的初始化操作不耗时却使用lazy进行懒加载是不明智的,lazy的适合场景是类的初始化操作比较耗时占资源。

Fragment数据懒加载及原理

最近据后台同事反馈说,某些接口调用的频率有点高,而这块业务还没完全开放,照理说很少会用到,于是让我查查怎么回事。

我看了下日志,把网络请求日志过滤出来,发现的确有问题,每次打开首页后都有许多那块业务相关的网络请求。于是马上联想到可能是因为首页改版之后嵌套使用了ViewPager,业务未完全开放的那个fragment里嵌套了一个ViewPager,里面有多个fragment,这样每次打开首页都会去加载该page,然后是一连串的fragment初始化以及网络请求,所以为了解决该问题就不得不使用懒加载。

最终想要实现的效果是:1)当fragment不可见的时候不加载数据;2)当数据已经加载过之后,除非手动刷新否则不重新请求数据。

首先,默认情况下,由于ViewPager会预加载左右两边相邻的至少1个fragment,通过setOffscreenPageLimit()设置预加载page数为0并不会起作用,这点从ViewPager的源码中可以看到:

从以上源码可以看出相邻fragment的加载是必然的,但是我们如果可以得知fragment可见性,那么就可以在fragment可见时才去加载数据。这样虽然不是完全的懒加载,只是数据懒加载,但是同样也可以满足我们的需求了。

那么fragment中有没有可以获取当前fragment是否可见的方法呢,当然是有的,它就是setUserVisibleHint(booleanisVisibleToUser)。

无论你使用的是FragmentPagerAdapter还是FragmentStatePagerAdapter,当它们初始化fragment的时候,该方法都会被调用两次。

一次是在实例化的时候,也就是在instantioateItem()方法中:

一次是在用户滑动到当前fragment的时候,在setPrimaryItem()方法中:

另外,当用户从当前fragment滑出的时候,setPrimaryItem()方法也会被调用。

来看下setUserVisibleHint()的注释:

系统正是通过该方法来判断当前fragment的UI是否对用户可见,而该方法被暴露出来的主要目的也是让我们可以提醒系统当前fragment已经不可见了,是时候重新更新fragment的生命周期了。

不过如果只是实现数据懒加载,我们不需要直接去调用该方法,只要覆写它并实现控制数据加载的逻辑就可以了。

这里我参考了一种比较简便的做法,原文来自尹star的ViewPager+FragmentLazyLoad最优解。

实现效果:lazy_load_fragment_demo

项目地址:aJIEw/DemoUI-LazyLoadFragment

可以看到只有第一次进入fragment的时候才会加载数据,而且也不会主动加载相邻的fragment或者已经加载过的数据了。

首先,由于setUserVisibleHint()会在fragment实例化时就先被调用(在onAttach()之前),所以我们最好在view创建完毕之后加载数据,因此需要设置一个view是否初始化完毕的标志位。另外,当然也需要一个view是否可见的标志位,只有等到view可见才允许加载。然后还可以选择保存数据的初始化状态,这样可以控制在fragment生命周期中的合适时机重新加载数据。所以,我们需要以下3个标志位:

然后接下来分为两种情况,一种是view初始化完毕但是此时还不可见的情况。很显然,我们只要判断setUserVisibleHint()中参数的值就可以了:

还有一种情况是,如果当前fragment是整个ViewPager的第一个fragment,那么setUserVisibleHint(true)会在view初始化之前就在setPrimaryItem()中被调用,此时view已经可见了,但是我们要等到view初始化才加载数据,所以我们要在某个地方判断view是否已经初始化并且去加载数据。

最好的地方是在onActivityCreated()中。根据fragment生命周期我们知道,onActivityCreated()会在onCreateView()之后调用,此时view已经初始化完毕,我们可以在这里将isViewInitiated标记为true,同时在这里为第一个显示的fragment加载数据:

最后,我们还需要判断下数据是否已经加载过,避免重复加载。

我们将以上所有判断逻辑写在prepareFetchData()中,判断条件为view已经初始化、可见且数据未加载:

最后再定义一个抽象方法fetchData(),让子类去实现:

这样一个完整的数据懒加载就实现完毕了。

我们可以看下以上操作的日志来验证下我们的想法。

第一次打开,FirstFragment作为第一个可见的fragment立马被初始化:

此时isVisibleToUser会在isViewInitiated之前设为true,所以FirstFragment会在onActivityCreated()中真正开始获取数据。

另外,由于预加载的存在,SecondFragment也会被创建,但是此时还不可见:

当滑动到SecondFragment的时候,SecondFragment状态变为可见,setUserVisibleHint(true)被调用,所以开始获取数据:

而此时FirstFragment由可见变为不可见:

ThirdFragment则开始第一次被创建,同样此时并不可见:

当滑动到ThirdFragment的时候,状态变为可见,所以也就开始获取数据:

此时SecondFragment由可见变为不可见:

而FirstFragment由于超出了ViewPager可以保存的Fragment的数量,所以被销毁:

此时SecondFragment重新变得可见:

而FirstFragment也开始重新被创建:

此时FirstFragment重新变得可见,虽然FirstFragment之前被销毁了,但是由于之前获取的数据会被恢复,所以现在不会重新去获取数据:

当然我们也可以选择在onDestroy()中将isDataInitiated置为false,这样每次fragment重新创建都会重新获取数据。当然前提是你使用的是FragmentStatePagerAdapter,因为如果使用FragmentPagerAdapter,不会每次都调用onDestroy(),fragment实例会被保存。而SecondFragment再次变得不可见,ThirdFragment被销毁,过程与3中移动到ThirdFragment类似,这里就不截图了。

通过以上日志,验证了我们的想法是对的。

另外,如果是ViewPager嵌套ViewPager其实效果也是一样的,如果不做特殊处理,相邻的fragment的会被加载,导致该fragment中的ViewPager会去加载其中的fragment。

热心网友 时间:2024-10-21 12:43

如何实现图片懒加载?

懒加载也叫延迟加载,指的是在长网页中延迟加载图片的时机,当用户需要访问时,再去加载,这样可以提高网站的首屏加载速度,提升用户的体验,并且可以减少服务器的压力。它适用于图片很多,页面很长的电商网站的场景。懒加载的实现原理是,将页面上的图片的src属性设置为空字符串,将图片的真实路径保存在一个自定义属性中,当页面滚动的时候,进行判断,如果图片进入页面可视区域内,则从自定义属性中取出真实路径赋值给图片的src属性,以此来实现图片的延迟加载。

kotlin—lazy及其原理

lazy是属性委托的一种,是有kotlin标准库实现。它是属性懒加载的一种实现方式,在对属性使用时才对属性进行初始化,并且支持对属性初始化的操作时进行加锁,使属性的初始化在多线程环境下线程安全。lazy默认是线程安全的。

lazy既然是属性委托的一种,那么其语法也遵循属性委托的语法:

对应的lazy的语法为:

由于lazy为函数,其最后一个参数是函数,那么可以使用lamda的语法代替最后一个函数参数:

通过2中的语法,我们知道lazy是kotlin标准库中的重载函数,我们先从标准库lazy的函数的分析其原理:

lazy函数默认情况下是同步安全锁模式,其可以指定线程同步模式、线程公有模式、非线程安全模式,也在同步模式时指定使用的锁对象,lazy函数会创建懒加载类的实现类,通过懒加载类的实现类实现不同模式的懒加载。

我们依次分析SynchronizedLazyImpl、SafePublicationLazyImpl、UnsafeLazyImpl这三种模式是怎么实现懒加载的:

SynchronizedLazyImpl是同步模式的懒加载,它是lazy的默认实现,其在多线程环境下进行初始化是线程安全的,我们看看其源码实现:

同步模式的懒加载SynchronizedLazyImpl的实现原理其实是使用两个属性,一个是公有属性value—对外代表属性的值,一个是私有属性_value——是真正的值。value的get内部对_value进行初始化,如果_value已初始化则直接返回,如果没有初始化过则加锁并调用初始化函数把返回值赋值给_value。

SafePublicationLazyImpl是多线程环境下的公共线程安全模式,我们从其源码分析其原理:

公共线程安全模式SafePublicationLazyImpl与同步模式SynchronizedLazyImpl的区别在于,SafePublicationLazyImpl使用自旋锁进行初始化操作,而SynchronizedLazyImpl是要同步锁的方式进行初始化操作,其他与SynchronizedLazyImpl的实现一样。

SafePublicationLazyImpl是非线程安全的懒加载实现模式,在单线程下进行初始化是没啥问题,但是多线程下是进行初始化是不安全的,我们从其源码分析其原理:

SafePublicationLazyImpl与前面的两种模式的实现方式不一样就是初始化时没有加任何锁,其它是一样的。

上面分析了使用lazy函数之后返回了不同的懒加载实现类及各懒加载实现类的原理,所以lazy的语句最终语句的是:

valpropertyNameby[SynchronizedLazyImpl|SafePublicationLazyImpl|UnsafeLazyImpl]

但具体是怎么个懒加载实现类的value的get方法呢?——下面我们举例,然后通过编译后的字节码分析器实现原理:

举例:

编译后的字节码文件:

通过对生成的字节码的分析,lazy的原理:

注意lazy实现了懒加载,达到在使用时才进行初始化的目的,但是也为此增加了一个懒加载类,如果一个类的初始化操作不耗时却使用lazy进行懒加载是不明智的,lazy的适合场景是类的初始化操作比较耗时占资源。

Fragment数据懒加载及原理

最近据后台同事反馈说,某些接口调用的频率有点高,而这块业务还没完全开放,照理说很少会用到,于是让我查查怎么回事。

我看了下日志,把网络请求日志过滤出来,发现的确有问题,每次打开首页后都有许多那块业务相关的网络请求。于是马上联想到可能是因为首页改版之后嵌套使用了ViewPager,业务未完全开放的那个fragment里嵌套了一个ViewPager,里面有多个fragment,这样每次打开首页都会去加载该page,然后是一连串的fragment初始化以及网络请求,所以为了解决该问题就不得不使用懒加载。

最终想要实现的效果是:1)当fragment不可见的时候不加载数据;2)当数据已经加载过之后,除非手动刷新否则不重新请求数据。

首先,默认情况下,由于ViewPager会预加载左右两边相邻的至少1个fragment,通过setOffscreenPageLimit()设置预加载page数为0并不会起作用,这点从ViewPager的源码中可以看到:

从以上源码可以看出相邻fragment的加载是必然的,但是我们如果可以得知fragment可见性,那么就可以在fragment可见时才去加载数据。这样虽然不是完全的懒加载,只是数据懒加载,但是同样也可以满足我们的需求了。

那么fragment中有没有可以获取当前fragment是否可见的方法呢,当然是有的,它就是setUserVisibleHint(booleanisVisibleToUser)。

无论你使用的是FragmentPagerAdapter还是FragmentStatePagerAdapter,当它们初始化fragment的时候,该方法都会被调用两次。

一次是在实例化的时候,也就是在instantioateItem()方法中:

一次是在用户滑动到当前fragment的时候,在setPrimaryItem()方法中:

另外,当用户从当前fragment滑出的时候,setPrimaryItem()方法也会被调用。

来看下setUserVisibleHint()的注释:

系统正是通过该方法来判断当前fragment的UI是否对用户可见,而该方法被暴露出来的主要目的也是让我们可以提醒系统当前fragment已经不可见了,是时候重新更新fragment的生命周期了。

不过如果只是实现数据懒加载,我们不需要直接去调用该方法,只要覆写它并实现控制数据加载的逻辑就可以了。

这里我参考了一种比较简便的做法,原文来自尹star的ViewPager+FragmentLazyLoad最优解。

实现效果:lazy_load_fragment_demo

项目地址:aJIEw/DemoUI-LazyLoadFragment

可以看到只有第一次进入fragment的时候才会加载数据,而且也不会主动加载相邻的fragment或者已经加载过的数据了。

首先,由于setUserVisibleHint()会在fragment实例化时就先被调用(在onAttach()之前),所以我们最好在view创建完毕之后加载数据,因此需要设置一个view是否初始化完毕的标志位。另外,当然也需要一个view是否可见的标志位,只有等到view可见才允许加载。然后还可以选择保存数据的初始化状态,这样可以控制在fragment生命周期中的合适时机重新加载数据。所以,我们需要以下3个标志位:

然后接下来分为两种情况,一种是view初始化完毕但是此时还不可见的情况。很显然,我们只要判断setUserVisibleHint()中参数的值就可以了:

还有一种情况是,如果当前fragment是整个ViewPager的第一个fragment,那么setUserVisibleHint(true)会在view初始化之前就在setPrimaryItem()中被调用,此时view已经可见了,但是我们要等到view初始化才加载数据,所以我们要在某个地方判断view是否已经初始化并且去加载数据。

最好的地方是在onActivityCreated()中。根据fragment生命周期我们知道,onActivityCreated()会在onCreateView()之后调用,此时view已经初始化完毕,我们可以在这里将isViewInitiated标记为true,同时在这里为第一个显示的fragment加载数据:

最后,我们还需要判断下数据是否已经加载过,避免重复加载。

我们将以上所有判断逻辑写在prepareFetchData()中,判断条件为view已经初始化、可见且数据未加载:

最后再定义一个抽象方法fetchData(),让子类去实现:

这样一个完整的数据懒加载就实现完毕了。

我们可以看下以上操作的日志来验证下我们的想法。

第一次打开,FirstFragment作为第一个可见的fragment立马被初始化:

此时isVisibleToUser会在isViewInitiated之前设为true,所以FirstFragment会在onActivityCreated()中真正开始获取数据。

另外,由于预加载的存在,SecondFragment也会被创建,但是此时还不可见:

当滑动到SecondFragment的时候,SecondFragment状态变为可见,setUserVisibleHint(true)被调用,所以开始获取数据:

而此时FirstFragment由可见变为不可见:

ThirdFragment则开始第一次被创建,同样此时并不可见:

当滑动到ThirdFragment的时候,状态变为可见,所以也就开始获取数据:

此时SecondFragment由可见变为不可见:

而FirstFragment由于超出了ViewPager可以保存的Fragment的数量,所以被销毁:

此时SecondFragment重新变得可见:

而FirstFragment也开始重新被创建:

此时FirstFragment重新变得可见,虽然FirstFragment之前被销毁了,但是由于之前获取的数据会被恢复,所以现在不会重新去获取数据:

当然我们也可以选择在onDestroy()中将isDataInitiated置为false,这样每次fragment重新创建都会重新获取数据。当然前提是你使用的是FragmentStatePagerAdapter,因为如果使用FragmentPagerAdapter,不会每次都调用onDestroy(),fragment实例会被保存。而SecondFragment再次变得不可见,ThirdFragment被销毁,过程与3中移动到ThirdFragment类似,这里就不截图了。

通过以上日志,验证了我们的想法是对的。

另外,如果是ViewPager嵌套ViewPager其实效果也是一样的,如果不做特殊处理,相邻的fragment的会被加载,导致该fragment中的ViewPager会去加载其中的fragment。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
在线条形码生成 ie地址栏是什么 ie浏览器的官网地址是什么? 团餐定制电话 抖音电商与广东商务厅启动“富域计划” 简单的晚餐晒美食文案句子 简单的晚餐晒美食说说短句 鸭肉炒什么好吃家常做法 为什么越背越记不住 绯牡丹锦多久浇水 绯牡丹养护要点 关于import的一些思考 生产销售假药和诈骗罪有什么区别? 医生过度医疗属于诈骗吗? 河北女子职业技术学院艺术类学费多少钱一年 广东女子职业技术学院艺术类学费多少钱一年 广东女子职业技术学院各个专业学费是多少 庆阳女子职业学校2024年学费多少 河北女子职业技术学院单招学费多少钱一年 保定女子职业中专2024年学费多少钱一年 贵阳女子职业学校2024年学费多少 拳皇1268疯火神隐藏超必杀怎么放 诈骗罪与销售假药罪哪个重? 诈骗罪和销售假药罪想象竞合怎么处理? 诈骗罪和销售假药罪想象竞合怎么处理? 售卖假药是不是诈骗罪 切诊正常脉象 DNF手游黑暗武士自觉怎么加点 技能摆放顺序分享 空间的皮肤怎样换? 我开通了黄钻,不知道怎么把皮肤全部换成一种颜色 怎么快速输入带√的方框和叉号? 如何把qq消息记录导出为bak格式文件? 六个月宝宝拒奶怎么办 《冒险岛online》冒险岛幽灵船详解心得 冒险岛幽灵船7怎么进 "姐姐我要回家"是谁唱的? “姐姐我要回家”这句歌词是哪首歌里面的谢谢 我的世界国际服怎么注册手游 简单易懂 进来看看 男方父母叫他回老家工作,老家东北。我们现在天津。我该怎么办? 我在天津身份证丢失老家在东北可以不回去补办么? 我今年23~家在东北!要在天津打工!没文化!说话一口东北味~明天要去... ...东北旺的网友帮忙:我要找一个人,只知道他家在东北旺的一个平房区里 ... ...不过15号前就要回家准备上班,我家在东北,苏杭,海南 广告收入属于什么收入 广告收入属于什么科目 广告费算什么收入 广告制作收入属于什么 广告收入记入什么分录 广告收入做什么科目 黄瓜浇水注意事项黄瓜浇水有什么注意事项 广告款收入计入什么