当前位置: 首页 > news >正文

【开源框架】Glide的图片加载流程

本篇文章从Glide 4.11源码入手,简单的分析整个图片请求的流程,本着 ”只见树林,不见树木“ 的原则,宏观请求流程,不细究实现细节(细节留坑埋点,之后慢慢写)

引入依赖

以下的所有分析都是基于此版本的Glide分析

//引入第三方库glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

分析

Glide的使用就是短短的一行代码

Glide.with(this).load("xxx").into(imageView);//拆分成三步
RequestManager with = Glide.with(this);RequestBuilder<Drawable> load = with.load("");load.into(iv);
  1. 首先通过构造Glide的单例对象
  2. 调用with给每一个RequestManager绑定一个空白的Fragment来管理图片加载的生命周期
  3. 构建Request对象(真正的实现类SingleRequest)
  4. 请求之前先检测缓存
    1. 先检测活动缓存
    2. 在检测内存缓存
  5. 没有缓存就构建一个新的异步任务
  6. 检测有没有本地磁盘缓存
  7. 没有磁盘缓存,就通过网络请求,返回输入流InputStream
  8. 解析输入流InputStream进行采样压缩,最终拿到Bitmap对象
  9. 对Bitmap进行转换成Drawble
  10. 构建磁盘缓存DiskCache
  11. 构建内存缓存
  12. 最终回到ImageViewTarget显示图片

image.png

with(如何实现生命周期的管控)

调用with方法

public static RequestManager with(@NonNull FragmentActivity activity) {return getRetriever(activity).get(activity);
}

会来到RequestManagerRetriever类的get方法中,在这里区分主线程还是在子线程中使用with。
如果在子线程中,绑定的是app的生命周期,在主线程中会将图片的加载与当前activity的生命周期绑定。
这也是为什么不能在子线程中使用Glide的原因,生命周期的管理会失效。

public RequestManager get(@NonNull FragmentActivity activity) {if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());} else {assertNotDestroyed(activity);FragmentManager fm = activity.getSupportFragmentManager();return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}}

最终会返回一个RequestManager对象。

load

调用load最终会返回一个RequestBuilder对象
load负责做什么?
image.png

into

into创建请求

RequestBuilder

RequestBuilder中的into方法,首先会创建一个请求,它是一个接口,真正的实现类是SingleRequest

private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,@Nullable RequestListener<TranscodeType> targetListener,BaseRequestOptions<?> options,Executor callbackExecutor) {······//1.创建一个请求//这里的Request是一个接口,实际构造的对象是SingleRequest实现类Request request = buildRequest(target, targetListener, options, callbackExecutor);······//2.requestManager.clear(target);target.setRequest(request);//3.继续跟踪tarck()requestManager.track(target, request);return target;}

RequestTracker

一个用于跟踪、取消和重新启动正在进行的、已完成的和失败的请求的类
跟踪track方法最终调用的是runRequest方法。
其中有两个列表:

  • requests:运行中的队列
  • pendingRequests:等待中的队列
public void runRequest(@NonNull Request request) {//1.将请求加入到运行队列中requests.add(request);//2.请求没有暂停就调用SingleRequest的begin方法if (!isPaused) {request.begin();//3.请求暂停了就加入到等待运行的队列中} else {request.clear();pendingRequests.add(request);}}

SingleRequest

SingleRequest类中begin函数是添加了synchronized,保证在多线程下的线程安全
onSizeReady方法,最终返回调用的engine.load()

@Overridepublic void begin() {synchronized (requestLock) {······status = Status.WAITING_FOR_SIZE;if (Util.isValidDimensions(overrideWidth, overrideHeight)) {//1.继续深入此方法onSizeReady(overrideWidth, overrideHeight);} else {target.getSize(this);}······}}

Engine(活动和内存缓存)

这时已经来到了Engine类的load方法中

 public <R> LoadStatus load(GlideContext glideContext,Object model,Key signature,int width,int height,······) {long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;//1.得到一个唯一的key,用于唯一标识图片,用于缓存读取EngineKey key =keyFactory.buildKey(model,signature,width,height,······);//2.将key传入,从缓存中获取图片EngineResource<?> memoryResource;synchronized (this) {memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);if (memoryResource == null) {//4.如果缓存中没有则加载return waitForExistingOrStartNewJob(glideContext,model,signature,width,height,······key,startTime);}}//3.存在图片缓存,则直接将它回调出去cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);return null;}

详看loadFromMemory方法
在这里存在活动缓存和内存缓存两级缓存,这两级缓存都是运行时缓存,当APP进程被杀,这这两级缓存是不再存在的。
活动缓存是:直接面向用户正在被展示的图片,是一个弱引用对象,当弱引用对象被回收之后,会把它持有的图片资源放到内存缓存中

@Nullableprivate EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {······//1.从活动缓存中获取EngineResource<?> active = loadFromActiveResources(key);if (active != null) {return active;}//2.活动缓存没有,才从内存缓存中获取EngineResource<?> cached = loadFromCache(key);if (cached != null) {return cached;}//3.都没有则直接退出return null;}

回过头再看waitForExistingOrStartNewJob方法,这个方法是没有检测到缓存时才会被调用。
首先会再次检测磁盘缓存中是否存在,不存在则创建异步任务

private <R> LoadStatus waitForExistingOrStartNewJob(GlideContext glideContext,Object model,Key signature,int width,int height,······EngineKey key,long startTime) {//1.get磁盘缓存EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);if (current != null) {current.addCallback(cb, callbackExecutor);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Added to existing load", startTime, key);}return new LoadStatus(cb, current);}//2.执行图片各类操作的jobEngineJob<R> engineJob =engineJobFactory.build(key,isMemoryCacheable,useUnlimitedSourceExecutorPool,useAnimationPool,onlyRetrieveFromCache);//3.真正需要执行的任务,最终放入到engineJob中执行DecodeJob<R> decodeJob =decodeJobFactory.build(glideContext,model,key,······);jobs.put(key, engineJob);engineJob.addCallback(cb, callbackExecutor);//4.将decodeJob放入engineJob开始执行engineJob.start(decodeJob);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Started new load", startTime, key);}return new LoadStatus(cb, engineJob);}

engineJob启动decodeJob的执行。最终通过线程池去执行decodeJob操作,可见decodeJob肯定是Runnable的实现类。

public synchronized void start(DecodeJob<R> decodeJob) {this.decodeJob = decodeJob;GlideExecutor executor =decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();executor.execute(decodeJob);}

DecedeJob

执行的是decodeJob中的run方法

@Overridepublic void run() {·······try {//1.重点关注此方法runWrapped();} catch (CallbackException e) {throw e;} catch (Throwable t) {·····} finally {}

runWrapped主要是对不同任务的区分。
具体执行哪一个看此篇文章:https://www.jianshu.com/p/faeeb7bb39a3

private void runWrapped() {switch (runReason) {//1.首次初始化并获取资源case INITIALIZE:stage = getNextStage(Stage.INITIALIZE);currentGenerator = getNextGenerator();runGenerators();break;//2.从磁盘缓存获取不到数据,重新获取case SWITCH_TO_SOURCE_SERVICE:runGenerators();break;//3.获取资源成功,解码数据case DECODE_DATA:decodeFromRetrievedData();break;default:throw new IllegalStateException("Unrecognized run reason: " + runReason);}}

不管是 INITIALIZE 初始化还是 SWITCH_TO_SOURCE_SERVICE 从磁盘缓存获取不到数据进行重试,都是要调用 runGenerators 来获取数据。我们先来看getNextGenerator,在首次初始化时会调用这个方法。可以看到这里由很多的XXXGenerator
DataFetcherGenerator的三个实现子类:

  • ResourceCacheGenerator:从磁盘缓存中获取原始资源处理后的Resource资源
  • DataCacheGenerator:从磁盘缓存中获取原始资源
  • SourceGenerator:从数据源(例如网络)中获取资源

具体从哪一个生成器中获取资源跟用户的使用配置有关,没有配置时默认是Source
因此上面的currentGeneratorSourceGenerator

private DataFetcherGenerator getNextGenerator() {switch (stage) {case RESOURCE_CACHE:return new ResourceCacheGenerator(decodeHelper, this);case DATA_CACHE:return new DataCacheGenerator(decodeHelper, this);case SOURCE:return new SourceGenerator(decodeHelper, this);case FINISHED:return null;default:throw new IllegalStateException("Unrecognized stage: " + stage);}}

继续深入会发现在runGenerators方法中会调用currentGeneratorstartNext方法,很明显是SourceGenenrator的startNext方法。

@Overridepublic boolean startNext() {······loadData = null;boolean started = false;while (!started && hasNextModelLoader()) {//1.注意getLoadDataloadData = helper.getLoadData().get(loadDataListIndex++);if (loadData != null&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {started = true;startNextLoad(loadData);}}return started;}

重点关注getLoadData方法,这个方法中调用了modelLoader.buildLoadData,返回一个LoadData,它是工厂接口ModelLoader中的一个内部类。不必深究这个接口为什么设计,主要知道它是返回的它的实现子类HttpGlideUrlLoader,这时在初始化Glide是动态注册的

@Overridepublic LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height, @NonNull Options options) {// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time// spent parsing urls.GlideUrl url = model;if (modelCache != null) {url = modelCache.get(model, 0, 0);if (url == null) {modelCache.put(model, 0, 0, model);url = model;}}int timeout = options.get(TIMEOUT);return new LoadData<>(url, new HttpUrlFetcher(url, timeout));}

以下截图来自Glide类中
image.png

HttpGlideUrlLoader

既然如此自然而然调用的就是HttpGlideUrlLoader类中的buildLoadData方法

@Overridepublic LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height, @NonNull Options options) {······int timeout = options.get(TIMEOUT);return new LoadData<>(url, new HttpUrlFetcher(url, timeout));}

可以看到最终又创建了一个HttpUrlFetcher,往这个类中深入

HttpUrlFetcher

来到HttpUrlFetcher中,可算是柳暗花明,终于看到了网络请求,最终返回的是一个InputStream对象。
而网络请求的实现原理是使用Android中的HttpURLConnection,但是从Android 4.4开始,HttpURLConnection的实现确实是通过调用okhttp完成的,而具体的方法则是通过HttpHandler这个桥梁,以及在OkHttpClient, HttpEngine中增加相应的方法来实现,当然,其实还涉及一些类的增加或删除。所以想研究网络请求的实现不妨再看以下OkHttp这个网络请求框架。

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {······//1.网络请求urlConnection = connectionFactory.build(url);for (Map.Entry<String, String> headerEntry : headers.entrySet()) {urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());}urlConnection.setConnectTimeout(timeout);urlConnection.setReadTimeout(timeout);urlConnection.setUseCaches(false);urlConnection.setDoInput(true);urlConnection.setInstanceFollowRedirects(false);urlConnection.connect();stream = urlConnection.getInputStream();if (isCancelled) {return null;}final int statusCode = urlConnection.getResponseCode();if (isHttpOk(statusCode)) {return getStreamForSuccessfulRequest(urlConnection);} else if (isHttpRedirect(statusCode)) {······}

DecedeJob

拿到了请求的结果,我们并不能直接显示这张图片,还需要进行采样压缩,否则很容易出现大图OOM。
多级回调最终回到了DecedeJob类中。
在runLoadPath方法中,又继续深入到了LoadPath类中执行采样压缩(这里就不再深入分析是怎么采样压缩的了,目的很明显,最终返回的就是一张被压缩优化的Bitmap图片)

private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)throws GlideException {Options options = getOptionsWithHardwareConfig(dataSource);DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);try {// ResourceType in DecodeCallback below is required for compilation to work with gradle.return path.load(rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));} finally {rewinder.cleanup();}}

PathLoad

采样压缩的细节不再深入,最终返回的是一张优化处理后的Bitmap位图
最终将图片一系列的回调,回到了Engine类中

Engine

回调到EngineJob中,再回调到Engine中的onEngineJobComplete,顾名思义就是到是异步请求任务已经完成

@Override
public synchronized void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {if (resource != null && resource.isMemoryCacheable()) {activeResources.activate(key, resource);}jobs.removeIfCurrent(key, engineJob);
}

调用activate方法将它放入到活动缓存中,注意这是一个弱引用

synchronized void activate(Key key, EngineResource<?> resource) {ResourceWeakReference toPut =new ResourceWeakReference(key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);ResourceWeakReference removed = activeEngineResources.put(key, toPut);if (removed != null) {removed.reset();}}

SingleRequest

将图片保存到活动缓存中后,再由EngineJob回调到SingeRequest中的onResourceReady,意为资源已经准备好了

@GuardedBy("requestLock")private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {······try {······if (!anyListenerHandledUpdatingTarget) {//1.加载动画Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);//2.返回图片和动画target.onResourceReady(result, animation);}} finally {isCallingCallbacks = false;}//3.通知加载成功notifyLoadSuccess();}

ImageViewTarget

最终回到起点,设置图片,调用的是ImageViewTarget中的setResourceInternal方法,最终调用其中的抽象方法setResource

@Overridepublic void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {//1.设置图片资源if (transition == null || !transition.transition(resource, this)) {setResourceInternal(resource);} else {maybeUpdateAnimatable(resource);}}

有3个子类实现了这个方法,最终由它们去显示图片
image.png

DrawableImageViewTarget

这里的view就是一个ImageView对象,至此一张图片就显示在了屏幕上

@Overrideprotected void setResource(@Nullable Drawable resource) {view.setImageDrawable(resource);}

相关文章:

【开源框架】Glide的图片加载流程

本篇文章从Glide 4.11源码入手&#xff0c;简单的分析整个图片请求的流程&#xff0c;本着 ”只见树林&#xff0c;不见树木“ 的原则&#xff0c;宏观请求流程&#xff0c;不细究实现细节&#xff08;细节留坑埋点&#xff0c;之后慢慢写&#xff09; 引入依赖 以下的所有分…...

win10下Mariadb绿色版安装步骤

使用绿色版的mariadb数据库管理软件&#xff0c;免费开源&#xff0c;可以用来替换MySQL。首先从mariadb官网下载绿色版本的压缩包。解压后、配置好即可以使用。 把他解压缩到C:\mariadb\之下。打开powershell&#xff1a; Cd c:\mariadb\bin .\mysql_install_db.exe 这一…...

wiresharak捕获DNS

DNS解析&#xff1a; 过滤项输入dns&#xff1a; dns查询报文 应答报文&#xff1a; 事务id相同&#xff0c;flag里 QR字段1&#xff0c;表示响应&#xff0c;answers rrs变成了2. 并且响应报文多了Answers 再具体一点&#xff0c;得到解析出的ip地址&#xff08;最底下的add…...

vue源码分析(一)——源码目录说明

文章目录 一、如何下载源码&#xff08;可忽略&#xff09;&#xff08;1&#xff09;打开地址&#xff08;2&#xff09;复制链接&#xff08;3&#xff09;git clone 链接 二、源码目录说明1.可以根据你下载的源码通过package.json文件查看vue版本2.源码目录说明 一、如何下载…...

【深度学习】吴恩达课程笔记(二)——浅层神经网络、深层神经网络

笔记为自我总结整理的学习笔记&#xff0c;若有错误欢迎指出哟~ 笔记链接 【深度学习】吴恩达课程笔记(一)——深度学习概论、神经网络基础 吴恩达课程笔记——浅层神经网络、深层神经网络 四、浅层神经网络1.双层神经网络表示2.双层神经网络的前向传播第一层前向传播第二层前…...

UI自动化概念 + Web自动化测试框架介绍

1.UI自动化测试概念:我们先明确什么是UI UI&#xff0c;即(User Interface简称UI用户界面)是系统和用户之间进行交互和信息交换的媒介 UI自动化测试: Web自动化测试和移动自动化测试都属于UI自动化测试&#xff0c;UI自动化测试就是借助自动化工具对程序UI层进行自动化的测试 …...

在 macOS 上的多个 PHP 版本之间切换

文章目录 前言一、前提条件1.引入库需要安装 Xcode 2.安装多个PHP版本2.PHP版本切换 开源替代品 前言 不同项目使用php版本可能不同&#xff0c;需要安装不同版本php 一、前提条件 1.引入库 需要安装 Xcode 命令行工具和Homebrew xcode-select --install检查brew是否已安…...

地址解析协议ARP

地址解析协议&#xff08;Address Resolution Protocol&#xff0c;ARP&#xff09;&#xff0c;用于根据本网内目的主机或默认网关的IP地址获取其MAC地址。 ARP的基本思想&#xff1a;在每一台主机中设置专用内存区域&#xff0c;称为ARP高速缓存&#xff08;也称为ARP表&…...

Go学习第十三章——Gin入门与路由

Go web框架——Gin入门与路由 1 Gin框架介绍1.1 基础介绍1.2 安装Gin1.3 快速使用 2 路由2.1 基本路由GET请求POST请求 2.2 路由参数2.3 路由分组基本分组带中间件的分组 2.4 重定向 1 Gin框架介绍 github链接&#xff1a;https://github.com/gin-gonic/gin 中文文档&#xf…...

[减脂期食谱] 自制千岛酱

[减脂期食谱] 自制千岛酱 成品如下&#xff1a; 最中间的那个&#xff0c;算比较居中的颜色吧&#xff0c;其实自己家做原版的千岛酱还是比较简单的&#xff0c;它的底就是蛋黄酱(蛋黄油乳化的酱)&#xff0c;随后里面的材料比较自由&#xff0c;维基百科是这么介绍的&#xf…...

Android 系统架构

目录 Android 系统架构 1. Android 应用层 2. Android应用框架层 2.1 Activity Manager &#xff08;活动管理器&#xff09; 2.2 Window Manager &#xff08;窗口管理器&#xff09; 2.3 Content Provider (内容提供器) 2.4 View System&#xff08;视图系统&a…...

【Docker】Python Flask + Redis 练习

一、构建flask镜像 1.准备文件 创建app.py,内容如下 from flask import Flask from redis import Redis app Flask(__name__) redis Redis(hostos.environ.get(REDIS_HOST,127.0.0.1),port6379)app.route(/) def hello():redis.incr(hits)return f"Hello Container W…...

shell_52.Linux测试与其他网络主机的连通性脚本

实战演练 本节将展示一个实用脚本&#xff0c;该脚本在处理用户输入的同时&#xff0c;使用 ping 命令或 ping6 命令来测试与其他网络主机的连通性。 ping 命令或 ping6 命令可以快速测试网络主机是否可用。这个命令很有用&#xff0c;经常作为首选工具。如果只是检查单个主机&…...

OpenCV C++ 图像处理实战 ——《缺陷检测》

OpenCV C++ 图像处理实战 ——《缺陷检测》 一、结果演示二、缺陷检测算法2.1、多元模板图像2.2、训练差异模型三、图像配准3.1 功能源码3.1 功能效果四、多元模板图像4.1 功能源码五、缺陷检测5.1 功能源码六、源码测试图像下载总结一、结果演示...

Python操作MySQL基础使用

Python操作MySQL基础使用 链接数据库并查询数据 import pymysql# 链接数据库 conn pymysql.connect(host10.5.6.250,port3306,userroot,password******** )# 查看MySQL版本信息 print(conn.get_server_info()) # 5.5.27# 获取到游标对象 cursor conn.cursor()# 选择数据库…...

【pytorch】pytorch中的高级索引

这里只介绍pytorch的高级索引&#xff0c;是一些奇怪的切片索引 基本版 a[[0, 2], [1, 2]] 等价 a[0, 1] 和 a[2, 2]&#xff0c;相当于索引张量的第一行的第二列和第三行的第三列元素&#xff1b; a[[1, 0, 2], [0]] 等价 a[1, 0] 和 a[0, 0] 和 a[2, 0]&#xff0c;相当于索…...

基于图像识别的自动驾驶汽车障碍物检测与避障算法研究

基于图像识别的自动驾驶汽车障碍物检测与避障算法研究是一个涉及计算机视觉、机器学习、人工智能和自动控制等多个领域的复杂问题。以下是对这个问题的研究内容和方向的一些概述。 障碍物检测 障碍物检测是自动驾驶汽车避障算法的核心部分&#xff0c;它需要从车辆的感知数据…...

Spring boot定时任务

目录 前言一、使用 Scheduled 注解二、使用 ScheduledExecutorService三、使用 Spring 的 TaskScheduler四、使用第三方调度框架 前言 在 Spring Boot 中&#xff0c;有多种方法来编写定时任务&#xff0c;以执行周期性或延迟执行的任务。下面是几种常见的方式 一、使用 Sche…...

Glide原理

本文基于Carson整理 1.简介 相比其他几种图片加载框架&#xff0c;Glide性能最好。这得益于其高效的图片缓存策略 其还有多样化的媒体格式加载&#xff1a;如GIF、Video&#xff0c;对于商城首页需展示丰富样式、信息的页面需求来说&#xff0c;也是必不可少的。 2.加载原理…...

wps表格按分隔符拆分单元格

有数据如下&#xff1b;看选中区域&#xff0c;一个单元格中有一个v&#xff0c;空格&#xff0c;然后有三个数值&#xff0c;以空格分开&#xff1b;点击菜单中的数据-分列&#xff1b; 弹出分列向导&#xff1b;选择 分隔符号&#xff1b; 选择分隔符为空格&#xff1b;出现预…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...