Android Glide 框架线程管理模块原理的源码级别深入分析
一、引言
在现代的 Android 应用开发中,图片加载是一个常见且重要的功能。Glide 作为一款广泛使用的图片加载框架,以其高效、灵活和易用的特点受到了开发者的青睐。其中,线程管理模块是 Glide 框架中至关重要的一部分,它负责协调不同线程之间的工作,确保图片的加载、解码、处理等操作能够高效、有序地进行。合理的线程管理可以提高应用的性能,避免主线程阻塞,从而为用户提供流畅的交互体验。
本文将深入 Glide 框架的源码,详细剖析其线程管理模块的原理。从线程池的创建和配置,到不同任务在各个线程之间的调度和执行,每一个步骤都会结合具体的代码进行分析。同时,还会探讨线程管理模块与 Glide 其他模块之间的协作关系,以及如何在实际开发中合理利用线程管理来优化图片加载性能。
二、线程管理模块概述
Glide 的线程管理模块主要负责以下几个方面的工作:
- 线程池的创建和管理:Glide 使用多个线程池来处理不同类型的任务,如网络请求、磁盘缓存读写、图片解码等。通过合理配置线程池的参数,可以提高任务的执行效率。
- 任务的调度和执行:根据任务的类型和优先级,将任务分配到合适的线程池中执行。同时,处理任务的排队和并发控制,确保系统资源的合理利用。
- 线程间的通信和同步:在不同线程之间传递数据和状态信息,保证各个模块之间的协作和数据的一致性。例如,在图片加载完成后,将结果从子线程传递到主线程进行显示。
三、线程池的创建和配置
3.1 线程池的种类
Glide 中主要使用了以下几种线程池:
- DiskCacheService:用于处理磁盘缓存的读写操作。磁盘 I/O 操作通常比较耗时,使用单独的线程池可以避免阻塞其他任务。
- SourceService:用于处理网络请求和图片解码等操作。这些操作可能会消耗大量的 CPU 和网络资源,使用专门的线程池可以提高处理效率。
- AnimationExecutor:用于处理动画相关的任务。动画需要在主线程或特定的线程中执行,以保证动画的流畅性。
3.2 线程池的创建和配置源码分析
3.2.1 DiskCacheService 线程池
DiskCacheService 是一个单线程的线程池,用于处理磁盘缓存的读写操作。以下是其创建和配置的源码:
java
// GlideExecutor 类中创建 DiskCacheService 线程池的方法
private static GlideExecutor newDiskCacheExecutor() {// 线程池的核心线程数为 1,即单线程int corePoolSize = 1;// 线程池的最大线程数也为 1int maximumPoolSize = 1;// 线程空闲时的存活时间为 0 毫秒long keepAliveTime = 0L;// 时间单位为毫秒TimeUnit unit = TimeUnit.MILLISECONDS;// 使用 LinkedBlockingQueue 作为任务队列BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();// 创建一个线程工厂,用于创建线程ThreadFactory threadFactory = new DefaultThreadFactory("disk-cache", Process.THREAD_PRIORITY_BACKGROUND);// 创建一个线程池执行器ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory);// 将线程池执行器包装成 GlideExecutor 对象并返回return new GlideExecutor(executor, /*isShutdownAllowed=*/ true);
}
在上述代码中,newDiskCacheExecutor 方法创建了一个单线程的线程池。核心线程数和最大线程数都设置为 1,确保只有一个线程在处理磁盘缓存的读写操作。使用 LinkedBlockingQueue 作为任务队列,保证任务按顺序执行。线程空闲时的存活时间为 0 毫秒,即线程在空闲时会立即终止。最后,将线程池执行器包装成 GlideExecutor 对象返回。
3.2.2 SourceService 线程池
SourceService 是一个多线程的线程池,用于处理网络请求和图片解码等操作。以下是其创建和配置的源码:
java
// GlideExecutor 类中创建 SourceService 线程池的方法
private static GlideExecutor newSourceExecutor() {// 获取可用的处理器核心数int availableProcessors = Runtime.getRuntime().availableProcessors();// 线程池的核心线程数为可用处理器核心数的一半int corePoolSize = Math.max(1, availableProcessors / 2);// 线程池的最大线程数为可用处理器核心数int maximumPoolSize = availableProcessors;// 线程空闲时的存活时间为 60 秒long keepAliveTime = 60L;// 时间单位为秒TimeUnit unit = TimeUnit.SECONDS;// 使用 SynchronousQueue 作为任务队列BlockingQueue<Runnable> workQueue = new SynchronousQueue<>();// 创建一个线程工厂,用于创建线程ThreadFactory threadFactory = new DefaultThreadFactory("source", Process.THREAD_PRIORITY_BACKGROUND);// 创建一个线程池执行器ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory);// 将线程池执行器包装成 GlideExecutor 对象并返回return new GlideExecutor(executor, /*isShutdownAllowed=*/ true);
}
在上述代码中,newSourceExecutor 方法创建了一个多线程的线程池。核心线程数为可用处理器核心数的一半,最大线程数为可用处理器核心数,这样可以充分利用系统资源。使用 SynchronousQueue 作为任务队列,该队列不存储任务,而是直接将任务交给线程处理,提高了任务的执行效率。线程空闲时的存活时间为 60 秒,当线程空闲超过 60 秒时会被终止。最后,将线程池执行器包装成 GlideExecutor 对象返回。
3.2.3 AnimationExecutor 线程池
AnimationExecutor 用于处理动画相关的任务。以下是其创建和配置的源码:
java
// GlideExecutor 类中创建 AnimationExecutor 线程池的方法
private static GlideExecutor newAnimationExecutor() {// 线程池的核心线程数为 1int corePoolSize = 1;// 线程池的最大线程数为 1int maximumPoolSize = 1;// 线程空闲时的存活时间为 0 毫秒long keepAliveTime = 0L;// 时间单位为毫秒TimeUnit unit = TimeUnit.MILLISECONDS;// 使用 LinkedBlockingQueue 作为任务队列BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();// 创建一个线程工厂,用于创建线程ThreadFactory threadFactory = new DefaultThreadFactory("animation", Process.THREAD_PRIORITY_DISPLAY);// 创建一个线程池执行器ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory);// 将线程池执行器包装成 GlideExecutor 对象并返回return new GlideExecutor(executor, /*isShutdownAllowed=*/ true);
}
在上述代码中,newAnimationExecutor 方法创建了一个单线程的线程池。核心线程数和最大线程数都设置为 1,确保动画任务按顺序执行。使用 LinkedBlockingQueue 作为任务队列,保证任务的顺序性。线程空闲时的存活时间为 0 毫秒,即线程在空闲时会立即终止。最后,将线程池执行器包装成 GlideExecutor 对象返回。
3.3 线程池的获取和使用
在 Glide 中,可以通过 GlideExecutor 类的静态方法获取不同的线程池。以下是获取 DiskCacheService 线程池的示例代码:
java
// 获取 DiskCacheService 线程池
GlideExecutor diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
// 提交一个任务到 DiskCacheService 线程池
diskCacheExecutor.execute(new Runnable() {@Overridepublic void run() {// 执行磁盘缓存的读写操作// ...}
});
通过调用 GlideExecutor 类的 newDiskCacheExecutor 方法可以获取 DiskCacheService 线程池,然后使用 execute 方法提交一个任务到该线程池。其他线程池的获取和使用方式类似。
四、任务的调度和执行
4.1 任务的分类
在 Glide 中,任务主要分为以下几类:
- 磁盘缓存读写任务:如从磁盘缓存中读取图片数据或将图片数据写入磁盘缓存。
- 网络请求任务:从网络获取图片数据。
- 图片解码任务:将图片数据解码为
Bitmap或其他可显示的格式。 - 动画任务:处理图片的动画效果。
4.2 任务调度的源码分析
Glide 通过 EngineJob 类来管理任务的调度和执行。以下是 EngineJob 类中任务调度的部分源码:
java
// EngineJob 类负责管理任务的调度和执行
public class EngineJob<R> implements DecodeJob.Callback<R> {private final GlideExecutor diskCacheExecutor; // 磁盘缓存线程池private final GlideExecutor sourceExecutor; // 源数据线程池private final GlideExecutor animationExecutor; // 动画线程池private DecodeJob<R> decodeJob; // 解码任务public EngineJob(GlideExecutor diskCacheExecutor,GlideExecutor sourceExecutor,GlideExecutor animationExecutor) {this.diskCacheExecutor = diskCacheExecutor;this.sourceExecutor = sourceExecutor;this.animationExecutor = animationExecutor;}// 开始执行任务public void start(DecodeJob<R> decodeJob) {this.decodeJob = decodeJob;// 将解码任务提交到磁盘缓存线程池执行diskCacheExecutor.execute(decodeJob);}@Overridepublic void onResourceDecoded(Resource<R> resource, DataSource dataSource) {// 当资源解码完成后,根据数据源类型选择合适的线程池执行后续任务if (dataSource == DataSource.DATA_DISK_CACHE) {// 如果数据来自磁盘缓存,将任务提交到源数据线程池执行sourceExecutor.execute(new ResourceReadyCallback(resource, dataSource));} else {// 否则,直接在当前线程执行后续任务handleResultOnMainThread(resource, dataSource);}}// 在主线程处理结果private void handleResultOnMainThread(Resource<R> resource, DataSource dataSource) {// ...}// 资源准备好的回调任务private class ResourceReadyCallback implements Runnable {private final Resource<R> resource;private final DataSource dataSource;public ResourceReadyCallback(Resource<R> resource, DataSource dataSource) {this.resource = resource;this.dataSource = dataSource;}@Overridepublic void run() {// 处理资源准备好的逻辑handleResultOnMainThread(resource, dataSource);}}
}
在上述代码中,EngineJob 类包含了三个线程池:diskCacheExecutor、sourceExecutor 和 animationExecutor。在 start 方法中,将解码任务 decodeJob 提交到 diskCacheExecutor 线程池执行。当资源解码完成后,在 onResourceDecoded 方法中,根据数据源类型选择合适的线程池执行后续任务。如果数据来自磁盘缓存,将任务提交到 sourceExecutor 线程池执行;否则,直接在当前线程执行后续任务。
4.3 任务执行的源码分析
任务的执行主要通过 Runnable 接口实现。以 DecodeJob 类为例,以下是其实现 Runnable 接口的部分源码:
java
// DecodeJob 类实现了 Runnable 接口,用于执行解码任务
public class DecodeJob<R> implements Runnable {private final EngineJob<R> engineJob; // 引擎任务private final DataFetcherGenerator generator; // 数据获取生成器public DecodeJob(EngineJob<R> engineJob, DataFetcherGenerator generator) {this.engineJob = engineJob;this.generator = generator;}@Overridepublic void run() {try {// 执行解码任务boolean isResourceDecoded = decodeFromSource();if (isResourceDecoded) {// 如果资源解码成功,通知引擎任务engineJob.onResourceDecoded(resource, dataSource);} else {// 如果资源解码失败,通知引擎任务engineJob.onLoadFailed(new GlideException("Failed to decode resource"));}} catch (Exception e) {// 处理异常情况,通知引擎任务engineJob.onLoadFailed(e);}}// 从源数据解码资源private boolean decodeFromSource() throws Exception {// ...return false;}
}
在上述代码中,DecodeJob 类实现了 Runnable 接口的 run 方法。在 run 方法中,调用 decodeFromSource 方法执行解码任务。如果资源解码成功,调用 engineJob 的 onResourceDecoded 方法通知引擎任务;如果解码失败,调用 engineJob 的 onLoadFailed 方法通知引擎任务。
五、线程间的通信和同步
5.1 线程间通信的方式
在 Glide 中,线程间的通信主要通过以下几种方式实现:
- Handler 机制:用于在子线程和主线程之间传递消息。例如,在图片加载完成后,将结果从子线程传递到主线程进行显示。
- 回调接口:通过定义回调接口,在不同线程之间传递数据和状态信息。例如,在
DecodeJob完成解码任务后,通过回调接口通知EngineJob。
5.2 Handler 机制的源码分析
Glide 使用 Handler 机制在子线程和主线程之间传递消息。以下是相关的源码分析:
java
// MainThreadExecutor 类用于在主线程执行任务
public class MainThreadExecutor implements Executor {private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());@Overridepublic void execute(Runnable command) {// 将任务提交到主线程的消息队列中执行MAIN_THREAD_HANDLER.post(command);}
}
在上述代码中,MainThreadExecutor 类实现了 Executor 接口,用于在主线程执行任务。通过 Handler 的 post 方法将任务提交到主线程的消息队列中执行。以下是在 EngineJob 类中使用 MainThreadExecutor 的示例代码:
java
// EngineJob 类中在主线程处理结果的方法
private void handleResultOnMainThread(Resource<R> resource, DataSource dataSource) {// 获取主线程执行器MainThreadExecutor mainThreadExecutor = new MainThreadExecutor();// 将任务提交到主线程执行mainThreadExecutor.execute(new Runnable() {@Overridepublic void run() {// 在主线程处理资源结果// ...}});
}
在 handleResultOnMainThread 方法中,创建了一个 MainThreadExecutor 对象,然后使用 execute 方法将任务提交到主线程执行。
5.3 回调接口的源码分析
回调接口是 Glide 中常用的线程间通信方式。以 DecodeJob 和 EngineJob 之间的通信为例,以下是相关的源码分析:
java
// DecodeJob 类的回调接口
public interface Callback<R> {// 资源解码完成的回调方法void onResourceDecoded(Resource<R> resource, DataSource dataSource);// 加载失败的回调方法void onLoadFailed(GlideException e);
}// DecodeJob 类
public class DecodeJob<R> implements Runnable {private final EngineJob<R> engineJob;public DecodeJob(EngineJob<R> engineJob) {this.engineJob = engineJob;}@Overridepublic void run() {try {// 执行解码任务boolean isResourceDecoded = decodeFromSource();if (isResourceDecoded) {// 如果资源解码成功,通知引擎任务engineJob.onResourceDecoded(resource, dataSource);} else {// 如果资源解码失败,通知引擎任务engineJob.onLoadFailed(new GlideException("Failed to decode resource"));}} catch (Exception e) {// 处理异常情况,通知引擎任务engineJob.onLoadFailed(e);}}
}// EngineJob 类实现了 DecodeJob 的回调接口
public class EngineJob<R> implements DecodeJob.Callback<R> {@Overridepublic void onResourceDecoded(Resource<R> resource, DataSource dataSource) {// 处理资源解码完成的逻辑// ...}@Overridepublic void onLoadFailed(GlideException e) {// 处理加载失败的逻辑// ...}
}
在上述代码中,DecodeJob 类定义了一个回调接口 Callback,包含了 onResourceDecoded 和 onLoadFailed 两个方法。EngineJob 类实现了该回调接口,当 DecodeJob 完成解码任务后,会调用 EngineJob 的相应回调方法,从而实现了线程间的通信。
六、线程管理模块与其他模块的协作
6.1 与缓存模块的协作
线程管理模块与缓存模块密切协作,确保磁盘缓存的读写操作在合适的线程中执行。例如,在 DecodeJob 中,如果需要从磁盘缓存中读取图片数据,会将该任务提交到 DiskCacheService 线程池执行。以下是相关的源码分析:
java
// DecodeJob 类中从磁盘缓存读取数据的方法
private boolean decodeFromCache() {// 创建一个磁盘缓存读取任务DiskCacheGenerator generator = new DiskCacheGenerator(this,diskCache,diskCacheService);// 将任务提交到磁盘缓存线程池执行diskCacheService.execute(generator);return false;
}
在上述代码中,decodeFromCache 方法创建了一个 DiskCacheGenerator 对象,该对象负责从磁盘缓存中读取数据。然后将该任务提交到 diskCacheService 线程池执行。
6.2 与网络模块的协作
线程管理模块与网络模块协作,确保网络请求任务在合适的线程中执行。例如,在 SourceGenerator 中,如果需要从网络获取图片数据,会将该任务提交到 SourceService 线程池执行。以下是相关的源码分析:
java
// SourceGenerator 类中从网络获取数据的方法
private boolean startNext() {// 创建一个网络数据获取器HttpUrlFetcher fetcher = new HttpUrlFetcher(url, timeout);// 将任务提交到源数据线程池执行sourceExecutor.execute(new Runnable() {@Overridepublic void run() {try {// 执行网络请求InputStream inputStream = fetcher.loadData(priority, this);// 处理网络请求结果// ...} catch (IOException e) {// 处理网络请求异常// ...}}});return true;
}
在上述代码中,startNext 方法创建了一个 HttpUrlFetcher 对象,该对象负责从网络获取图片数据。然后将该任务提交到 sourceExecutor 线程池执行。
6.3 与动画模块的协作
线程管理模块与动画模块协作,确保动画任务在合适的线程中执行。例如,在 AnimatableDrawable 中,如果需要执行动画效果,会将该任务提交到 AnimationExecutor 线程池执行。以下是相关的源码分析:
java
// AnimatableDrawable 类中执行动画的方法
public void start() {// 创建一个动画任务AnimationRunnable animationRunnable = new AnimationRunnable(this);// 将任务提交到动画线程池执行animationExecutor.execute(animationRunnable);
}// 动画任务类
private class AnimationRunnable implements Runnable {private final AnimatableDrawable drawable;public AnimationRunnable(AnimatableDrawable drawable) {this.drawable = drawable;}@Overridepublic void run() {// 执行动画逻辑// ...}
}
在上述代码中,start 方法创建了一个 AnimationRunnable 对象,该对象负责执行动画逻辑。然后将该任务提交到 animationExecutor 线程池执行。
七、线程管理模块的性能优化
7.1 合理配置线程池参数
合理配置线程池的参数可以提高任务的执行效率。例如,根据系统的处理器核心数和任务的类型,调整线程池的核心线程数和最大线程数。在 SourceService 线程池中,核心线程数设置为可用处理器核心数的一半,最大线程数设置为可用处理器核心数,这样可以充分利用系统资源。
7.2 任务的优先级管理
Glide 可以通过设置任务的优先级来管理任务的执行顺序。例如,在 Priority 枚举中定义了不同的优先级:
java
// Priority 枚举定义了任务的优先级
public enum Priority {IMMEDIATE, // 立即执行HIGH, // 高优先级NORMAL, // 正常优先级LOW // 低优先级
}
在提交任务时,可以指定任务的优先级,线程池会根据优先级来调度任务。例如:
java
// 提交一个高优先级的任务到源数据线程池
sourceExecutor.execute(new Runnable() {@Overridepublic void run() {// 执行高优先级任务// ...}
}, Priority.HIGH);
7.3 避免线程阻塞
在任务执行过程中,要避免线程阻塞,以免影响其他任务的执行。例如,在进行网络请求时,要使用异步方式进行,避免在主线程中执行耗时的网络操作。Glide 在处理网络请求时,会将网络请求任务提交到 SourceService 线程池执行,避免阻塞主线程。
7.4 线程池的复用和关闭
在使用线程池时,要注意线程池的复用和关闭。避免频繁创建和销毁线程池,以减少系统资源的消耗。同时,在应用退出时,要及时关闭线程池,释放资源。例如,在 Glide 类的 shutdown 方法中,会关闭所有的线程池:
java
// Glide 类的 shutdown 方法
public void shutdown() {// 关闭磁盘缓存线程池diskCacheExecutor.shutdown();// 关闭源数据线程池sourceExecutor.shutdown();// 关闭动画线程池animationExecutor.shutdown();
}
八、线程管理模块的异常处理
8.1 任务执行异常的处理
在任务执行过程中,可能会出现各种异常情况,如网络异常、解码异常等。Glide 在任务执行过程中会捕获这些异常,并进行相应的处理。例如,在 DecodeJob 类的 run 方法中,会捕获异常并通知 EngineJob:
java
// DecodeJob 类的 run 方法
@Override
public void run() {try {// 执行解码任务boolean isResourceDecoded = decodeFromSource();if (isResourceDecoded) {// 如果资源解码成功,通知引擎任务engineJob.onResourceDecoded(resource, dataSource);} else {// 如果资源解码失败,通知引擎任务engineJob.onLoadFailed(new GlideException("Failed to decode resource"));}} catch (Exception e) {// 处理异常情况,通知引擎任务engineJob.onLoadFailed(e);}
}
在上述代码中,run 方法捕获了所有异常,并调用 engineJob 的 onLoadFailed 方法通知引擎任务。
8.2 线程池异常的处理
线程池在执行任务时也可能会出现异常,如线程池已满、线程创建失败等。Glide 通过设置线程池的拒绝策略来处理这些异常。例如,在 ThreadPoolExecutor 中,可以设置拒绝策略:
java
// 创建一个线程池执行器,并设置拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,new ThreadPoolExecutor.CallerRunsPolicy() // 使用 CallerRunsPolicy 拒绝策略
);
在上述代码中,使用 CallerRunsPolicy 拒绝策略,当线程池已满时,会将任务返回给调用者线程执行。
九、总结
Glide 的线程管理模块是其高效运行的关键之一。通过合理创建和配置线程池,将不同类型的任务分配到合适的线程池中执行,实现了任务的高效调度和执行。同时,利用线程间的通信和同步机制,确保了各个模块之间的协作和数据的一致性。
线程管理模块与 Glide 的其他模块密切协作,如缓存模块、网络模块和动画模块,共同完成图片的加载、解码和显示等任务。在实际开发中,要合理利用线程管理模块的功能,进行性能优化,
相关文章:
Android Glide 框架线程管理模块原理的源码级别深入分析
一、引言 在现代的 Android 应用开发中,图片加载是一个常见且重要的功能。Glide 作为一款广泛使用的图片加载框架,以其高效、灵活和易用的特点受到了开发者的青睐。其中,线程管理模块是 Glide 框架中至关重要的一部分,它负责协调…...
每天记录一道Java面试题---day32
MySQL索引的数据结构、各自优劣 回答重点 B树:是一个平衡的多叉树,从根节点到每个叶子节点的高度差不超过1,而且同层级的节点间有指针相互连接。在B树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大…...
Vue3 Pinia 符合直觉的Vue.js状态管理库
Pinia 符合直觉的Vue.js状态管理库 什么时候使用Pinia 当两个关系非常远的组件,要传递参数时使用Pinia组件的公共参数使用Pinia...
深度学习与大模型基础-向量
大家好!今天我们来聊聊向量(Vector)。别被这个词吓到,其实向量在我们的生活中无处不在,只是我们没注意罢了。 1. 向量是什么? 简单来说,向量就是有大小和方向的量。比如你从家走到学校&#x…...
【网络编程】完成端口 IOCP
10.11 完成端口 10.11.1 基本概念 完成端口的全称是I/O 完成端口,英文为IOCP(I/O Completion Port) 。IOCP是一个异 步I/O 的 API, 可以高效地将I/O 事件通知给应用程序。与使用select() 或是其他异步方法不同 的是,一个套接字与一个完成端口关联了起来…...
《苍穹外卖》SpringBoot后端开发项目重点知识整理(DAY1 to DAY3)
目录 一、在本地部署并启动Nginx服务1. 解压Nginx压缩包2. 启动Nginx服务3. 验证Nginx是否启动成功: 二、导入接口文档1. 黑马程序员提供的YApi平台2. YApi Pro平台3. 推荐工具:Apifox 三、Swagger1. 常用注解1.1 Api与ApiModel1.2 ApiModelProperty与Ap…...
管理网络安全
防火墙在 Linux 系统安全中有哪些重要的作用? 防火墙作为网络安全的第一道防线,能够根据预设的规则,对进出系统的网络流量进行严格筛选。它可以阻止未经授权的外部访问,只允许符合规则的流量进入系统,从而保护系统免受…...
明日直播|Go IoT 开发平台,开启万物智联新征程
在物联网技术飞速发展的当下,物联网行业却深受协议碎片化、生态封闭、开发低效等难题的困扰。企业和开发者们渴望找到一个能突破这些困境,实现高效、便捷开发的有力工具。 3 月 11 日(星期二)19:00,GitCode 特别邀请独…...
系统架构设计师—系统架构设计篇—软件架构风格
文章目录 概述经典体系结构风格数据流风格批处理管道过滤器对比 调用/返回风格主程序/子程序面向对象架构风格层次架构风格 独立构件风格进程通信事件驱动的系统 虚拟机风格解释器基于规则的系统 仓库风格(数据共享风格)数据库系统黑板系统超文本系统 闭…...
【MySQL_04】数据库基本操作(用户管理--配置文件--远程连接--数据库信息查看、创建、删除)
文章目录 一、MySQL 用户管理1.1 用户管理1.11 mysql.user表详解1.12 添加用户1.13 修改用户权限1.14 删除用户1.15 密码问题 二、MySQL 配置文件2.1 配置文件位置2.2 配置文件结构2.3 常用配置参数 三、MySQL远程连接四、数据库的查看、创建、删除4.1 查看数据库4.2 创建、删除…...
【Zinx】Day5-Part4:Zinx 的连接属性设置
目录 Day5-Part4:Zinx 的连接属性设置给连接添加连接配置的接口连接属性方法的实现 测试 Zinx-v1.0总结 Day5-Part4:Zinx 的连接属性设置 在 Zinx 当中,我们使用 Server 来开启服务并监听指定的端口,当接收到来自客户端的连接请求…...
【英伟达AI论文】多模态大型语言模型的高效长视频理解
摘要:近年来,基于视频的多模态大型语言模型(Video-LLMs)通过将视频处理为图像帧序列,显著提升了视频理解能力。然而,许多现有方法在视觉主干网络中独立处理各帧,缺乏显式的时序建模,…...
小程序事件系统 —— 32 事件系统 - 事件分类以及阻止事件冒泡
在微信小程序中,事件分为 冒泡事件 和 非冒泡事件 : 冒泡事件:当一个组件的事件被触发后,该事件会向父节点传递;(如果父节点中也绑定了一个事件,父节点事件也会被触发,也就是说子组…...
全球首款 5G-A 人形机器人发布
全球首款 5G-A 人形机器人于世界移动通信大会(MWC2025)上由中国移动、华为、乐聚联合发布。以下是关于这款机器人的详细介绍: 名称与背景 名称9:这款人形机器人名为 “夸父”,是中国移动、华为与乐聚机器人在 GTI 平台…...
Tomcat 新手入门指南
Tomcat 新手入门指南 Apache Tomcat 是一个开源的 Java Servlet 容器和 Web 服务器,广泛用于部署和运行 Java Web 应用程序。以下是 Tomcat 的入门指南,帮助你快速上手。 1. 安装 Tomcat 步骤 1: 下载 Tomcat 访问 Apache Tomcat 官网。选择适合的版…...
Flink-DataStreamAPI-生成水印
下面我们将学习Flink提供的用于处理事件时间戳和水印的API,也会介绍有关事件时间、流转时长和摄取时间,下面就让我们跟着官网来学习吧 一、水印策略介绍 为了处理事件时间,Flink需要知道事件时间戳,这意味着流中的每个元素都需要…...
【单片机】ARM 处理器简介
ARM 公司简介 ARM(Advanced RISC Machine) 是英国 ARM 公司(原 Acorn RISC Machine) 开发的一种精简指令集(RISC) 处理器架构。ARM 处理器因其低功耗、高性能、广泛适用性,成为嵌入式系统、移动…...
Flutter——最详细原生交互(MethodChannel、EventChannel、BasicMessageChannel)使用教程
MethodChannel(方法通道) 用途:实现 双向通信,用于调用原生平台提供的 API 并获取返回结果。 场景:适合一次性操作,如调用相机、获取设备信息等。 使用步骤: Flutter 端:通过 Meth…...
Kafka常用指令(详细)
Kafka常用指令(详细) 启停命令 前台启动 前台启动命令 ./bin/kafka-server-start.sh config/server.properties 后台启动方式1 后台启动命令加上参数-daemon,窗口关闭之后kafka后台程序继续运行 ./bin/kafka-server-start.sh -daemon co…...
供应链工作效率如何提升
提升供应链工作效率可以从以下几个关键方面入手: 1. 优化供应链管理 数据驱动决策:利用AI和大数据分析,提高预测准确性,优化库存管理。供应链可视化:采用ERP(企业资源计划)和SCM(供…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...
