图片加载框架Picasso解析

2024-06-02 00:18

本文主要是介绍图片加载框架Picasso解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

picasso是Square公司开源的一个Android图形缓存库

主要有以下一些特性:

在adapter中回收和取消当前的下载;

使用最少的内存完成复杂的图形转换操作;

自动的内存和硬盘缓存;

图形转换操作,如变换大小,旋转等,提供了接口来让用户可以自定义转换操作;

加载载网络或本地资源;

Picasso.class

他有一个内部类,一般是通过他来创建实例的:


downloader(Downloader downloader) :

容许使用自定义的下载器,可以用okhttp或者volley,必须实现Downloader接口。

executor(ExecutorService executorService):

容许使用自己的线程池来进行下载

memoryCache(Cache memoryCache):

容许使用自己的缓存类,必须实现Cache接口。

requestTransformer(RequestTransformer transformer):

listener(Listener listener):

addRequestHandler(RequestHandler requestHandler):

indicatorsEnabled(boolean enabled):设置图片来源的指示器。

loggingEnabled(boolean enabled):

再来看build()方法:

 /** Create the {@link Picasso} instance. */public Picasso build() {Context context = this.context;if (downloader == null) {downloader = Utils.createDefaultDownloader(context);}if (cache == null) {cache = new LruCache(context);}if (service == null) {service = new PicassoExecutorService();}if (transformer == null) {transformer = RequestTransformer.IDENTITY;}Stats stats = new Stats(cache);Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);return new Picasso(context, dispatcher, cache, listener, transformer,requestHandlers, stats, indicatorsEnabled, loggingEnabled);}
这里会使用默认的下载器,缓存类,线程池,并将这些对象封装到了分发器Dispatcher里。然后在返回一个Picasso对象。

一般情况下,如果不需要自定义bulid里的这些方法,可以使用Picasso.with(context)默认方法来获得单例对象:

public static Picasso with(Context context) {if (singleton == null) {synchronized (Picasso.class) {if (singleton == null) {singleton = new Builder(context).build();}}}return singleton;}
如果需要自定义一些对象:

public class SamplePicassoFactory {private static Picasso sPicasso;public static Picasso getPicasso(Context context) {if (sPicasso == null) {sPicasso = new Picasso.Builder(context).downloader(new OkHttpDownloader(context, ConfigConstants.MAX_DISK_CACHE_SIZE)).memoryCache(new LruCache(ConfigConstants.MAX_MEMORY_CACHE_SIZE)).build();}return sPicasso;}
}
通过上面的方法,获得sPicasso对象后就设置成了单例,但是最好设置成双重校验锁模式。

如果通过以上方法获得对象后,还可以通过Picasso.setSingletonInstance(Picasso picasso)方法设置对象到Picasso中,这样以后还是通过Picasso.with(context)来调用。

接着就可以通过以下方式设置加载源:

可以是uri地址,file文件,res资源drawable。

最终都是通过以下方法来创建一个RequestCreator对象:

public RequestCreator load(Uri uri) {return new RequestCreator(this, uri, 0);}
RequestCreator(Picasso picasso, Uri uri, int resourceId) {if (picasso.shutdown) {throw new IllegalStateException("Picasso instance already shut down. Cannot submit new requests.");}this.picasso = picasso;this.data = new Request.Builder(uri, resourceId); }
RequestCreator对象是用来设置一系列属性的,如:


noPlaceholder():设置没有加载等待图片

placeholder(int placeholderResId):设置加载等待图片

placeholder(Drawable placeholderDrawable):设置加载等待图片

error(int errorResId):设置加载出错的图片。

error(Drawable errorDrawable):设置加载出错的图片。

tag(Object tag):设置标记

fit():自适应,下载的图片有多少像素就显示多少像素

resizeDimen(int targetWidthResId, int targetHeightResId):设置图片显示的像素。

resize(int targetWidth, int targetHeight):设置图片显示的像素。

centerCrop():设置ImageView的ScaleType属性.

centerInside():设置ImageView的ScaleType属性.

rotate(float degrees):设置旋转角度。

rotate(float degrees, float pivotX, float pivotY):设置以某个中心点设置某个旋转角度。

config(Bitmap.Config config):设置Bitmap的Config属性

priority(Priority priority):设置请求的优先级。

transform(Transformation transformation):

skipMemoryCache():跳过内存缓存

memoryPolicy(MemoryPolicy policy, MemoryPolicy... additional):

networkPolicy(NetworkPolicy policy, NetworkPolicy... additional):

noFade():没有淡入淡出效果

get():获得bitmap对象

fetch():

设置完以上一系列属性之后,最关键的就是into方法,现在以into(ImageView target)举例:

public void into(ImageView target) {into(target, null);}
他实际调用的是:

 public void into(ImageView target, Callback callback) {long started = System.nanoTime();checkMain();if (target == null) {throw new IllegalArgumentException("Target must not be null.");}if (!data.hasImage()) {picasso.cancelRequest(target);if (setPlaceholder) {setPlaceholder(target, getPlaceholderDrawable());}return;}if (deferred) {if (data.hasSize()) {throw new IllegalStateException("Fit cannot be used with resize.");}int width = target.getWidth();int height = target.getHeight();if (width == 0 || height == 0) {if (setPlaceholder) {setPlaceholder(target, getPlaceholderDrawable());}picasso.defer(target, new DeferredRequestCreator(this, target, callback));return;}data.resize(width, height);}Request request = createRequest(started);String requestKey = createKey(request);if (shouldReadFromMemoryCache(memoryPolicy)) {Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);if (bitmap != null) {picasso.cancelRequest(target);setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);if (picasso.loggingEnabled) {log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);}if (callback != null) {callback.onSuccess();}return;}}if (setPlaceholder) {setPlaceholder(target, getPlaceholderDrawable());}Action action =new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,errorDrawable, requestKey, tag, callback, noFade);picasso.enqueueAndSubmit(action);}
checkMain();

首先检查是否是主线程

if (target == null) {throw new IllegalArgumentException("Target must not be null.");}
检查目标view是否存在

if (!data.hasImage()) {picasso.cancelRequest(target);if (setPlaceholder) {setPlaceholder(target, getPlaceholderDrawable());}return;}
如果没有设置uri或者resDrawable,就停止请求,如果设置了加载等待的图片就设置,然后就return

if (deferred) {if (data.hasSize()) {throw new IllegalStateException("Fit cannot be used with resize.");}int width = target.getWidth();int height = target.getHeight();if (width == 0 || height == 0) {if (setPlaceholder) {setPlaceholder(target, getPlaceholderDrawable());}picasso.defer(target, new DeferredRequestCreator(this, target, callback));return;}data.resize(width, height);}
如果设置fit自适应:如果已经设置了图片像素大小就抛异常,如果目标view的长宽等于0,就在设置等待图片后延期处理,如果不等于0就设置size到Request.Builder的data里。

   Request request = createRequest(started);String requestKey = createKey(request);
接着就创建Request,并生成一个String类型的requestKey。

/** Create the request optionally passing it through the request transformer. */private Request createRequest(long started) {int id = nextId.getAndIncrement();Request request = data.build();request.id = id;request.started = started;boolean loggingEnabled = picasso.loggingEnabled;if (loggingEnabled) {log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());}Request transformed = picasso.transformRequest(request);if (transformed != request) {// If the request was changed, copy over the id and timestamp from the original.transformed.id = id;transformed.started = started;if (loggingEnabled) {log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);}}return transformed;}
在以上代码可以看出,这里调用picasso.transformRequest(request);来给使用者提供一个可以更改request的机会,他最终调用的是Picasso.Build里面的通过requestTransformer(RequestTransformer transformer)方法传进去的RequestTransformer 接口:

 public interface RequestTransformer {/*** Transform a request before it is submitted to be processed.** @return The original request or a new request to replace it. Must not be null.*/Request transformRequest(Request request);/** A {@link RequestTransformer} which returns the original request. */RequestTransformer IDENTITY = new RequestTransformer() {@Override public Request transformRequest(Request request) {return request;}};}
默认使用的是IDENTITY。这里没有做任何的修改,如果有需要可以自己设置接口以达到修改Request的目的。

if (shouldReadFromMemoryCache(memoryPolicy)) {Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);if (bitmap != null) {picasso.cancelRequest(target);setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);if (picasso.loggingEnabled) {log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);}if (callback != null) {callback.onSuccess();}return;}}
接着要判断是否需要从缓存中读取图片,如果需要,就要根据requestKey从缓存中读取:

Bitmap quickMemoryCacheCheck(String key) {Bitmap cached = cache.get(key);if (cached != null) {stats.dispatchCacheHit();} else {stats.dispatchCacheMiss();}return cached;}
这里的cache用的是LruCache内存缓存类,这个内存缓存类,实现了Cache接口,最后调用的是:

 @Override public Bitmap get(String key) {if (key == null) {throw new NullPointerException("key == null");}Bitmap mapValue;synchronized (this) {mapValue = map.get(key);if (mapValue != null) {hitCount++;return mapValue;}missCount++;}return null;}
这里的map是LinkedHashMap<String, Bitmap>:

this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
缓存的策略稍后分析,现在先回到前面,如果从缓存中拿到的bitmap不等于null,就调用picasso.cancelRequest(target)来删除请求,然后通过

setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
设置图片,然后如果设置了callback回调,再回掉callback.onSuccess();方法,然后就return。


如果没有设置内存缓存,或者缓存中的图片已经不存在:

 if (setPlaceholder) {setPlaceholder(target, getPlaceholderDrawable());}Action action =new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,errorDrawable, requestKey, tag, callback, noFade);picasso.enqueueAndSubmit(action);
接着就设置等待加载图片,然后封装一个action,然后将action加入到分发器中:

void enqueueAndSubmit(Action action) {Object target = action.getTarget();if (target != null && targetToAction.get(target) != action) {// This will also check we are on the main thread.cancelExistingRequest(target);targetToAction.put(target, action);}submit(action);}void submit(Action action) {dispatcher.dispatchSubmit(action);}
然后调用了dispatcher的performSubmit()方法:

 void performSubmit(Action action) {performSubmit(action, true);}void performSubmit(Action action, boolean dismissFailed) {if (pausedTags.contains(action.getTag())) {pausedActions.put(action.getTarget(), action);if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),"because tag '" + action.getTag() + "' is paused");}return;}BitmapHunter hunter = hunterMap.get(action.getKey());if (hunter != null) {hunter.attach(action);return;}if (service.isShutdown()) {if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");}return;}hunter = forRequest(action.getPicasso(), this, cache, stats, action);hunter.future = service.submit(hunter);hunterMap.put(action.getKey(), hunter);if (dismissFailed) {failedActions.remove(action.getTarget());}if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());}}
这里通过 hunter = forRequest(action.getPicasso(), this, cache, stats, action);获得一个BitmapHunter对象,接着就提交到线程池中 hunter.future = service.submit(hunter);:

接着线程池就会调用hunter 的run方法,因为这实现了Runnable对象:

 @Override public void run() {try {updateThreadName(data);if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));}result = hunt();if (result == null) {dispatcher.dispatchFailed(this);} else {dispatcher.dispatchComplete(this);}} catch (Downloader.ResponseException e) {if (!e.localCacheOnly || e.responseCode != 504) {exception = e;}dispatcher.dispatchFailed(this);} catch (NetworkRequestHandler.ContentLengthException e) {exception = e;dispatcher.dispatchRetry(this);} catch (IOException e) {exception = e;dispatcher.dispatchRetry(this);} catch (OutOfMemoryError e) {StringWriter writer = new StringWriter();stats.createSnapshot().dump(new PrintWriter(writer));exception = new RuntimeException(writer.toString(), e);dispatcher.dispatchFailed(this);} catch (Exception e) {exception = e;dispatcher.dispatchFailed(this);} finally {Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);}}
接着调用hunt()方法:

Bitmap hunt() throws IOException {Bitmap bitmap = null;if (shouldReadFromMemoryCache(memoryPolicy)) {bitmap = cache.get(key);if (bitmap != null) {stats.dispatchCacheHit();loadedFrom = MEMORY;if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");}return bitmap;}}data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;RequestHandler.Result result = requestHandler.load(data, networkPolicy);if (result != null) {loadedFrom = result.getLoadedFrom();exifRotation = result.getExifOrientation();bitmap = result.getBitmap();// If there was no Bitmap then we need to decode it from the stream.if (bitmap == null) {InputStream is = result.getStream();try {bitmap = decodeStream(is, data);} finally {Utils.closeQuietly(is);}}}if (bitmap != null) {if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_DECODED, data.logId());}stats.dispatchBitmapDecoded(bitmap);if (data.needsTransformation() || exifRotation != 0) {synchronized (DECODE_LOCK) {if (data.needsMatrixTransform() || exifRotation != 0) {bitmap = transformResult(data, bitmap, exifRotation);if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());}}if (data.hasCustomTransformations()) {bitmap = applyCustomTransformations(data.transformations, bitmap);if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");}}}if (bitmap != null) {stats.dispatchBitmapTransformed(bitmap);}}}return bitmap;}
这里如果从内存缓存中渠道bitmap对象,就直接返回了;否则通过requestHandler.load(data, networkPolicy);来发起网络请求(这个是NetworkRequestHandler类型的):

@Override public Result load(Request request, int networkPolicy) throws IOException {Response response = downloader.load(request.uri, request.networkPolicy);if (response == null) {return null;}Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;Bitmap bitmap = response.getBitmap();if (bitmap != null) {return new Result(bitmap, loadedFrom);}InputStream is = response.getInputStream();if (is == null) {return null;}// Sometimes response content length is zero when requests are being replayed. Haven't found// root cause to this but retrying the request seems safe to do so.if (loadedFrom == DISK && response.getContentLength() == 0) {Utils.closeQuietly(is);throw new ContentLengthException("Received response with 0 content-length header.");}if (loadedFrom == NETWORK && response.getContentLength() > 0) {stats.dispatchDownloadFinished(response.getContentLength());}return new Result(is, loadedFrom);}
这里调用了downloader.load(request.uri, request.networkPolicy)方法,这是一个UrlConnectionDownloader类型的对象,调用的是UrlConnectionDownloader的load()方法:

 @Override public Response load(Uri uri, int networkPolicy) throws IOException {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {installCacheIfNeeded(context);}HttpURLConnection connection = openConnection(uri);connection.setUseCaches(true);if (networkPolicy != 0) {String headerValue;if (NetworkPolicy.isOfflineOnly(networkPolicy)) {headerValue = FORCE_CACHE;} else {StringBuilder builder = CACHE_HEADER_BUILDER.get();builder.setLength(0);if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {builder.append("no-cache");}if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {if (builder.length() > 0) {builder.append(',');}builder.append("no-store");}headerValue = builder.toString();}connection.setRequestProperty("Cache-Control", headerValue);}int responseCode = connection.getResponseCode();if (responseCode >= 300) {connection.disconnect();throw new ResponseException(responseCode + " " + connection.getResponseMessage(),networkPolicy, responseCode);}long contentLength = connection.getHeaderFieldInt("Content-Length", -1);boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));return new Response(connection.getInputStream(), fromCache, contentLength);}
如果系统版本是4.0以上,就使用installCacheIfNeeded(context)来启动data\data\包名\cacha下的某个目录的磁盘缓存:

 private static void installCacheIfNeeded(Context context) {// DCL + volatile should be safe after Java 5.if (cache == null) {try {synchronized (lock) {if (cache == null) {cache = ResponseCacheIcs.install(context);}}} catch (IOException ignored) {}}}private static class ResponseCacheIcs {static Object install(Context context) throws IOException {File cacheDir = Utils.createDefaultCacheDir(context);HttpResponseCache cache = HttpResponseCache.getInstalled();if (cache == null) {long maxSize = Utils.calculateDiskCacheSize(cacheDir);cache = HttpResponseCache.install(cacheDir, maxSize);}return cache;}
但是如果要是用这个磁盘缓存,就要在HttpURLConnection的响应头上加上缓存的控制头"Cache-Control"!

最后返回new Response(connection.getInputStream(), fromCache, contentLength)请求结果。

接着回到上面的hunt()方法的流程继续:

 if (result != null) {loadedFrom = result.getLoadedFrom();exifRotation = result.getExifOrientation();bitmap = result.getBitmap();// If there was no Bitmap then we need to decode it from the stream.if (bitmap == null) {InputStream is = result.getStream();try {bitmap = decodeStream(is, data);} finally {Utils.closeQuietly(is);}}}if (bitmap != null) {if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_DECODED, data.logId());}stats.dispatchBitmapDecoded(bitmap);if (data.needsTransformation() || exifRotation != 0) {synchronized (DECODE_LOCK) {if (data.needsMatrixTransform() || exifRotation != 0) {bitmap = transformResult(data, bitmap, exifRotation);if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());}}if (data.hasCustomTransformations()) {bitmap = applyCustomTransformations(data.transformations, bitmap);if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");}}}if (bitmap != null) {stats.dispatchBitmapTransformed(bitmap);}}}return bitmap;
然后bitmap返回到run()方法里面的result应用上:

 if (result == null) {dispatcher.dispatchFailed(this);} else {dispatcher.dispatchComplete(this);}
如果result有结果就分发完成消息,最后将会调用到Picasso里面:

 static final Handler HANDLER = new Handler(Looper.getMainLooper()) {@Override public void handleMessage(Message msg) {switch (msg.what) {case HUNTER_BATCH_COMPLETE: {@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;//noinspection ForLoopReplaceableByForEachfor (int i = 0, n = batch.size(); i < n; i++) {BitmapHunter hunter = batch.get(i);hunter.picasso.complete(hunter);}break;}
 void complete(BitmapHunter hunter) {Action single = hunter.getAction();List<Action> joined = hunter.getActions();boolean hasMultiple = joined != null && !joined.isEmpty();boolean shouldDeliver = single != null || hasMultiple;if (!shouldDeliver) {return;}Uri uri = hunter.getData().uri;Exception exception = hunter.getException();Bitmap result = hunter.getResult();LoadedFrom from = hunter.getLoadedFrom();if (single != null) {deliverAction(result, from, single);}if (hasMultiple) {//noinspection ForLoopReplaceableByForEachfor (int i = 0, n = joined.size(); i < n; i++) {Action join = joined.get(i);deliverAction(result, from, join);}}if (listener != null && exception != null) {listener.onImageLoadFailed(this, uri, exception);}}
接着分发action:

private void deliverAction(Bitmap result, LoadedFrom from, Action action) {if (action.isCancelled()) {return;}if (!action.willReplay()) {targetToAction.remove(action.getTarget());}if (result != null) {if (from == null) {throw new AssertionError("LoadedFrom cannot be null.");}action.complete(result, from);if (loggingEnabled) {log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);}} else {action.error();if (loggingEnabled) {log(OWNER_MAIN, VERB_ERRORED, action.request.logId());}}}
然后调用的是ImageViewAction里面的action.complete(result, from)方法:

 @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {if (result == null) {throw new AssertionError(String.format("Attempted to complete action with no result!\n%s", this));}ImageView target = this.target.get();if (target == null) {return;}Context context = picasso.context;boolean indicatorsEnabled = picasso.indicatorsEnabled;PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);if (callback != null) {callback.onSuccess();}}

最后一步通过PicassoDrawable.setBitmap来设置,就算大功完成了。

附上部分流程图:









这篇关于图片加载框架Picasso解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

数据治理框架-ISO数据治理标准

引言 "数据治理"并不是一个新的概念,国内外有很多组织专注于数据治理理论和实践的研究。目前国际上,主要的数据治理框架有ISO数据治理标准、GDI数据治理框架、DAMA数据治理管理框架等。 ISO数据治理标准 改标准阐述了数据治理的标准、基本原则和数据治理模型,是一套完整的数据治理方法论。 ISO/IEC 38505标准的数据治理方法论的核心内容如下: 数据治理的目标:促进组织高效、合理地

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。