Glide源码解析---缓存篇

Glide框架解析---缓存机制介绍及活动缓存 内存缓存 复用池相关类(一) Glide是一个非常成熟的图片加载框架了,一开始从事Android开发就一直使用这个框架,使用的很熟,但是原理和源代码上就没怎么研究了,最近抽出时间来,好好的学习了Glide的源码和原理,我把自己的学习成果分享给大家。因为Glide的原理相对比较复杂,一篇博客很难把Glide说清楚,所以之后我会按照顺序分享6-8篇博客,手写一个阉割版的Glide。 今天我先简单介绍一下Glide的缓存机制及写一... 阅读详情

Glide的缓存分为两部分

  1. 内存缓存
  2. 硬盘缓存

今天我们从源码的角度来分析。

Glide.with(context).load(url).into(imageView);

上面一行代码我们就可以实现图片的加载,这一行代码背后进行了成吨的工作,我们今天只来看看它关于缓存的部分。
在into方法的背后会执行到Engine的load()方法,这里面的工作就是获取图片的最终来源,就包含了缓存的部分

Engine load()

public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
        DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
        Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
    Util.assertMainThread();
    long startTime = LogTime.getLogTime();

    final String id = fetcher.getId();
    //生成缓存key
    EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
            loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
            transcoder, loadProvider.getSourceEncoder());
    //从LruCache中获取缓存
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
        cb.onResourceReady(cached);//回调 进行弱引用HashMap缓存的写入
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
        }
        return null;
    }
	//在value为弱引用的hashMap中获取
	EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
	if (active != null) {
	    cb.onResourceReady(active);//回调 进行弱引用HashMap缓存的写入
	    if (Log.isLoggable(TAG, Log.VERBOSE)) {
	        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
	    }
	    return null;
	}
	
	EngineJob current = jobs.get(key);
	if (current != null) {
	    current.addCallback(cb);
	    if (Log.isLoggable(TAG, Log.VERBOSE)) {
	        logWithTimeAndKey("Added to existing load", startTime, key);
	    }
	    return new LoadStatus(cb, current);
	}
	//上诉两种方案都获取不到 EngineResource 开启线程
	EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
	DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
	        transcoder, diskCacheProvider, diskCacheStrategy, priority);
	EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
	jobs.put(key, engineJob);
	engineJob.addCallback(cb);
	engineJob.start(runnable);
	
	if (Log.isLoggable(TAG, Log.VERBOSE)) {
	    logWithTimeAndKey("Started new load", startTime, key);
	}
	return new LoadStatus(cb, engineJob);
}

通过上面的代码注释我们可以看到Glide的Engine类将获取图片来源进行了两大部分的分割,一部分是内存缓存,一部分是硬盘缓存,有的同学会问了,内存我看到了硬盘缓存在哪里提现,这部分就在//上诉两种方案都获取不到 EngineResource 开启线程这个注释下面,EngineRunnable中的代码稍后我会发出来,这里我们先看第一部分

内存缓存的获取

从LruCache中获取
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);


private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    //如果skipMemoryCache()传入true则isMemoryCacheable为false 表示跳过内存缓存
    if (!isMemoryCacheable) { 
        return null;
    }
    
    EngineResource<?> cached = getEngineResourceFromCache(key);
    //如果LruCache中有缓存
    if (cached != null) {
        //引用计数++
        cached.acquire();
        //加入到activeResources这个弱引用的hashMap中 活动缓存 正在使用
        activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
    }
    return cached;
}


弱引用HashMap中获取
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);


private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
        return null;
    }

    EngineResource<?> active = null;
    WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
    if (activeRef != null) {
        active = activeRef.get();
        if (active != null) {
            //引用计数++
            active.acquire();
        } else {
            //从弱引用hashMap中移除,添加到LruCache中
            activeResources.remove(key);
        }
    }

    return active;
}

内存缓存的写入

弱引用HashMap的缓存写入

private void handleResultOnMainThread() {
    if (isCancelled) {
        resource.recycle();
        return;
    } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
    }
    //通过EngineResourceFactory构建出了一个包含图片资源的EngineResource对象
    engineResource = engineResourceFactory.build(resource, isCacheable);
    hasResource = true;

    // Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
    // synchronously released by one of the callbacks.
    engineResource.acquire();
    //回调Engine中写入缓存
    listener.onEngineJobComplete(key, engineResource);

    for (ResourceCallback cb : cbs) {
        if (!isInIgnoredCallbacks(cb)) {
            engineResource.acquire();
            cb.onResourceReady(engineResource);
        }
    }
    // Our request is complete, so we can release the resource.
    engineResource.release();
}


@SuppressWarnings("unchecked")
@Override
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
    Util.assertMainThread();
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null) {
        resource.setResourceListener(key, this);

        if (resource.isCacheable()) {
            //存入弱引用HashMap对象中
            activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
        }
    }
    // TODO: should this check that the engine job is still current?
    jobs.remove(key);
}

引用计数

void acquire() {
    if (isRecycled) {
        throw new IllegalStateException("Cannot acquire a recycled resource");
    }
    if (!Looper.getMainLooper().equals(Looper.myLooper())) {
        throw new IllegalThreadStateException("Must call acquire on the main thread");
    }
    ++acquired;
}

void release() {
    if (acquired <= 0) {
        throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
    }
    if (!Looper.getMainLooper().equals(Looper.myLooper())) {
        throw new IllegalThreadStateException("Must call release on the main thread");
    }
    if (--acquired == 0) {
        //当引用为0时,回调 进行LruCache缓存写入
        listener.onResourceReleased(key, this);
    }
}

LruCache的缓存写入

@Override
public void onResourceReleased(Key cacheKey, EngineResource resource) {
    Util.assertMainThread();
    activeResources.remove(cacheKey);
    if (resource.isCacheable()) {
        //写入LruCache缓存中
        cache.put(cacheKey, resource);
    } else {
        resourceRecycler.recycle(resource);
    }
}

EngineResource缓存对象

引用计数的方式判断当前图片是否正在使用
正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。
缓存先写入activeResources弱引用的HashMap然后引用计数的方式判断是否加入到LruCache类LruResourceCache

在loadFromCache()方法的一开始,首先就判断了isMemoryCacheable是不是false,如果是false的话就直接返回null。这是什么意思呢?其实很简单,我们刚刚不是学了一个skipMemoryCache()方法吗?如果在这个方法中传入true,那么这里的isMemoryCacheable就会是false,表示内存缓存已被禁用。

activeResources就是一个弱引用的HashMap,用来缓存正在使用中的图片,我们可以看到,loadFromActiveResources()方法就是从activeResources这个HashMap当中取值的。使用activeResources来缓存正在使用中的图片,可以保护这些图片不会被LruCache算法回收掉。

硬盘缓存的获取

DiskCacheStrategy.NONE: 表示不缓存任何内容。
DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。

先来看EngineRunnable对象中的run方法内容
EngineRunnable线程对象

class EngineRunnable implements Runnable, Prioritized {

    public EngineRunnable(EngineRunnableManager manager, DecodeJob<?, ?, ?> decodeJob, Priority priority) {
        this.manager = manager;
        this.decodeJob = decodeJob;
        this.stage = Stage.CACHE;
        this.priority = priority;
    }

    @Override
    public void run() {
        if (isCancelled) {
            return;
        }

        Exception exception = null;
        Resource<?> resource = null;
        try {
            resource = decode(); //解码
        } catch (Exception e) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "Exception decoding", e);
            }
            exception = e;
        }

        if (isCancelled) {
            if (resource != null) {
                resource.recycle();
            }
            return;
        }

        if (resource == null) {
            onLoadFailed(exception);
        } else {
            onLoadComplete(resource);
        }
    }
    
    private Resource<?> decode() throws Exception {
        if (isDecodingFromCache()) {
            return decodeFromCache();//从硬盘缓存中获取
        } else {
            return decodeFromSource();//从网络中下载获取
        }
    }
}

从硬盘缓存中获取decodeFromCache()

private Resource<?> decodeFromCache() throws Exception {
    Resource<?> result = null;
    try {
        //对应DiskCacheStrategy.RESULT表示获取缓存转换后的图片
        result = decodeJob.decodeResultFromCache();
    } catch (Exception e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Exception decoding result from cache: " + e);
        }
    }

    if (result == null) {
        //对应DiskCacheStrategy.SOURCE表示获取缓存原始图片
        result = decodeJob.decodeSourceFromCache();
    }
    return result;
}

会先去调用DecodeJob的decodeResultFromCache()方法来获取缓存,如果获取不到,会再调用decodeSourceFromCache()方法获取缓存,这两个方法的区别其实就是DiskCacheStrategy.RESULT和DiskCacheStrategy.SOURCE这两个参数的区别

//DiskCacheStrategy.RESULT
public Resource<Z> decodeResultFromCache() throws Exception {
    if (!diskCacheStrategy.cacheResult()) {
        return null;
    }
    long startTime = LogTime.getLogTime();
    Resource<T> transformed = loadFromCache(resultKey);
    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    return result;
}

//DiskCacheStrategy.SOURCE
public Resource<Z> decodeSourceFromCache() throws Exception {
    if (!diskCacheStrategy.cacheSource()) {
        return null;
    }
    long startTime = LogTime.getLogTime();
    Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());
    return transformEncodeAndTranscode(decoded);//对图片进行转换和转码
}



private Resource<T> loadFromCache(Key key) throws IOException {
    //从DisLruCache中获取缓存文件
    File cacheFile = diskCacheProvider.getDiskCache().get(key);
    if (cacheFile == null) {
        return null;
    }
    Resource<T> result = null;
    try {
        result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
    } finally {
        if (result == null) {
            diskCacheProvider.getDiskCache().delete(key);
        }
    }
    return result;
}

调用getDiskCache()方法获取到的就是Glide自己编写的DiskLruCache工具类的实例,然后调用它的get()方法并把缓存Key传入,就能得到硬盘缓存的文件了。如果文件为空就返回null,如果文件不为空则将它解码成Resource对象后返回即可

从网络下载获取decodeFromSource()

public Resource<Z> decodeFromSource() throws Exception {
    Resource<T> decoded = decodeSource();
    return transformEncodeAndTranscode(decoded);
}


private Resource<T> decodeSource() throws Exception {
    Resource<T> decoded = null;
    try {
        long startTime = LogTime.getLogTime();
        //从网络中下载
        final A data = fetcher.loadData(priority);
        if (isCancelled) {
            return null;
        }
        decoded = decodeFromSourceData(data);
    } finally {
        fetcher.cleanup();
    }
    return decoded;
}

private Resource<T> decodeFromSourceData(A data) throws IOException {
    final Resource<T> decoded;
    //判断时候支持缓存原始图片
    if (diskCacheStrategy.cacheSource()) {
        decoded = cacheAndDecodeSourceData(data);
    } else {
        long startTime = LogTime.getLogTime();
        decoded = loadProvider.getSourceDecoder().decode(data, width, height);
    }
    return decoded;
}

硬盘缓存的写入

 private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
   long startTime = LogTime.getLogTime();
    SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
    //将原始图片写入到硬盘缓存
    diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
    startTime = LogTime.getLogTime();
    Resource<T> result = loadFromCache(resultKey.getOriginalKey());
    return result;
}

转换后的图片缓存写入

private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
    long startTime = LogTime.getLogTime();
    Resource<T> transformed = transform(decoded);
    //转换后的图片缓存写入
    writeTransformedToCache(transformed);
    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    return result;
}

private void writeTransformedToCache(Resource<T> transformed) {
    //是否允许缓存转换后的图片
    if (transformed == null || !diskCacheStrategy.cacheResult()) {
        return;
    }
    long startTime = LogTime.getLogTime();
    SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
    //将转换后的图片写入到缓存
    diskCacheProvider.getDiskCache().put(resultKey, writer);
}
字节Android岗面试必问:Glide的缓存机制,金三银四旗开得胜 } if (decodeFormat == null) { decodeFormat = DecodeFormat.DEFAULT; } return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat); } } 这里也就是构建Glide对象的地方了。那么观察第22行,你会发现这里new出了一个LruResourceCache,并把它赋值到了memoryCache这个对象上面... 阅读详情

相关推荐

Android——Glide加载网络图片——缓存

利用Glide实现图片的三级缓存 图片加载框架之所以高效,是因为它不但封装了访问网络的步骤,而且引入了三级缓存机制。 缓存过程说明如下:(1)先到内存(运存)中查找图片;(2)有找到就直接显示内存图片,没找到的话再去磁盘(闪存)查找图片;(3)在磁盘能找到就直接显示磁盘图片,没找到的话再去请求网络; 如此便形成“内存→磁盘→网络”的三级缓存。 Glide的缓存参数设置方法 Glide使用Req...

小白龙白龙马的博客 1535

Glide的缓存机制原理分析

一、Glide请求图片的流程 当执行 Glide.with(this).load("").into(imageview)的into方法时候,该方法接收一个Target或者一个ImageView,Target是一个接口,默认的实现类包括SimpleTarget,ViewTarget,ImageViewTarget等,如果传递的是ImageView接下来回执行以下几步: 调用RequestBuil...

一杯清泉 3960

【Android】Glide基本用法及With方法源码解析

Glide是一个快速高效的Android图片加载库。专门用来帮助开发者从网络、文件系统或应用资源中加载图片,并显示在 `ImageView` 或其他 UI 组件上。它具备高效的缓存机制和解码能力,能够处理图片、GIF、视频缩略图等多种媒体文件。

Patrick_yuxuan的博客 2402

Glide系列(四) — Glide缓存流程分析

Glide 是一个图片加载库,跟它同类型的库还有 Picasso、Fresco、Universal-Image-Loader 等。基于 Glide 优秀的缓存管理策略和生命周期关联的特点,目前市面上对 Glide 的使用非常广,因此我们有必要深入研究下 Glide 相关的实现原理,便于更好的使用它。

Elson的博客 1982

Glide源码解析

Glide是一款专为Android设计的开源图片加载库。有以下特点:1.支持高效加载网络、本地及资源图片;2.具备良好的缓存策略及生命周期管理策略;3.提供了简易的API和强大的功能。本文将对其源码进行剖析。

云瀚的自习室 1301

Glide-源码详解

前言:之前的文章中,笔者介绍了很多Glide的使用方法,但是由于Glide框架封装得太好了,很多人在使用的时候,只是知其然不知其所以然,为了不要仅仅成为”cv工程师”,只会复制粘贴,所以这篇文章我们就一起来研究一下Glide的源码,看看Glide到底是怎么将一张图片加载出来的~

夏雨 2万+

第三方库github地址汇总

1.butterknife: https://github.com/JakeWharton/butterknife 使用 android { ... // Butterknife requires Java 8. compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { im

qingwangwang的博客 5447

读源码-Glide源码解析

文章目录1-初始化with()2-加载准备load()3-加载显示图片 into()4-Glide三级缓存机制5-总结 本文基于Glide版本: com.github.bumptech.glide:glide:4.11.0 Glide最常用的一行代码如下,也概括了Glide的初始化、加载图片(本地、缓存、网络图片)、绑定显示的流程。本文就从该行代码开启Glide的源码之旅。 Glide.with(this).load(url).into(mainPic); 1-初始化with() 首先是Glide.with

SuperMing学习之路 845

Android Glide 缓存机制及源码全解

Glide 会在开始一个新的图片请求之前检查以下多级的缓存: 活动资源 (Active Resources) - 现在是否有另一个 View 正在展示这张图片? 内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中? 资源类型(Resource) - 该图片是否之前曾被解码、转换并写入过磁盘缓存? 数据来源 (Data) - 构建这个图片的资源是否之前曾被写入过文件缓存?

帅次的博客 3317

Glide的缓存源码分析

Glide的缓存流程 上一篇讲解了Glide的整体流程,其实很多时候,只有第一次加载图片的时候,我们才会按照那一个流程去走。因为很多时候,我们都是有缓存了。有了缓存之后,加载流程就会稍微变一下了。那么今天,我们就来讲解一下Glide中的缓存。在讲解Glide缓存之后,我建议大家先去了解一下LinkedHashMap的实现。因为这里涉及到LRU算法。 先来一张Glide缓存的流程图吧,让大家对Glide的流程有一个印象,方便之后的分析,以下流程图是基于配置了允许缓存的流程,配置了不允许缓存的不在本博客的讨论范

nufuli123的博客 663

ST7789驱动实战:从SPI时序到RGB565显示的完整配置解析

本文详细解析了ST7789液晶驱动芯片的SPI时序配置与RGB565显示实现,从数据手册解读到代码实战,涵盖SPI接口配置、关键寄存器设置及完整初始化流程。通过优化DMA传输和常见问题排查,帮助开发者高效驱动ST7789显示屏,适用于嵌入式显示项目开发。

weixin_29162439的博客 184

Glide源码解析3 -- 缓存功能

一 前言 我们知道,Glide的一大优势就是它的缓存功能是它的一大优势之一,虽然其他图片框架也有缓存功能比如Picasso,但是Picasso只能缓存原始图片,而Glide却可以缓存多种规格的图片,也就是说可以通过ImageView的大小来缓存相应的图片大小;同时通过API灵活的制定缓存策略,实现高效缓存。 福利:这里附上各个图片框架的对比图 二 缓存机制简介 2.1 缓存对象 Glide缓存对象分为两种,也就是上面提到的分别是原始图片和经过转换后的图片: 原始图片:就是图片源的初始大小和分辨率 转换后的

qq_39431405的博客 2039

路由工具之策略路由policy-based-routing、MQC三板斧流分类、流行为、流策略

PBR(Policy-Based Routing,策略路由)策略路由可理解为控制路由,因为PBR策略的优先级高于传统路由表的转发,所以它主要用于影响数据包转发的技术。

Hades_Ling的优快云博客 5580

Android Glide 缓存模块源码深度解析

在 Android 开发领域,图片加载是一个极为常见且关键的功能。Glide 作为一款被广泛使用的图片加载库,其缓存模块是提升图片加载效率和性能的核心组件。合理的缓存机制能够显著减少网络请求,降低流量消耗,同时加快图片显示速度,为用户带来流畅的使用体验。本文将深入 Glide 缓存模块的源码,从整体架构到具体实现细节,全方位剖析其工作原理。在移动应用中,图片资源通常占据较大的存储空间和网络带宽。频繁的网络请求不仅会增加用户的流量成本,还会导致图片加载缓慢,影响用户体验。通过缓存机制,Glide 可以将已经加

Android开发领域创作者 1158

如何浏览与下载全球免费的地图高分辨率(亚米级)的遥感影像?

前言 如果你想使用商业卫星的高分辨遥感影像,一般价格昂贵。我们可以使用地图平台上免费的高分辨率数据。目前数据源很多,比如说google earth、天地图、四维地球、必应地图等等。这些影像统一有一个共同点就是:RGB三波段;Web地图瓦片服务。这意味着我们可以通过瓦片服务下载影像,但下载的影像只有三个波段,分别是 一.引言 二、可使用高分辨率的地图数据源 1.Google Earth影像 2.Esri World Image 3.天地图 4.必...

锐多宝的地理空间 2万+

西安电子科技大学841学长经验分享(总成绩第三名)

西安电子科技大学841学长经验分享(总成绩第三名)

qq_30953547的博客 1082

Glide 缓存机制

Glide缓存分为 内存缓存 和 硬盘缓存。 这两个缓存模块的作用各不相同,内存缓存 的主要作用是防止应用重复将图片数据读取到内存当中,而 硬盘缓存 的主要作用是防止应用重复从网络或其他地方重复下载和读取数据。 缓存key 那么Glide的缓存Key是怎么生成的呢?我不得不说,Glide的缓存Key生成规则非常繁琐,决定缓存Key的参数竟然有10个之多。不过繁琐归繁琐,至少逻辑还是比较简单的,...

jiang19921002的博客 7975

【大模型+知识图谱】大模型与知识图谱融合:技术演进、实践应用与未来挑战

大模型与知识图谱的融合不是简单拼接,而是认知智能的范式重构。当符号主义的精确性与连接主义的泛化能力深度交织,我们正站在通向可信AGI的关键路口。正如OpenKG技术委员会王昊奋教授所言:“2025年将是图模协同从技术探索走向规模落地的分水岭”。参考文献OpenKG 2024年度报告(同济大学王昊奋)大型语言模型与知识图谱协同研究综述(海豚实验室,2024)知识图谱与大模型结合方法概述(黄巍,2023)图模互补:知识图谱与大模型融合综述(优快云,2024)

知识靠谱的博客 1092

Glide 4.9源码解析-缓存策略

本文Glide源码基于4.9,版本下载地址如下:Glide 4.9 前言 在分析了Glide的图片加载流程后,更加发觉到Glide的强大,于是这篇文章将继续深入分析Glide的缓存策略。不过今天的文章的源码很多基于上一篇加载流程的基础之上,因此还没有看上一篇的小伙伴,建议先去阅读Glide4.9源码解析-图片加载流程效果会更佳哟! 一、设计 1. 二级缓存 内存缓存:基于LruCache和弱...

jsyjst的博客 646

sim800c插卡后指示灯还是一秒一闪无法接收发送短信,如何解决?

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1167

【YOLOv11】从零搭建基于YOLOv11深度学习算法的农作物虫害检测系统(附带环境搭建+完整代码+讲解)

《基于YOLOv11的农作物病虫害智能检测系统》项目摘要 本项目开发了一个基于YOLOv11深度学习算法的农作物病虫害检测系统,支持玉米、水稻、草莓和西红柿等四大类经济作物的30多种病虫害识别。系统采用多模态检测方式,支持图片、视频和实时摄像头输入,并集成了完整的Web应用框架。 技术架构上,系统以PyTorch实现YOLOv11算法为核心,结合Flask搭建深度学习服务,SpringBoot构建业务后端,Vue3+TypeScript开发前端界面,MySQL数据库存储检测数据。系统具备用户权限管理功能,支

AnChenliang_1002的博客 812

Android Glide 原理解析

Android Glide 原理解析一.提供的功能特性二.简单使用三.源码结构1.内存缓存(1)MemoryCache(2)BitmapPool(3)二级内存缓存2.磁盘缓存3.图片对应的Key4.Glide对象5.RequestManager—绑定生命周期6.GenericRequestBuilder对象7.Target对象8.RequestTracker对象9.Engine、EngineJob...

依生依世 7656

002、搭建你的第一个Agent开发环境:Python与核心库安装

在上一篇《Agent智能体开发入门:概念、应用与前景》中,我们描绘了Agent智能体的宏伟蓝图,理解了其作为“数字员工”的巨大潜力。然而,许多开发者在迈出第一步时,常常被复杂的依赖、版本冲突和晦涩的配置所劝退,空有想法却无从下手。这种感觉就像拿到了一张藏宝图,却找不到出发的港口。本文正是你的“启航指南”。我们将彻底解决这个痛点,通过一个清晰、可复现的流程,为你搭建一个专为Agent开发量身定制的Python环境。一个纯净、隔离的Python虚拟环境,避免项目间的“依赖地狱”。一套经过验证的核心工具链。

G_RE_RE的博客 111

Glide——缓存分析整理

文章目录LruCache概述原理DiskLruCache概述Glide缓存概述资源分类缓存设计Glide 缓存源码分析1、生成key2、创建缓存对象LruResourceCache3、获取内存缓存中的图片4、开启加载图片线程5、获取磁盘缓存6、写入磁盘7、写入内存缓存写入弱引用缓存写入LruCache LruCache 概述 LruCache是Android 3.1所提供的一个缓存类,所以在And...

PdKingLiu 2051

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Glide v4 缓存解析

不同于以往的缓存键值仅以URL作为唯一标识,Glide 针对不同缓存场景构建了不同的key,活动资源和内存缓存使用的键还和磁盘资源缓存略有不同,以适应内存 ,选项,比如影响 Bitmap 配置的选项或其他解码时才会用到的参数。第一步:生成缓存的key,这里会根据上面所说的,模型,签名,长宽,变换以及原始资源,变换后的资源,以及选项创建一个key。大概逻辑就是,先从本地资源缓存文件查找有没有需要的资源,如果没有找到,则去查找元数据缓存,如果还是没有就去源数据去加载,并将当前的数据写入磁盘缓存。

2401_89733819的博客 752

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Android Glide源码解析

功能介绍使用文章介绍以及和Picasso的对比分析请参考Introduction to Glide, Image Loader Library for Android, recommended by Google由于这篇文章使用glide的老版本,因此有些使用方法可能不太一致了。 本文基于github上Glide最新代码4.0.0版本做解析。 最基本的使用方式如下:Glide.with(this

u014592587的专栏 5750

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

上一篇: 算法系列-----树的遍历
下一篇: Okhttp源码解析(二)
omyrobin
博客等级 码龄12年 65粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值