文章目录

  • 1. 前言
  • 2. ViewPager+Fragment优化
    • 2.1 预加载
    • 2.2 懒加载
  • 3. 后记

1. 前言

ViewPager+Fragment的组合比较适合用来做页面的导航,这里因为在Android插件化开发指南——实践之仿酷狗音乐首页一文的实践中需要用来这块的知识。为了app加载更加流畅,这里考虑使用预加载和懒加载两种机制。当然,这里对于ViewPager+Fragment的简单实现,这里记录下:
首先定义好ViewPager控件:

<androidx.viewpager.widget.ViewPagerandroid:id="@+id/fx_viewpager"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/>

然后定义好需要显示的三个子项的布局文件:

然后定义一个ViewPager控件的适配器:

public class PageViewPagerAdapter<T extends View> extends PagerAdapter {// 外部传入的ViewPager对应的Item对象private List<T> mList;public PageViewPagerAdapter(List<T> mList) {this.mList = mList;}@Overridepublic int getCount() {return this.mList.size();}@Overridepublic boolean isViewFromObject(@NonNull View view, @NonNull Object object) {return object == view;}@NonNull@Overridepublic Object instantiateItem(@NonNull ViewGroup container, int position) {container.addView(mList.get(position));return mList.get(position);}@Overridepublic void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {container.removeView(mList.get(position));}
}

然后只需要在Fragment中完成初始化itemView实例,并设置adapter,最后设置OnPageChangeListener监听即可。这里我的效果为:

2. ViewPager+Fragment优化

预加载和懒加载。ViewPager控件有个特有的预加载机制,即默认情况下当前页面左右两侧的1个页面会被加载,以方便用户滑动切换到相邻的界面时,可以更加顺畅的显示出来。这样就会导致本来加载一个页面,其实在背后会预先加载三个页面,也就是会导致内存消耗比较严重。如果页面的数据也很大的时候,可能存在极端的情况,即将内存撑爆,也就是OOM问题。

所以在内存消耗比较低的场景中,可以使用预加载技术来提高响应时间,进而带来比较丝滑的滑动效果。在内存消耗比较高的场景中,对应的需要使用懒加载技术,来延迟资源的加载。懒加载对服务器端和客户端内存有一定的缓解压力作用,预加载则会增加服务器和和客户端压力。

2.1 预加载

ViewPager中,可以通过setOffscreenPageLimit(int limit)来设置预加载页面数量,当前页面相邻的limit个页面会被预加载进内存。不妨来看看源码:

// ViewPager.java
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;public void setOffscreenPageLimit(int limit) {if (limit < DEFAULT_OFFSCREEN_PAGES) { // 如果小于1limit = DEFAULT_OFFSCREEN_PAGES; // 直接设置为1}if (limit != mOffscreenPageLimit) { // 如果大于1mOffscreenPageLimit = limit; // 设置预加载页面数量为设置的limipopulate(); // 填充}
}

从上面的代码中可以看出,预加载无法按照预想的,将limit设置为0来取消预加载。所以我们需要考虑其余的方式来实现取消ViewPager+Fragment的预加载。在博客 ViewPager+Fragment取消预加载(延迟加载)一文中给出了一个解决的思路。即:

通过判断Fragment对用于的可见性来实现,也就是在这个Fragment对用户可见了再进行数据的加载。而再Fragment中提供了两个方法,分别是:

boolean getUserVisibleHint() // 获得Fragment可见状态
void setUserVisibleHint(boolean isVisibleToUser) // 设置Fragment可见状态

我们只需要复写这两个方法即可,就可以判断当前的Fragment是否可见,进而判断是否进行数据的加载。其实根据上面的分析,这里我们知道根本上这个Fragment还是会加载,只是我们将那些实际请求数据的操作放置在了之后,其实也就是懒加载。

也即是说,取消Fragment预加载的解决为使用懒加载。因为预加载从前面代码中我们知道,解决不了,且默认设置为1,也就是会预加载左右两个页面。

2.2 懒加载

比如定义如下一个懒加载的Fragment父类:

public abstract class LazyFragment extends Fragment {private View rootView; // 表示当前View实例对象private boolean isViewCreated = false; // rootView是否创建private boolean isDatasLoaded = false; // 是否加载过private boolean isCurrentVisible = false; // 是否可见@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {if(rootView == null){rootView = inflater.inflate(getLayoutResources(), container, false);}isViewCreated = true; // rootView创建完毕if(getUserVisibleHint()) setUserVisibleHint(true);return rootView;}protected View getRootView(){return rootView;}@Overridepublic void setUserVisibleHint(boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);if(isViewCreated){Log.e("TAG", "isCurrentVisible: " + isCurrentVisible + " | isVisibleToUser: " + isVisibleToUser );if(!isCurrentVisible && isVisibleToUser){// 加载数据if(!isDatasLoaded) lazyLoadData();isDatasLoaded = true;isCurrentVisible = isVisibleToUser;}else if(isCurrentVisible && !isVisibleToUser){// 停止加载数据stopLoadData();isCurrentVisible = isVisibleToUser;}}}/*** 对子类提供一个查找元素的方法*/protected <T extends View> T findViewById(int id) {if(isViewCreated) return (T) rootView.findViewById(id);return null;}/*** 由具体子类来实现这个方法,以实现返回当前页面的布局文件ID*/protected abstract int getLayoutResources();/*** 提供两个方法,用来进行加载数据,或者停止加载数据*/protected abstract void lazyLoadData();protected void stopLoadData(){}
}

然后将原本传入ViewPager的直接通过LayoutInflater实例化的View对象换成Fragment对象。同时,将适配器修改为继承自FragmentPagerAdapter的类:

public class PageViewPagerAdapter<T extends Fragment> extends FragmentPagerAdapter {// 外部传入的ViewPager对应的Item对象private List<T> mList;public PageViewPagerAdapter(@NonNull FragmentManager fm, List<T> mList) {super(fm);this.mList = mList;}@NonNull@Overridepublic Fragment getItem(int position) {return mList.get(position);}@Overridepublic int getCount() {return this.mList.size();}
}

然后再创建ViewPager关联的三个页面的Fragment的时候,就需要继承自前面所定义的LazyFragment,比如在下面的示例中,我使用Handler发送一个延迟消息,来模拟数据的耗时加载:

public class FxPageMusicFragment extends LazyFragment {private View rootView;@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {super.onCreateView(inflater, container, savedInstanceState);rootView = getRootView();initViews();return rootView;}private void initViews() {// todo 初始化一些数据}@Overrideprotected int getLayoutResources() {// 设置布局文件-音乐return R.layout.fx_viewpager_item_yy;}@Overrideprotected void lazyLoadData() {Log.e("TAG", "lazyLoadData: 音乐加载数据");// todo 加载数据模拟Handler handler = new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);if (msg.what == 0){Button button = findViewById(R.id.yy_page_loading_button);button.setText("数据加载完毕。");}}};Message msg = new Message();msg.what = 0;handler.sendMessageDelayed(msg, 1000);}@Overrideprotected void stopLoadData() {super.stopLoadData();Log.e("TAG", "lazyLoadData: 音乐停止加载数据");}
}

最后的效果为:

当然,对于ViewPager+Fragment优化的懒加载处理这块,我看的bilibili的视频:懒加载方案源码解析之一。

3. 后记

对于ViewPager+Fragment优化的懒加载处理,主要参考了上面的那个视频。然后简单修改了一部分。从观看视频到依葫芦画瓢的这个过程,确实也说明了自己知识储备确实不够。还需要多加练习。


References

  • Android懒加载vs预加载------Viewpager+Fragment
  • ViewPager+Fragment组合的预加载和懒加载
  • ViewPager+Fragment取消预加载(延迟加载)
  • 【视频】懒加载方案源码解析之一

Android插件化开发指南——实践之ViewPager+Fragment优化(预加载和懒加载)相关推荐

  1. Android插件化开发指南——实践之仿酷狗音乐首页

    文章目录 1. 前言 2. 布局分析 3. 底部导航栏的实现 4. 顶部导航栏和ViewPager+Fragment的关联 1. 前言 在Android插件化开发指南--2.15 实现一个音乐播放器A ...

  2. Android插件化开发指南——实践之Activity转场效果(仿酷狗音乐启动页)

    文章目录 1. 前言 2. Activity退出动画 2.1 简单使用 2.2 overridePendingTransition 3. 后记 1. 前言 在Android插件化开发指南--2.15 ...

  3. Android插件化开发指南——实践之仿酷狗音乐首页(自定义ImageView控件)

    文章目录 1. 前言 2. 基础环境--实现RecyclerView的网格布局 3. 自定义ImageView 3. 后记 1. 前言 拟定实现效果部分为下图的歌单列表部分,也就是图中红线框出来的部分 ...

  4. Android插件化开发指南——Hook技术(一)【长文】

    文章目录 1. 前言 2. 将外部dex加载到宿主app的dexElements中 3. 插件中四大组件的调用思路 4. Hook 2.1 对startActivity进行Hook 2.1.1 AMS ...

  5. Android插件化开发指南——插件化技术简介

    文章目录 1. 为什么需要插件化技术 2. 插件化技术的历史 3. 插件化实现思路 3.1 InfoQ:您在 GMTC 中的议题叫做<Android 插件化:从入门到放弃>,请问这个标题代 ...

  6. Android插件化开发指南——Hook技术(二)

    文章目录 1. 前言 2. 分析 3. 加载外部资源文件代码 4. References 1. 前言 在上篇Android插件化开发指南--Hook技术(一)[长文]中提到最终的效果其实在插件中的Ma ...

  7. Android插件化开发之解决OpenAtlas组件在宿主的注冊问题

    Android插件化开发之解决OpenAtlas组件在宿主的注冊问题 OpenAtlas有一个问题,就是四大组件必须在Manifest文件里进行注冊,那么就必定带来一个问题,插件中的组件都要反复在宿主 ...

  8. Android插件化开发之动态加载本地皮肤包进行换肤

    Android插件化开发之动态加载本地皮肤包进行换肤 前言: 本文主要讲解如何用开源换肤框架 android-skin-loader-lib来实现加载本地皮肤包文件进行换肤,具体可自行参考框架原理进行 ...

  9. Android插件化开发之动态加载三个关键问题详解

    本文摘选自任玉刚著<Android开发艺术探索>,介绍了Android插件化技术的原理和三个关键问题,并给出了作者自己发起的开源插件化框架. 动态加载技术(也叫插件化技术)在技术驱动型的公 ...

  10. Android插件化原理和实践 (一) 之 插件化简介和基本原理简述

    1 插件化简介 Android插件化技术是一种这几年间非常火爆的技术,也是只有在中国才流行起来的技术,这几年间每每开发者大会上几乎都会提起关于插件化技术和相关方向.在国内各大互联网公司无不都有自己的插 ...

最新文章

  1. Eureka集群搭建,unavailable-replicas服务节点不可用解決方案
  2. MySQL server PID file could not be found!
  3. python 常量_python学习丨变量与常量
  4. 中介者模式小记【原创】
  5. linux中的bash shell的特性
  6. ASP.NET Core 2.0 自定义 _ViewStart 和 _ViewImports 的目录位置
  7. 【渝粤教育】国家开放大学2018年春季 8126-21T制药工程 参考试题
  8. keep-alive使用笔记
  9. 京东开通数字人民币“硬件钱包”线上消费功能
  10. Golang sort 包使用
  11. windows 编译FFMPEG
  12. 6线AB相马达与ARDUINO 和 L298N电机之间
  13. 字符串的concat方法_字符串concat()方法
  14. HBuilderX格式化css
  15. c语言自动画波形程序,【小程序】C语言实现简易钢琴-利用sin函数构造不同频率波形模拟各琴键发音...
  16. 知识图谱 | 从六个方面解析知识图谱的价值和应用
  17. 四位共阳极数码管显示函数_单片机利用四位共阳极得数码管显示2016
  18. 电脑使用技巧 快捷键
  19. 农信计算机资料录入试题,农村信用社计算机考试试题.docx
  20. 使用chat-gpt 最新最快方法

热门文章

  1. 中学-综合素质【2】
  2. 抖音那种一道光闪过转场效果是怎么做的?
  3. 根据日期获取周数的计算
  4. WORD/OFFICE排版时插入脚注和分栏冲突的解决方法
  5. python socket和多线程实现多人对话聊天室
  6. 第二届CCF计算机职业资格认证考试题解(C++)
  7. 设备日常检查(巡检)
  8. 非模式物种ROSE超级增强子鉴定分析详解
  9. eaxyexcel获取指定行,获取总行数
  10. CEC2018:动态多目标测试函数DF6~DF9的PS及PF