浅析notifyDataSetChanged内部工作流程

2023-10-29 17:20

本文主要是介绍浅析notifyDataSetChanged内部工作流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先我们知道notifyDataSetChanged是Adater的一个方法,主要用来通知ListView,告诉它Adapter的数据发生了变化,需要更新ListView的显示,所以当Adapter的数据内容改变时会调用notifyDataSetChanged()方法。
直接看看BaseAdapter中notifyDataSetChanged的源码实现,看notifyDataSetChanged是如何工作的

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {private final DataSetObservable mDataSetObservable = new DataSetObservable();public void notifyDataSetChanged() {mDataSetObservable.notifyChanged();}
}


可以看到它调用的是DataSetObservable中的notifyChanged,进入DataSetObservable查看实现

public class DataSetObservable extends Observable<DataSetObserver> {public void notifyChanged() {synchronized(mObservers) {for (int i = mObservers.size() - 1; i >= 0; i--) {mObservers.get(i).onChanged();}}}
}


其中mObervers是在DataSetObservable的父类Observable中定义:

public abstract class Observable<T> {protected final ArrayList<T> mObservers = new ArrayList<T>();}


它是一个DataSetObserver类型的ArrayList,最终执行的是DataSetObserver的onChange函数。

再来看看DataSetOberver类的源代码:

public abstract class DataSetObserver {public void onChanged() {// Do nothing}public void onInvalidated() {// Do nothing}
}


从上面的过程可以大致看出,如果需要得到ListView更新的通知,首先实现一个DataSetObserver类,重写其中的onChanged回调方法,然后把这个对象添加(注册)到ArrayList中,这样当我们调用notifyDataSetChanged的时候,它会遍历这个ArrayList取出DataSetObserver对象,回调onChanged方法。也就是说我们最终的刷新ListViewd的工作应该在这个onChanged方法中。

那么疑问就是系统在哪个地方实现了实现一个DataSetObserver类,重写其中的onChanged回调方法,然后把这个对象添加到ArrayList当中的。

其实这个工作在setAdapter中完成的,当为ListView设置一个Adapter的时候,就在这个Adapter中注册了一个回调监听,也就是上面说的实现一个DataSetObserver类,重写其中的onChanged回调方法,然后把这个对象添加到ArrayList当中,当Adapter调用notifyDataSetChanged的时候,就会回调onChanged函数,在onChanged里面进行ListView的更新,这样ListView就进行更新操作。

那么我们来看看ListAdapter中setAdapter的实现:   

@Overridepublic void setAdapter(ListAdapter adapter) {//这里判断是否已经注册了监听//如果已经注册,则取消注册//如果重复调用setAdapter,下面的代码就会执行mDataSetObserver从ArrayList中移除if (mAdapter != null && mDataSetObserver != null) {mAdapter.unregisterDataSetObserver(mDataSetObserver);}resetList();mRecycler.clear();if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);} else {mAdapter = adapter;}mOldSelectedPosition = INVALID_POSITION;mOldSelectedRowId = INVALID_ROW_ID;// AbsListView#setAdapter will update choice mode states.super.setAdapter(adapter);if (mAdapter != null) {mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();mOldItemCount = mItemCount;//得到数据的数量mItemCount = mAdapter.getCount();checkFocus();//看到没有这里定义了一个AdapterDataSetObserver,它就是DataSetObserver的实现类mDataSetObserver = new AdapterDataSetObserver();//看到这里应该明白了,这里将这个DataSetObserver实现了类对象添加到ArrayList中,这样就可以回调了mAdapter.registerDataSetObserver(mDataSetObserver);mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());int position;if (mStackFromBottom) {position = lookForSelectablePosition(mItemCount - 1, false);} else {position = lookForSelectablePosition(0, true);}setSelectedPositionInt(position);setNextSelectedPositionInt(position);if (mItemCount == 0) {// Nothing selectedcheckSelectionChanged();}} else {mAreAllItemsSelectable = true;checkFocus();// Nothing selectedcheckSelectionChanged();}//会导致调用measure()过程 和 layout()过程requestLayout();}


具体的解释直接看代码的注释,这样更方便理解,主要就是mDataSetObserver = new AdapterDataSetObserver()和mAdapter.registerDataSetObserver(mDataSetObserver)这两句,我们上面已经注释了AdapterDataSetObserver就是DataSetObserver的实现类,它重写了onChanged方法。
看AdapterDataSetObserver源代码,在这里就可以看到,notifyDatasetChanged的最终执行的操作是什么了,因为它最终回调了这个onChanged方法。AdapterDataSetObserver是AbsListView的一个内部类   

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {@Overridepublic void onChanged() {//这里是核心操作super.onChanged();if (mFastScroller != null) {mFastScroller.onSectionsChanged();}}@Overridepublic void onInvalidated() {super.onInvalidated();if (mFastScroller != null) {mFastScroller.onSectionsChanged();}}}


最终回调的就是这里的onChanged函数,直接看代码,核心操作在super.onChanged()里面。
super.onChanged意思就是执行AdapterView.AdapterDataSetObserver里面的onChanged函数,它是ApdaterView的一个内部类。   

class AdapterDataSetObserver extends DataSetObserver {private Parcelable mInstanceState = null;@Overridepublic void onChanged() {mDataChanged = true;mOldItemCount = mItemCount;mItemCount = getAdapter().getCount();// Detect the case where a cursor that was previously invalidated has// been repopulated with new data.if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null&& mOldItemCount == 0 && mItemCount > 0) {AdapterView.this.onRestoreInstanceState(mInstanceState);mInstanceState = null;} else {rememberSyncState();}checkFocus();//会导致调用measure()过程 和 layout()过程requestLayout();}@Overridepublic void onInvalidated() {mDataChanged = true;if (AdapterView.this.getAdapter().hasStableIds()) {// Remember the current state for the case where our hosting activity is being// stopped and later restartedmInstanceState = AdapterView.this.onSaveInstanceState();}// Data is invalid so we should reset our statemOldItemCount = mItemCount;mItemCount = 0;mSelectedPosition = INVALID_POSITION;mSelectedRowId = INVALID_ROW_ID;mNextSelectedPosition = INVALID_POSITION;mNextSelectedRowId = INVALID_ROW_ID;mNeedSync = false;checkFocus();requestLayout();}public void clearSavedState() {mInstanceState = null;}}


看到没有,这个类实现了DataSetObserver,正好说明了上面的说法,直接看它的onChanged函数。直接看最后一句requestLayout(),这里就会进行刷新了,
如果细心的话,你应该也会看到在setAdapter中也执行了这个函数,这样就充分说了,执行这个函数,这里是进行了布局和重绘。
这里我们可以知道一点,就是我们上面对ListView的刷新,本质就是调用了requestLayout方法。
 

这篇关于浅析notifyDataSetChanged内部工作流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/302157

相关文章

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Spring Boot分层架构详解之从Controller到Service再到Mapper的完整流程(用户管理系统为例)

《SpringBoot分层架构详解之从Controller到Service再到Mapper的完整流程(用户管理系统为例)》本文将以一个实际案例(用户管理系统)为例,详细解析SpringBoot中Co... 目录引言:为什么学习Spring Boot分层架构?第一部分:Spring Boot的整体架构1.1

nodejs打包作为公共包使用的完整流程

《nodejs打包作为公共包使用的完整流程》在Node.js项目中,打包和部署是发布应用的关键步骤,:本文主要介绍nodejs打包作为公共包使用的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言一、前置准备二、创建与编码三、一键构建四、本地“白嫖”测试(可选)五、发布公共包六、常见踩坑提醒

C#利用Free Spire.XLS for .NET复制Excel工作表

《C#利用FreeSpire.XLSfor.NET复制Excel工作表》在日常的.NET开发中,我们经常需要操作Excel文件,本文将详细介绍C#如何使用FreeSpire.XLSfor.NET... 目录1. 环境准备2. 核心功能3. android示例代码3.1 在同一工作簿内复制工作表3.2 在不同

Ubuntu向多台主机批量传输文件的流程步骤

《Ubuntu向多台主机批量传输文件的流程步骤》:本文主要介绍在Ubuntu中批量传输文件到多台主机的方法,需确保主机互通、用户名密码统一及端口开放,通过安装sshpass工具,准备包含目标主机信... 目录Ubuntu 向多台主机批量传输文件1.安装 sshpass2.准备主机列表文件3.创建一个批处理脚

一个Java的main方法在JVM中的执行流程示例详解

《一个Java的main方法在JVM中的执行流程示例详解》main方法是Java程序的入口点,程序从这里开始执行,:本文主要介绍一个Java的main方法在JVM中执行流程的相关资料,文中通过代码... 目录第一阶段:加载 (Loading)第二阶段:链接 (Linking)第三阶段:初始化 (Initia

Git打标签从本地创建到远端推送的详细流程

《Git打标签从本地创建到远端推送的详细流程》在软件开发中,Git标签(Tag)是为发布版本、标记里程碑量身定制的“快照锚点”,它能永久记录项目历史中的关键节点,然而,仅创建本地标签往往不够,如何将其... 目录一、标签的两种“形态”二、本地创建与查看1. 打附注标http://www.chinasem.cn

通过Docker容器部署Python环境的全流程

《通过Docker容器部署Python环境的全流程》在现代化开发流程中,Docker因其轻量化、环境隔离和跨平台一致性的特性,已成为部署Python应用的标准工具,本文将详细演示如何通过Docker容... 目录引言一、docker与python的协同优势二、核心步骤详解三、进阶配置技巧四、生产环境最佳实践

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

深入浅出Spring中的@Autowired自动注入的工作原理及实践应用

《深入浅出Spring中的@Autowired自动注入的工作原理及实践应用》在Spring框架的学习旅程中,@Autowired无疑是一个高频出现却又让初学者头疼的注解,它看似简单,却蕴含着Sprin... 目录深入浅出Spring中的@Autowired:自动注入的奥秘什么是依赖注入?@Autowired