iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >RecyclerView详解一,使用及缓存机制
  • 315
分享到

RecyclerView详解一,使用及缓存机制

缓存androidkotlin 2023-09-07 07:09:02 315人浏览 薄情痞子
摘要

本文大致会先讲解RecyclerView的基础知识及使用,最后会深入讲解一点原理。当然,本人知识水平有限哈,太深入的东西我现在还没接触到,还请大家包容,阿里嘎多~ 一、RecyclerView的

本文大致会先讲解RecyclerView的基础知识及使用,最后会深入讲解一点原理。当然,本人知识水平有限哈,太深入的东西我现在还没接触到,还请大家包容,阿里嘎多~

一、RecyclerView的历史与发展

既然讲到了RV,那不得不先知道它怎么来的。
 
RecyclerView是Android 5.0提出的新的UI控件,与其一起诞生的还有著名的Material Design以及CardView等新特性。最初位于support.v7包中,这里既然提到了v7,那我就简单介绍一点v4,v7包以及Androidx的历史发展。support-v4是Android 3.0推出的库,为了加入Fragment以及向下兼容老系统,即最低兼容到Android 1.6。support-v7向下兼容到Android 2.1,这两个库中包含有RecyclerView、ViewPager等常用控件。随着时间的推移,现在的Android系统已经发展到13了,显然这两个库就有些跟不上时代了,于是从Android 9.0开始,Google推出了androidx,以后推出的所有新特性都会加入到androidx中,并且androidx包下面的api都是随着扩展库发布的,这些API基本不会依赖于操作系统的具体版本,所有命名中它就不再包含版本号了。
 
所以,现在我们使用的RecyclerView都是包含在androidx包中。我们最开始学Android的时候肯定都接触过ListView,ListView的功能也很强大,在RecyclerView没出现之前,开发者们使用的都是ListView来展示大量的数据。但是ListView的性能比较差,之后我会对比一下二者的缓存策略,扩展性也不是很好,所以具有更加强大功能的RecyclerView诞生了。它包含有横向纵向排列的LinearLayoutManager、网格排列的GridLayoutManager和瀑布流排列的StaggeredGridLayoutManager。下面我先来带大家简单了解一下RecyclerView的使用。

二、RecyclerView的使用

这部分我不会讲很多,毕竟学会使用它也不是很难,具体的大家可以去参考一下《第一行代码》,本文的重点还是放在更深一点的缓存策略,回收复用,LayoutManager 以及 ItemTouchHelper等分析上。

(1)创建数据列表

这里演示的就不搞那么复杂了

class Data(val string: String)

(2)创建Adapter

class BasicAdapter(private val dataList: List<Data>) : RecyclerView.Adapter<BasicAdapter.ViewHolder>() {    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {        val dataString: TextView = view.findViewById(R.id.tv_str)    }    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {        val view = LayoutInflater.from(parent.context)            .inflate(R.layout.title_item, parent, false)        return ViewHolder(view)    }    override fun onBindViewHolder(holder: ViewHolder, position: Int) {        val data = dataList[position]        holder.dataString.text = data.string    }    override fun getItemCount(): Int = dataList.size}

(3)Activity创建RecyclerView对象

// 这是单向布局val layoutManager = LinearLayoutManager(this)vb.recyclerView.layoutManager = layoutManagerval adapter = BasicAdapter(dataList)vb.recyclerView.adapter = adapter// 网格布局// 将第一行代码替换为val layoutManager = LinearLayoutManager(this2) // 表示分两列排布// 瀑布流布局val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)

演示结果就不给大家展示了,比较简单,一笔带过。接下来的内容就涉及到更加深入的部分了。

三、RecyclerView的缓存复用机制

RecyclerView的性能之所以强大,就是得益于它的缓存机制。我们通常认为RV具有四级缓存机制,而官方表示只有三级,这里我还是以四级缓存来讲述。下面是RV的四级缓存结构图。
RV四级缓存结构

层级缓存变量缓存名用途
1mChangeScrap与 mAttachedScrap可见缓存用于布局过程中屏幕可见表项的回收和复用
2mCachedViews缓存列表用于移出屏幕表项的回收和复用,不会清空数据
3mViewCacheExtension自定义缓存自定义一个缓存,我们一般用不到
4RecycledViewPool缓存池用于移出屏幕表项的回收和复用,会将ViewHolder的数据重置

在正式介绍四级缓存之前,我们还需要了解一下RV的Item的几个状态。

方法FLAG含义具体场景
isInvalid()FLAG_INVALIDViewHolder的数据是无效的1. 调用了setAdapter()
2. 调用了notifyDataSetChanged()等方法
isRemoved()FLAG_REMOVEDViewHolder的数据已经被移除调用了notifyItemRemoved()
isUpdated()FLAG_UPDATEViewHolder的数据需要重新绑定1. isInvalid的几种情况
2. 调用了onBindViewHolder()
3. 调用了notifyItemChanged()
isBound()FLAG_BOUND数据已经绑定了某个Item上,数据是有效状态调用了onBindViewHolder()

1. 一级缓存

(1)一级缓存原理

Scrap是RV中最轻量的缓存,包括mChangeScrap和mAttachedScrap,只是作为临时缓存的存在。主要用于缓存出现在屏幕内的item,当我们通过notifyItemRemoved(),notifyItemChanged()通知item发生变化的时候,通过mAttachedScrap缓存没有发生变化的ViewHolder,其他的则由mChangedScrap缓存,添加itemView的时候快速从里面取出,完成局部刷新。通过源码理解一下。

void scrapView(View view) {final ViewHolder holder = getChildViewHolderInt(view);    if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)        || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {    if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {            throw new IllegalArgumentException("Called scrap view with an invalid view."                    + " Invalid views cannot be reused from scrap, they should rebound from"                    + " recycler pool." + exceptionLabel());        }        holder.setScrapContainer(this, false);        mAttachedScrap.add(holder);   } else {        if (mChangedScrap == null) {            mChangedScrap = new ArrayList<ViewHolder>();        }        holder.setScrapContainer(this, true);        mChangedScrap.add(holder);    }}

可以看到,对于页面中显示的Item,当调用 LayoutManager 类的 onLayoutChildren() 方法对views进行布局,这时会将RecyclerView上的items全部暂存到一个 ArrayList 集合,这里的数据是没有做修改的,所以不用重新绑定 Adapter。而如果其他情况比如调用了 notifyItemChanged()notifyItemRangeChanged() 来通知数据发生了更新,数据或位置发生改变,那么该ViewHolder会被缓存到mChangedScrap中,这里存储的是发生了变化的ViewHolder,所以要重新走Adapter的绑定方法。可能我文字表述的不是很清楚,下面我放一张图来助大家理解。
在这里插入图片描述
图中的itemB删除掉,然后itemC,itemD依次移动上来,这里itemA和itemB前后参数没有发生变化(虽然itemB被移除了,但移除的时候它还是有效的,会被打上REMOVED标签,表示它是要删除的),所以他们两个存储到mAttachedscrap(),而itemC和itemD的位置发生了改变,所以他俩要存到mChangedScrap()中去。总结来说,删除itemB时,ABCD都会进入Scrap缓存,删除后,会从Scrap中将ACD取出,A的位置和数据都没有发生变化,CD的位置发生了变化但数据还是原封不动。

(2)一级缓存复用

复用的源码在ViewHolder tryGetViewHolderForPositionByDeadline(*)方法中。我对源码的解释在代码的注释里。

ViewHolder holder = null;// 0) If there is a changed scrap, try to find from there// 这里是从mChangedScrap()取ViewHolder,isPreLayout()判断是否为预布局,是一个特殊情况。// 那什么是预布局呢?稍后我讲LayoutManager的时候会具体说一下if (mState.isPreLayout()) {            holder = getChangedScrapViewForPosition(position);                fromScrapOrHiddenOrCache = holder != null;}// OK,现在我们点进getChangedScrapViewForPosition()方法中看一下是怎么取得ViewHolder// find by position// 这是按照position来取for (int i = 0; i < changedScrapSize; i++) {final ViewHolder holder = mChangedScrap.get(i);    if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); // 添加标签表示从Scrap取        return holder;    }}// find by id// 这是通过定义的id来取if (mAdapter.hasStableIds()) {final int offsetPosition = mAdapterHelper.findPositionOffset(position);    if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {    final long id = mAdapter.getItemId(offsetPosition);        for (int i = 0; i < changedScrapSize; i++) {            // 从mChangedScrap中取holder        final ViewHolder holder = mChangedScrap.get(i);            if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);                return holder;            }        }    }}

从这里开始就是缓存复用真正的第一步,上面的是预加载的特殊情况。

// 1) Find by position from scrap/hidden list/cache// 这里就是从mAttachedScrap()中取ViewHolder了if (holder == null) {holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);    if (holder != null) {    // 这里还要检验ViewHolder的有效性    if (!validateViewHolderForOffsetPosition(holder)) {        // recycle holder (and unscrap if relevant) since it can't be used            if (!dryRun) {            // we would like to recycle this but need to make sure it is not used by                // animation logic etc.                holder.addFlags(ViewHolder.FLAG_INVALID);                if (holder.isScrap()) {                removeDetachedView(holder.itemView, false);                    holder.unScrap();                } else if (holder.wasReturnedFromScrap()) {                  holder.clearReturnedFromScrapFlag();                }                // 如果不满足有效性则直接回收该ViewHolder                recycleViewHolderInternal(holder);            }            holder = null;         } else {          fromScrapOrHiddenOrCache = true;         }    }}// 这是从mAttachedScrap()取VH的核心源码,和上面的差不多。// 只是多了几个判断条件,该holder须是有效的并且未被移除。final int scrapCount = mAttachedScrap.size();// Try first for an exact, non-invalid match from scrap.for (int i = 0; i < scrapCount; i++) {final ViewHolder holder = mAttachedScrap.get(i);// 第二个条件为索引判断,表示只能复用到指定位置    if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position    && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);        return holder;    }}

从源码中可以看到,ViewHolder的复用是有顺序的,首先会判断是否预布局,如果是就从一级缓存中的mChangedScrap()中获取。如果没获取到就去mAttachScrap()和二级缓存中找。而一级缓存之所以说轻量,首先是因为它只针对当前页面显示的这些item,其次是因为它用完就会清空缓存,不占空间,效率也快。所以通知数据更新我们推荐使用notifyItemChanged(),实现局部刷新,用的是一级缓存来实现复用。而如果我们调用notifyDataChanged()来通知更新,会使数据全部进行刷新,不会走Scrap,性能低下。

3. 二级缓存

(1)二级缓存原理

CacheView用于RecyclerView列表位置产生变动时,通常称为离屏缓存,对刚刚移出屏幕的view进行回收。它的默认容量是2(可以修改),同样我们用一张图来帮助理解一下。
在这里插入图片描述

(2)二级缓存复用

接着上面的一级缓存复用讲。
这里还是getScrapOrHiddenOrCachedHolderForPosition()方法里的,如果刚才从mAttachScrap里没取到ViewHolder,那么就会走二级缓存,从mCachedViews里找。

// Search in our first-level recycled view cache. 官方说这里是第一级,但在我们日常使用中还是称他为第二级缓存// 这是根据position来取final int cacheSize = mCachedViews.size();for (int i = 0; i < cacheSize; i++) {final ViewHolder holder = mCachedViews.get(i);    // invalid view holders may be in cache if adapter has stable ids as they can be    // retrieved via getScrapOrCachedViewForId    // 这里要对索引进行判断,只有当位置对得上才能拿来复用,    // 这也就意味着从mCatchedViews中取出的ViewHolder只能复用到指定的位置。    if (!holder.isInvalid() && holder.getLayoutPosition() == position    && !holder.isAttachedToTransitionOverlay()) {    // 如果不在容量范围内,就把ViewHolder丢出去,丢到缓存池中。    if (!dryRun) {        mCachedViews.remove(i);        }        if (DEBUG) {        Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position            + ") found match in cache: " + holder);        }        return holder;    }}

接下来就是源码里的第三步,这里是根据id来取,上面的是根据position来取,相差不大。

// 2) Find from scrap/cache via stable ids, if existsif (mAdapter.hasStableIds()) {holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),    type, dryRun);    if (holder != null) {    // update position        holder.mPosition = offsetPosition;        fromScrapOrHiddenOrCache = true;    }}// 点进getScrapOrCachedViewForId()// Search the first-level cachefinal int cacheSize = mCachedViews.size();for (int i = cacheSize - 1; i >= 0; i--) {final ViewHolder holder = mCachedViews.get(i);    ......}

CachedView的缓存主要是应对来回滑动的情况,这时候CachedView才会真正的起作用,其缓存的ViewHolder不需要重新赋值,就可以直接拿来用了。而且我们还可以修改它的容量,通过下面这个方法来修改。

public void setItemViewCacheSize(int size) {    mRecycler.setViewCacheSize(size);}

总结一下,mAttachedScrapp和mCachedViews都是需要进行索引判断,也就是说从这两个缓存中取出的ViewHolder只能复用到指定的位置。mCachedViews只能缓存屏幕外它容量大小的ViewHolder,超出容量的部分会被移除,丢到缓存池中,一会我再来具体讲解缓存池。

5. 三级缓存

三级缓存ViewCacheExtension是我们自定义的缓存,一般来说官方给的一、二、四级缓存就够用了,我们不会用到它,所以我也就一笔带过了。

if (holder == null && mViewCacheExtension != null) {// We are NOT sending the offsetPosition because LayoutManager does not know it.final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);......}

从源码中分析,如果我们自定义了一个缓存并且前面的一二级缓存没有找到ViewHolder,系统就会从我们自定义的这个缓存里去找ViewHolder。

6. 四级缓存

好的,本文的第一个重点来了!在这里我会详细地分析RV的缓存池机制。
先来看看这一级的复用机制。

(1)缓存池结构

if (holder == null) { // fallback to poolif (DEBUG) {    Log.d(TAG, "tryGetViewHolderForPositionByDeadline("        + position + ") fetching from shared pool");    }    holder = getRecycledViewPool().getRecycledView(type);    if (holder != null) {    holder.resetInternal();        if (FORCE_INVALIDATE_DISPLAY_LIST) {        invalidateDisplayListInt(holder);        }    }}

可以看到,这一部分和之前的一二级缓存复用机制有很大区别,没有那么多的限制条件了,不用判断索引是不是指定位置。但是它需要根据itemType来区分不同类型的ViewHolder。但在了解缓存池的复用机制之前,我们得先知道RecycledViewPool的基本结构。

public static class RecycledViewPool {//同类ViewHolder缓存个数上限为5    private static final int DEFAULT_MAX_SCRAP = 5;    // Tracks both pooled holders, as well as create/bind timing metadata for the given type.    // 回收池中存放单个类型ViewHolder的容器    static class ScrapData {    //同类ViewHolder存储在ArrayList中        ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();        int mMaxScrap = DEFAULT_MAX_SCRAP;    }    //回收池中存放所有类型ViewHolder的容器    SparseArray<ScrapData> mScrap = new SparseArray<>();    ...    //ViewHolder入池按viewType分类入池,一个类型的ViewType存放在一个ScrapData中    public void putRecycledView(ViewHolder scrap) {    final int viewType = scrap.getItemViewType();        final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;        //如果超限了,则放弃入池        if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {        return;        }        if (DEBUG && scrapHeap.contains(scrap)) {        throw new IllegalArgumentException("this scrap item already exists");        }        scrap.resetInternal();        //回收时,ViewHolder从列表尾部插入        scrapHeap.add(scrap);    }        //从回收池中获取ViewHolder对象    public ViewHolder getRecycledView(int viewType) {    // 获取到viewType    final ScrapData scrapData = mScrap.get(viewType);        if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {        final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;            //复用时,从列表尾部获取ViewHolder(优先复用刚入池的ViewHoler)            return scrapHeap.remove(scrapHeap.size() - 1);        }        return null;    }}

我们可以根据上述分析得到如下结论:RecycledViewPool中的ViewHolder存储在SparseArray中,并且按viewType分类存储,同一类型的ViewHolder存放在一个ArrayList中。虽然没有了对索引的判断,但是从mRecyclerPool中取出的ViewHolder只能复用于相同viewType的表项。 说来惭愧,就目前以我的水平来看,我大部分都只用到一种viewType。

那么现在我们再来分析缓存池的复用过程。

(2)缓存池复用

holder = getRecycledViewPool().getRecycledView(type);// 这里是getRecyclerViewPool(),主要作用就是new了一个RecyclerViewPool对象出来。// 然后再根据type来从缓存池中获取对应类型的ViewHolder。RecycledViewPool getRecycledViewPool() {if (mRecyclerPool == null) {    mRecyclerPool = new RecycledViewPool();    }    return mRecyclerPool;}

这就是缓存池复用的运作机制,相信大家都已经对这部分的内容有所了解了,那么什么时候数据会被放到缓存池中呢?

(3)表项放入缓存池的几种情况

item移出屏幕

如果大家有印象的话,上面交代了一部分,超出mCachedViews的部分会被丢到这里来。这种情况就是当你滑动屏幕,item移出到屏幕之外后,超出屏幕两个之外的item被缓存池回收,为什么是两个之外呢?因为这两个是缓存在mCachedViews中的,因为它的复用效率更快,优先级更高。超出两个之外的按照先入先出的原则,被mCachedViews移出缓存。从这里可以看出,当你在滑动屏幕的过程中mCachedViews是不断进行 “输入输出” 的。

一级缓存的ViewHolder无效

在讲一级二级缓存复用机制的时候,我说过从mAttachedScrap,mCachedViews中取ViewHolder时,还需要检验有效性,具体是怎么检验的呢,我们从源码入手。

boolean validateViewHolderForOffsetPosition(ViewHolder holder) {// if it is a removed holder, nothing to verify since we cannot ask adapter anymore    // if it is not removed, verify the type and id.    // item是否被移除    if (holder.isRemoved()) {    // 如果是被移除的,返回是否为预加载        return mState.isPreLayout();    }        // 如果不是预加载布局,就检查ViewType是否和Adapter对应位置的ViewHolder的相同    if (!mState.isPreLayout()) {    // don't check type if it is pre-layout.        final int type = mAdapter.getItemViewType(holder.mPosition);        if (type != holder.getItemViewType()) {        // 如果类型不相同,返回false,即无效        return false;        }    }// 这里是检查id    if (mAdapter.hasStableIds()) {    return holder.getItemId() == mAdapter.getItemId(holder.mPosition);    }    return true;}

从源码上看,只有当缓存中 ViewHolder 的 viewType 或 id 和 Adapter 对应位置上的属性相同时(简单来说就是只有对得上号的才是有效的ViewHolder)才会从一二级缓存中取出复用。否则就会将无效的ViewHolder丢到缓存池中。

还有其他几种情况我就不一一列举了,大体差不多。缓存池就相当于一个回收站,别人不要的都会往缓存池里塞。接下来我要讲一下从缓存池里拿出来复用的ViewHolder和前面几种有什么区别。

(4)从缓存池取出的ViewHolder和前面的区别

holder = getRecycledViewPool().getRecycledView(type);// 刚才没贴的代码现在补上if (holder != null) {holder.resetInternal();    if (FORCE_INVALIDATE_DISPLAY_LIST) {    invalidateDisplayListInt(holder);    }}

这段代码什么意思呢?就是当ViewHolder从缓存池取出来后,判断holder是否为空,如果不为空,说明holder从缓存池中取出来了。那么就执行 holder.resetInternal() ,意思是将取出的ViewHolder重置,我们点进这个方法看一下。

void resetInternal() {//将ViewHolder的flag置0,剩下的一些属性也将其重置,要么置0,要么置空mFlags = 0;mPosition = NO_POSITION;    mOldPosition = NO_POSITION;    mItemId = NO_ID;    mPreLayoutPosition = NO_POSITION;    mIsRecyclableCount = 0;    mShadowedHolder = null;    mShadowingHolder = null;    clearPayload();    mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;    mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;    clearNestedRecyclerViewIfNotNested(this);}// 上面重置时将flag置0,这里flag与FLAG_BOUND与操作,结果必为0// 所以将flag置0相当于解绑boolean isBound() {return (mFlags & FLAG_BOUND) != 0;}

综上所述,从缓存池里取出来的ViewHolder将其重置,复用的时候再重新绑定数据。而一二级缓存无需再绑定数据,直接拿来复用,因为他们的位置和数据都没有变化。当有相同类型的表项插入列表时,不用重新创建 ViewHolder 实例(执行 onCreateViewHolder()),从缓存池中获取即可。到这里,RV的四级缓存复用机制就差不多讲完了,大家也对RV有了更深一步的了解,但是,RV是什么时候又是怎么将这些ViewHolder填充屏幕的呢?这个问题我们还需深入探讨一下LayoutManager,相信大家对这个也不陌生。

来源地址:https://blog.csdn.net/m0_51276753/article/details/125667231

--结束END--

本文标题: RecyclerView详解一,使用及缓存机制

本文链接: https://www.lsjlt.com/news/397847.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • RecyclerView详解一,使用及缓存机制
    本文大致会先讲解RecyclerView的基础知识及使用,最后会深入讲解一点原理。当然,本人知识水平有限哈,太深入的东西我现在还没接触到,还请大家包容,阿里嘎多~ 一、RecyclerView的...
    99+
    2023-09-07
    缓存 android kotlin
  • mysql select缓存机制使用详解
    mysql Query Cache 默认为打开。从某种程度可以提高查询的效果,但是未必是最优的解决方案,如果有的大量的修改和查询时,由于修改造成的cache失效,会给服务器造成很大的开销,可以通过query...
    99+
    2024-04-02
  • PHPlaravel缓存cache机制详解
    目录一、访问多个缓存存储二、从缓存中获取数据1.获取数据并设置默认值2.检查缓存项是否存在3.数值增加/减少4.获取 & 存储5.获取 & 删除三、缓存中存储数据1....
    99+
    2022-11-13
    PHP laravel缓存机制 PHP laravel缓存cache PHP laravel 缓存
  • golang函数缓存机制详解及最佳实践
    go语言中的函数缓存机制通过sync.pool实现了对函数结果的存储和重用,从而提升了程序性能。该机制对纯函数且频繁调用的函数效果显著。最佳实践包括:选择合适的缓存容量、使用小对象、缩短...
    99+
    2024-05-04
    golang 缓存机制 go语言
  • 一文了解Django缓存机制
    目录缓存的介绍Django的6种缓存方式内存缓存locmem.LocMemCache文件缓存filebased.FileBasedCache⭐️数据库缓存db.DatabaseCac...
    99+
    2023-03-20
    Django缓存
  • 详细解读Hibernate的缓存机制
    一、why(为什么要用Hibernate缓存?)Hibernate是一个持久层框架,经常访问物理数据库。为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写...
    99+
    2023-05-31
    hibernate 缓存 te
  • 详解浏览器的缓存机制
    目录前言1 浏览器缓存1.1 浏览器缓存1.2 浏览器缓存的意义2 缓存类型2.1 第一次请求数据2.2 强制缓存2.3 协商缓存2.4 强制缓存和协商缓存的关系3 缓存相关head...
    99+
    2024-04-02
  • Mybatis缓存机制(一级缓存、二级缓存、三级缓存)
    一、含义: 缓存就是内存中的数据,常常来自对数据库查询结果的保存。 使用缓存,我们可以避免频繁与数据库进行交互,从而提高响应速度。 Mybatis的缓存分为一级缓存、二级缓存、三级缓存。 一...
    99+
    2023-09-02
    mybatis 缓存 java
  • mysql的查询缓存及innodb缓存回收机制讲解
    这篇文章主要讲解了“mysql的查询缓存及innodb缓存回收机制讲解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“mysql的查询缓存及innodb缓存回...
    99+
    2024-04-02
  • 关于Guava缓存详解及使用说明
    目录缓存Guava cache详细配置缓存的并发级别缓存的初始容量设置设置最大存储缓存清除策略显式清除Cache LoadingCache缓存 缓存分为本地缓存与分布式缓存...
    99+
    2022-11-13
    Guava缓存 Guava缓存使用 Guava缓存说明
  • PHP 性能优化:缓存机制详解
    php 缓存机制通过在内存中存储数据来提高网站性能,主要有三种类型:内存缓存(极快读取)、文件缓存(持久)、对象缓存(自定义序列化)。实战案例包括使用 apc 进行内存缓存、memcac...
    99+
    2024-05-10
    缓存 php redis
  • 详解Android ViewPager2中的缓存和复用机制
    目录1. 前言 2. 回顾RecyclerView缓存机制 3. offscreenPageLimit原理 4. FragmentStateAdapter原理以及缓存机制 4.1 简...
    99+
    2024-04-02
  • MyBatis 动态SQL和缓存机制实例详解
    有的时候需要根据要查询的参数动态的拼接SQL语句常用标签:- if:字符判断- choose【when...otherwise】:分支选择- trim【where,set】:字符串截取,其中where标签封装查询条件,s...
    99+
    2023-05-31
    mybatis 动态sql 缓存机制
  • SpringBoot缓存Ehcache的使用详解
    目录为什么引入缓存SpringBoot抽象缓存代码实现添加缓存依赖开启缓存数据缓存@Cacheable@CachePut@CacheEvict集成EhCache添加EhCache依赖...
    99+
    2024-04-02
  • spring缓存cache的使用详解
    目录spring缓存cache的使用springcache配置缓存存活时间spring缓存cache的使用 在spring配置文件中添加schema和spring对缓存注解的支持: ...
    99+
    2024-04-02
  • 怎么理解及使用JavaScript缓存
    怎么理解及使用JavaScript缓存,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。随着我们的应用程序的不断增长并开始进行复杂的计算时,对...
    99+
    2024-04-02
  • WCF缓存机制怎么理解
    这篇文章主要讲解了“WCF缓存机制怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“WCF缓存机制怎么理解”吧!缓存是很占内存的,缓存也有它的好处,这里就WCF缓存机制分析一个案例,希望...
    99+
    2023-06-17
  • 如何解析Eureka 缓存机制
    今天就跟大家聊聊有关如何解析Eureka 缓存机制,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。引言Eureka是Netflix开源的、用于实现服务注册和发现的服务。Spring C...
    99+
    2023-06-05
  • nginx缓存以及清除缓存的使用
    目录缓存清除缓存控制nginx缓存缓存 缓存的基本思想是利用客户端访问的时间局限性,将客户端访问过的内容做一个副本,在一定时间内存放到本地,当改数据下次被访问时,不必连接到后端服务器...
    99+
    2024-04-02
  • 如何理解Redis缓存之淘汰机制、缓存雪崩、数据不一致
    本篇内容介绍了“如何理解Redis缓存之淘汰机制、缓存雪崩、数据不一致”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作