Picasso源码分析

2023-11-01 10:59
文章标签 分析 源码 picasso

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

概述

前面分析了Volley的源码,现在来看一下Picasso的源码,其实Volley已经具备了加载了网络图片的功能,只是性能不是很好,Picasso是Square公司推出的一款图片加载框架,只能加载图片,所以性能肯定会比Volley好,Picasso的很多设计实际上跟Volley很相似,后来看到Picasso中的一个类BitmapHunter的注释,发现其实Picasso的作者也是参考了Volley的一些设计的。


Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since this will only ever happen in background threads we help avoid excessive memory thrashing as well as potential OOMs. Shamelessly stolen from Volley.

当时看到注释的最后一句就笑了,牛逼的框架也都是一步一步完善起来的,不过很佩服Picasso的作者,还在注释中说出来,也是没谁了,下面总结一下Picasso的设计原理。

Picasso

Picasso大致的可以分这几大块,其实也比较好理解,如果我们只是加载一张图片,那么很自然的可以连工具类都不用写,用HttpUrlConnection或者用OkhttoUrlConnection去读流,然后进行转码,适当的比例缩放就可以加载一张图片,框架无非就是为了提高代码的复用性以及加载速度,加载性能,不然也不会有人去写去用,对于Bitmap,有一点需要注意的就是OOM,然后结合以上节点,就是大部分框架需要考虑的东西。

正文

加载流程

先来看Picasso是如何调用的


Picasso.get()//获取Picasso单例.load("url")//RequestCreator配置url,返回RequestCreator.placeholder(R.mipmap.ic_launcher)//RequestCreator配置占位图,返回RequestCreator.error(R.mipmap.ic_launcher)//RequestCreator配置失败的占位图,返回RequestCreator.fit()//自动缩放图片,返回RequestCreator.into(new ImageView(this));//开始加载图片

这个是在Github上面的最新的demo里面,已经用get代替了之前的with方法这里并没有去画流程图或者类图,感觉太大必要,因为自己写过简单的图片加载框架,前面也分析过Volley的源码,整体的流程都是大同小异的,对于Picasso而言,依然是先配置一些基本信息,缓存策略,占位图等,然后就开始根据缓存策略进行数据读取,所以,不再赘述,打算就Picasso的一些核心类进行重点分析。

get方法


static volatile Picasso singleton = null;
public static Picasso get() {if (singleton == null) {synchronized (Picasso.class) {http:if (singleton == null) {if (PicassoProvider.context == null) {throw new IllegalStateException("context == null");}singleton = new Builder(PicassoProvider.context).build();}}}return singleton;
}

由于此方法是会在多线程中经常被调用的,所以为了保证线程安全,采用了双重检查,其实我们也可以用静态内部类的形式来替代如下:


private static class NestedPicasso {public static Picasso singleton;static {System.out.println("instance = new SingletonTest()");if (PicassoProvider.context == null) {throw new IllegalStateException("context == null");}singleton = new Builder(PicassoProvider.context).build();}
}
public static Picasso get() {return NestedPicasso.singleton;
}

into


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) {//fit操作跟resize不能同时存在,这个也很好理解,//要么根据ImageView自己的尺寸要么根据我传入的尺寸,鱼跟熊掌不可兼得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 || target.isLayoutRequested()) {//如果ImageView的宽高有一个为0,也就是wrap的情况那么就必须延迟加载if (setPlaceholder) {//设置占位图setPlaceholder(target, getPlaceholderDrawable());}// 在DeferredRequestCreator重新测量picasso.defer(target, new DeferredRequestCreator(this, target, callback));return;}//对重新设置ImageView的尺寸data.resize(width, height);}Request request = createRequest(started);//创建一个请求String requestKey = createKey(request);//创建缓存的keyif (shouldReadFromMemoryCache(memoryPolicy)) {//如果需要从缓存中读取,则查找内存缓存Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);if (bitmap != null) {//获取到缓存,取消掉请求picasso.cancelRequest(target);//设置Bitmap给ImageViewsetBitmap(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());}
//创建ImageViewActionAction action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,errorDrawable, requestKey, tag, callback, noFade);
//Action入队picasso.enqueueAndSubmit(action);
}

enqueueAndSubmit之后经过一系列调用,最终调用了Diapatcher的performSubmit

performSubmit


void performSubmit(Action action, boolean dismissFailed) {//如果之前是pause过,那么调用resumeif (pausedTags.contains(action.getTag())) {pausedActions.put(action.getTarget(), action);return;}//通过Key获取BitmapHunterBitmapHunter hunter = hunterMap.get(action.getKey());if (hunter != null) {//将action添加进BitmapHunterhunter.attach(action);return;}if (service.isShutdown()) {//如果线程池关闭,直接返回if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");}return;}//创建一个BitmapHunterhunter = forRequest(action.getPicasso(), this, cache, stats, action);//拿到线程池执行的结果,BitmapHunter肯定是个Runnable或者Futurehunter.future = service.submit(hunter);//存入map中hunterMap.put(action.getKey(), hunter);if (dismissFailed) {failedActions.remove(action.getTarget());}if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());}
}

我们知道把BitmapHunter放进了线程池,而BitmapHunter是个Runnable,那么所有的耗时操作肯定是放在run方法,追踪之后执行的是hunt方法,那么就来看一下hunt的具体流程

hunt


Bitmap hunt() throws IOException {Bitmap bitmap = null;if (shouldReadFromMemoryCache(memoryPolicy)) {//再次读取内存缓存,因为不同的请求优先级不一样,很可能等到真正执行的时候//当前的url已经被缓存了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;}}networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;//如果没有读取缓存或者缓存读取失败,就开始真正的请求RequestHandler.Result result = requestHandler.load(data, networkPolicy);if (result != null) {loadedFrom = result.getLoadedFrom();exifOrientation = result.getExifOrientation();bitmap = result.getBitmap();// If there was no Bitmap then we need to decode it from the stream.if (bitmap == null) {Source source = result.getSource();try {bitmap = decodeStream(source, data);} finally {try {source.close();} catch (IOException ignored) {}}}}if (bitmap != null) {if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_DECODED, data.logId());}stats.dispatchBitmapDecoded(bitmap);//看看是否需要进行变换前预处理,有的话就处理if (data.needsTransformation() || exifOrientation != 0) {synchronized (DECODE_LOCK) {//通过上锁,每次只有一个进行Decodeingif (data.needsMatrixTransform() || exifOrientation != 0) {bitmap = transformResult(data, bitmap, exifOrientation);if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());}}if (data.hasCustomTransformations()) {bitmap = applyCustomTransformations(data.transformations, bitmap);}}if (bitmap != null) {stats.dispatchBitmapTransformed(bitmap);}}}return bitmap;
}

接下来的就是将Bitmap回传给ImageView了,还是在Dispatcher中,调用了performRequest方法


void performComplete(BitmapHunter hunter) {if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {cache.set(hunter.getKey(), hunter.getResult());}hunterMap.remove(hunter.getKey());batch(hunter);//追踪Bitmap}

拿到数据,在这里还是在工作线程里面,由于需要在主线程进行显示ImageView,所以需要切换线程,这里用到了 初始化的Handler,也就是通过MainLooper初始化

batch


private void batch(BitmapHunter hunter) {if (hunter.isCancelled()) {return;}if (hunter.result != null) {hunter.result.prepareToDraw();}batch.add(hunter);if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {//切换线程handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);}
}

主线程拿到传递过来的消息之后,经过中转,最后还是到了ImageViewAction中,最终调用了complete方法

complete


@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;//设置BitmapPicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);if (callback != null) {callback.onSuccess();}
}

继续看setBitmap

setBitmap


static void setBitmap(ImageView target, Context context, Bitmap bitmap,Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {Drawable placeholder = target.getDrawable();if (placeholder instanceof Animatable) {((Animatable) placeholder).stop();}PicassoDrawable drawable =new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);//最终设置的是一个BitmapDrawabletarget.setImageDrawable(drawable);
}

所以最终设置给ImageView的不是Bitmap,而是一个BitmapDrawable,我们看一下setImagBitmap的源码


public void setImageBitmap(Bitmap bm) {// Hacky fix to force setImageDrawable to do a full setImageDrawable// instead of doing an object reference comparisonmDrawable = null;if (mRecycleableBitmapDrawable == null) {mRecycleableBitmapDrawable = new BitmapDrawable(mContext.getResources(), bm);} else {mRecycleableBitmapDrawable.setBitmap(bm);}//也是调用了setImageDrawablesetImageDrawable(mRecycleableBitmapDrawable);
}

之所以设置的是BitmapDrawable,有两个原因,一个是为了展示加载的动画,另一个是在debug模式下面可以绘制右上角的三角调试符号,这些都是在初始化BitmapDrawable的时候加进去的。

通过追踪源码的方式,可以了解到Picasso整体加载流程,有很多细节没有分析到,不过我们已经知道了,在分析过程中涉及到了很多类,其中有几个类是反复出现的,Picasso,RequestCreator,Dispatcher,RequestHandler,Action,另外还有一些类,比如说PicassoDrawable,LruCache,OkHttp3Downloader等,下面重点分析一下。

Picasso

成员变量


//清理线程,主要是用来清理ImageView被回收了的Request
private final CleanupThread cleanupThread;
//存放RequestHandler的集合
private final List<RequestHandler> requestHandlers;
//分发的一个关键类,主要用来转发各种请求结果
final Dispatcher dispatcher;
//缓存
final Cache cache;
//存放不同Action的Map
final Map<Object, Action> targetToAction;
//存放延迟加载的RequestCreator,也就是fit状态下获取的宽或者为高的ImageView
final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;
//引用队列,每隔一段时间获取被回收的target
final ReferenceQueue<Object> referenceQueue;

Picasso这个类持有了成员变量requestHandler集合,Action集合,Dispatcher,Cache这几个前面提到的核心类,所以它自己实际上没有什么特殊的功能,只是在间接的调用了这些核心类的功能,实际上是一个装饰者模式,暴露给外部调用者调用,这也是为什么Picasso使用起来很简洁,原因就在于Picasso在这个类,封装了所有的方法。

构造方法


Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {this.context = context;this.dispatcher = dispatcher;this.cache = cache;this.listener = listener;this.requestTransformer = requestTransformer;this.defaultBitmapConfig = defaultBitmapConfig;int builtInHandlers = 7; int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);List<RequestHandler> allRequestHandlers = new ArrayList<>(builtInHandlers + extraCount);allRequestHandlers.add(new ResourceRequestHandler(context));if (extraRequestHandlers != null) {allRequestHandlers.addAll(extraRequestHandlers);}//初始化所有的图片读取HandlerallRequestHandlers.add(new ContactsPhotoRequestHandler(context));allRequestHandlers.add(new MediaStoreRequestHandler(context));allRequestHandlers.add(new ContentStreamRequestHandler(context));allRequestHandlers.add(new AssetRequestHandler(context));allRequestHandlers.add(new FileRequestHandler(context));allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));requestHandlers = Collections.unmodifiableList(allRequestHandlers);this.targetToAction = new WeakHashMap<>();//软引用的Mapthis.targetToDeferredRequestCreator = new WeakHashMap<>();//软引用的Map}

Builder


public static class Builder {private final Context context;private Downloader downloader;private ExecutorService service;private Cache cache;private Listener listener;private RequestTransformer transformer;private List<RequestHandler> requestHandlers;private Bitmap.Config defaultBitmapConfig;private boolean indicatorsEnabled;private boolean loggingEnabled;//构造Builder对象public Builder(@NonNull Context context) {if (context == null) {throw new IllegalArgumentException("Context must not be null.");}this.context = context.getApplicationContext();}//配置默认信息public Builder defaultBitmapConfig(@NonNull Bitmap.Config bitmapConfig) {if (bitmapConfig == null) {throw new IllegalArgumentException("Bitmap config must not be null.");}this.defaultBitmapConfig = bitmapConfig;return this;}//配置下载器public Builder downloader(@NonNull Downloader downloader) {if (downloader == null) {throw new IllegalArgumentException("Downloader must not be null.");}if (this.downloader != null) {throw new IllegalStateException("Downloader already set.");}this.downloader = downloader;return this;}//配置线程池public Builder executor(@NonNull ExecutorService executorService) {if (executorService == null) {throw new IllegalArgumentException("Executor service must not be null.");}if (this.service != null) {throw new IllegalStateException("Executor service already set.");}this.service = executorService;return this;}//配置内存缓存策略public Builder memoryCache(@NonNull Cache memoryCache) {if (memoryCache == null) {throw new IllegalArgumentException("Memory cache must not be null.");}if (this.cache != null) {throw new IllegalStateException("Memory cache already set.");}this.cache = memoryCache;return this;}//配置回调接库public Builder listener(@NonNull Listener listener) {if (listener == null) {throw new IllegalArgumentException("Listener must not be null.");}if (this.listener != null) {throw new IllegalStateException("Listener already set.");}this.listener = listener;return this;}//请求转换器public Builder requestTransformer(@NonNull RequestTransformer transformer) {if (transformer == null) {throw new IllegalArgumentException("Transformer must not be null.");}if (this.transformer != null) {throw new IllegalStateException("Transformer already set.");}this.transformer = transformer;return this;}//添加请求处理器public Builder addRequestHandler(@NonNull RequestHandler requestHandler) {if (requestHandler == null) {throw new IllegalArgumentException("RequestHandler must not be null.");}if (requestHandlers == null) {requestHandlers = new ArrayList<>();}if (requestHandlers.contains(requestHandler)) {throw new IllegalStateException("RequestHandler already registered.");}requestHandlers.add(requestHandler);return this;}//指示器开关public Builder indicatorsEnabled(boolean enabled) {this.indicatorsEnabled = enabled;return this;}//日志开关public Builder loggingEnabled(boolean enabled) {this.loggingEnabled = enabled;return this;}//构造Picassopublic Picasso build() {Context context = this.context;if (downloader == null) {downloader = new OkHttp3Downloader(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, defaultBitmapConfig, indicatorsEnabled, loggingEnabled);}
}

由于Picasso的配置信息很多,所以采用的是Builder模式,所以跟大多数框架一样,采用了建造者模式进行构造的。

cancelTag

通过tag取消请求


public void cancelTag(@NonNull Object tag) {checkMain();if (tag == null) {throw new IllegalArgumentException("Cannot cancel requests with null tag.");}List<Action> actions = new ArrayList<>(targetToAction.values());//遍历所有的Action,如果找到该tag,则进行取消for (int i = 0, n = actions.size(); i < n; i++) {Action action = actions.get(i);if (tag.equals(action.getTag())) {//取消请求cancelExistingRequest(action.getTarget());}}List<DeferredRequestCreator> deferredRequestCreators =new ArrayList<>(targetToDeferredRequestCreator.values());//取消需要重新测量的deferredRequestCreatorfor (int i = 0, n = deferredRequestCreators.size(); i < n; i++) {DeferredRequestCreator deferredRequestCreator = deferredRequestCreators.get(i);if (tag.equals(deferredRequestCreator.getTag())) {deferredRequestCreator.cancel();}}
}

继续看cancelExistingRequest


void cancelExistingRequest(Object target) {checkMain();Action action = targetToAction.remove(target);if (action != null) {//取消Actionaction.cancel();//取消BitmapHunter中的Action,一会儿再看Action中分析dispatcher.dispatchCancel(action);}if (target instanceof ImageView) {ImageView targetImageView = (ImageView) target;DeferredRequestCreator deferredRequestCreator =targetToDeferredRequestCreator.remove(targetImageView);if (deferredRequestCreator != null) {//取消延迟加载的请求deferredRequestCreator.cancel();}}
}

enqueueAndSubmit


void enqueueAndSubmit(Action action) {Object target = action.getTarget();if (target != null && targetToAction.get(target) != action) {cancelExistingRequest(target);targetToAction.put(target, action);}//提交请求submit(action);
}

resumeAction


void resumeAction(Action action) {Bitmap bitmap = null;//从内存中进行读取if (shouldReadFromMemoryCache(action.memoryPolicy)) {bitmap = quickMemoryCacheCheck(action.getKey());}if (bitmap != null) {// 内存读取成功,完成整个流程deliverAction(bitmap, LoadedFrom.MEMORY, action, null);} else {// 重新加入队列enqueueAndSubmit(action);if (loggingEnabled) {log(OWNER_MAIN, VERB_RESUMED, action.request.logId());}}
}

handler


//主线程中创建
static final Handler HANDLER = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case HUNTER_BATCH_COMPLETE: {//请求完成@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;for (int i = 0, n = batch.size(); i < n; i++) {BitmapHunter hunter = batch.get(i);hunter.picasso.complete(hunter);}break;}//GC回收的ImageViewcase REQUEST_GCED: {Action action = (Action) msg.obj;action.picasso.cancelExistingRequest(action.getTarget());break;}//请求恢复case REQUEST_BATCH_RESUME:for (int i = 0, n = batch.size(); i < n; i++) {Action action = batch.get(i);action.picasso.resumeAction(action);}break;default:throw new AssertionError("Unknown handler message received: " + msg.what);}}
};

load

加载的资源路径


//加载路径
public RequestCreator load(@Nullable String path) {if (path == null) {return new RequestCreator(this, null, 0);}if (path.trim().length() == 0) {throw new IllegalArgumentException("Path must not be empty.");}return load(Uri.parse(path));
}
//加载文件
public RequestCreator load(@NonNull File file) {if (file == null) {return new RequestCreator(this, null, 0);}return load(Uri.fromFile(file));
}//加载资源id
public RequestCreator load(@DrawableRes int resourceId) {if (resourceId == 0) {throw new IllegalArgumentException("Resource ID must not be zero.");}return new RequestCreator(this, null, resourceId);
}

load有很多重载方法,可以用来加载网络url,读取文件,读取资源id,返回的是RequestCreator

pauseTag

暂停请求


 */
public void pauseTag(@NonNull Object tag) {if (tag == null) {throw new IllegalArgumentException("tag == null");}//diapatcher转发dispatcher.dispatchPauseTag(tag);
}

resumeTag


public void resumeTag(@NonNull Object tag) {if (tag == null) {throw new IllegalArgumentException("tag == null");}//diapatcher转发dispatcher.dispatchResumeTag(tag);
}

quickMemoryCacheCheck


Bitmap quickMemoryCacheCheck(String key) {Bitmap cached = cache.get(key);if (cached != null) {stats.dispatchCacheHit();} else {stats.dispatchCacheMiss();}return cached;
}

complete


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, exception);}if (hasMultiple) {//多个Action,依次遍历转发for (int i = 0, n = joined.size(); i < n; i++) {Action join = joined.get(i);deliverAction(result, from, join, exception);}}if (listener != null && exception != null) {listener.onImageLoadFailed(this, uri, exception);}
}

shutDown


public void shutdown() {if (this == singleton) {throw new UnsupportedOperationException("Default singleton instance cannot be shutdown.");}if (shutdown) {return;}cache.clear();  //清空缓存cleanupThread.shutdown();//清理引用队列stats.shutdown();//关闭统计dispatcher.shutdown(); //关闭Dispatcher//取消所有延迟加载的请求for (DeferredRequestCreator deferredRequestCreator:targetToDeferredRequestCreator.values()) {deferredRequestCreator.cancel();}targetToDeferredRequestCreator.clear();shutdown = true;
}

RequestHandler

继承关系

RequestHandler

RequestHandler有很多子类,这些是实际上进行网络请求的处理器,使用最多的是NetworkRequestHandler,其核心方法是load,主要交给子类实现,看一下NetworkRequestHandler的实现:


@Override
public Result load(Request request, int networkPolicy) throws IOException {//采用最新版的Okhttp3进行网络请求okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);//获取网络返回的结果Response response = downloader.load(downloaderRequest);ResponseBody body = response.body();//省略若干代码//判断数据结果是来源于网络还是磁盘Picasso.LoadedFrom loadedFrom = response.cacheResponse() == null ? NETWORK : DISK;return new Result(body.source(), loadedFrom);
}

这里其实有一点需要注意的就是,我们在Picasso包下并没有发现磁盘缓存,只有LruCache这个类,也就是通常所说的内存缓存,但是并没有磁盘缓存,实际上Picasso的缓存全部交给了OKhttp来实现了,默认的缓存路径是由Utils生成的,PICASSO_CACHE的值picasso-cache


static File createDefaultCacheDir(Context context) {File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);if (!cache.exists()) {cache.mkdirs();}return cache;
}

成员变量


private static final AtomicInteger nextId = new AtomicInteger();//请求ID
private final Picasso picasso;//Picasso引用
private final Request.Builder data;//builder构造对象
private boolean noFade;//开启加载动画
private boolean deferred;//是否延迟加载
private boolean setPlaceholder = true;
private int placeholderResId;
private int errorResId;
private int memoryPolicy;
private int networkPolicy;
private Drawable placeholderDrawable;
private Drawable errorDrawable;
private Object tag;//请求的tag

很简单,除了Picasso之外,没有持有其它核心类的引用

构造方法


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;//不管是传入的什么参数,Picasso最后都是通过Uri来进行构造的,这样就需要针对不同的加载类型//来实现重载了this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}

Builder模式

参数Builder


//通过Uri构造
public Builder(@NonNull Uri uri) {setUri(uri);
}
//通过资源ID构造
public Builder(@DrawableRes int resourceId) {setResourceId(resourceId);
}
//同时传入Uri,资源ID,BitmapConfig
Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {this.uri = uri;this.resourceId = resourceId;this.config = bitmapConfig;
}

Request构造


private Builder(Request request) {uri = request.uri;resourceId = request.resourceId;stableKey = request.stableKey;targetWidth = request.targetWidth;targetHeight = request.targetHeight;centerCrop = request.centerCrop;centerInside = request.centerInside;centerCropGravity = request.centerCropGravity;rotationDegrees = request.rotationDegrees;rotationPivotX = request.rotationPivotX;rotationPivotY = request.rotationPivotY;hasRotationPivot = request.hasRotationPivot;purgeable = request.purgeable;onlyScaleDown = request.onlyScaleDown;if (request.transformations != null) {transformations = new ArrayList<>(request.transformations);}config = request.config;priority = request.priority;
}

fit方法

自适应ImageView的尺寸


public RequestCreator fit() {deferred = true;return this;
}

resize

重设尺寸


public Builder resize(@Px int targetWidth, @Px int targetHeight) {if (targetWidth < 0) {throw new IllegalArgumentException("Width must be positive number or 0.");}if (targetHeight < 0) {throw new IllegalArgumentException("Height must be positive number or 0.");}if (targetHeight == 0 && targetWidth == 0) {throw new IllegalArgumentException("At least one dimension has to be positive number.");}this.targetWidth = targetWidth;this.targetHeight = targetHeight;return this;
}

属性配置


public Builder centerCrop(int alignGravity) {if (centerInside) {throw new IllegalStateException("Center crop can not be used after calling centerInside");}centerCrop = true;centerCropGravity = alignGravity;return this;
}public Builder clearCenterCrop() {centerCrop = false;centerCropGravity = Gravity.CENTER;return this;
}public Builder centerInside() {if (centerCrop) {throw new IllegalStateException("Center inside can not be used after calling centerCrop");}centerInside = true;return this;
}

Action

Action是一个包装类,会对我们的加载行为进行一次包装,包含了加载的信息以及回调,先看一下它的继承关系

继承关系

Action

Action跟RequestHandler一样,有很多子类,不过很常见的一个是ImageViewAction,一会儿会重点分析一下

成员变量


final Picasso picasso;
final Request request;
final WeakReference<T> target;
final boolean noFade;
final int memoryPolicy;
final int networkPolicy;
final int errorResId;
final Drawable errorDrawable;
final String key;
final Object tag;

包含了picasso,request以及target的弱引用

构造方法


Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {this.picasso = picasso;this.request = request;this.target =target == null ? null : new RequestWeakReference<>(this, target, picasso.referenceQueue);this.memoryPolicy = memoryPolicy;this.networkPolicy = networkPolicy;this.noFade = noFade;this.errorResId = errorResId;this.errorDrawable = errorDrawable;this.key = key;this.tag = (tag != null ? tag : this);
}

下面看一下它的两个方法,一个是complete一个是error方法,由于父类是抽象方法,所以只能交给子类去处理,所以下面以ImageViewAction来举例说明

complete


@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();}
}

error


@Override
public void error(Exception e) {ImageView target = this.target.get();if (target == null) {return;}Drawable placeholder = target.getDrawable();//如果的动画还在继续,那么就停止动画if (placeholder instanceof AnimationDrawable) {((AnimationDrawable) placeholder).stop();}//如果设置加载失败的占位图if (errorResId != 0) {target.setImageResource(errorResId);} else if (errorDrawable != null) {target.setImageDrawable(errorDrawable);}if (callback != null) {//失败的回调callback.onError(e);}
}

实际上complete跟error的并没有做什么,都只是做了简单的回调,那么也就是说Action只是一个包装类,没有额外的功能

BitmapHunter

Bitmap捕捉者,或者说是Bitmap猎人,就是用来寻找Bitmap的一个类,继承自Runnable,说明可以用来进行耗时操作。

注释


Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since this will only ever happen in background threads we help avoid excessive memory thrashing as well as potential OOMs. Shamelessly stolen from Volley.

解码Bitmap的全局锁用以保证我们每次只能够解码一个Bitmap。因为解码操作只是发生在后台线程,所以为了避免解码时太多的内存占用导致OOM,借鉴于Volley。

成员变量


final Picasso picasso;
final Dispatcher dispatcher;
final Cache cache;
final Stats stats;
final String key;
final Request data;
final int memoryPolicy;
int networkPolicy;
final RequestHandler requestHandler;
Action action;
List<Action> actions;
Bitmap result;
Future<?> future;
Picasso.LoadedFrom loadedFrom;
Exception exception;
int exifOrientation; // Determined during decoding of original resource.
int retryCount;
Picasso.Priority priority;

持有所有核心类

构造方法


BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,RequestHandler requestHandler) {this.sequence = SEQUENCE_GENERATOR.incrementAndGet();this.picasso = picasso;this.dispatcher = dispatcher;this.cache = cache;this.stats = stats;this.action = action;this.key = action.getKey();this.data = action.getRequest();this.priority = action.getPriority();this.memoryPolicy = action.getMemoryPolicy();this.networkPolicy = action.getNetworkPolicy();this.requestHandler = requestHandler;this.retryCount = requestHandler.getRetryCount();
}

可以看到,很多参数都是传递过来的,也就是自己不持有。

run


@Override
public void run() {try {updateThreadName(data);//获取Bitmapresult = hunt();if (result == null) {dispatcher.dispatchFailed(this);} else {dispatcher.dispatchComplete(this);}}
}

hunt


Bitmap hunt() throws IOException {Bitmap bitmap = null;if (shouldReadFromMemoryCache(memoryPolicy)) {//再次读取内存缓存,因为不同的请求优先级不一样,很可能等到真正执行的时候//当前的url已经被缓存了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;}}networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;//如果没有读取缓存或者缓存读取失败,就开始真正的请求RequestHandler.Result result = requestHandler.load(data, networkPolicy);if (result != null) {loadedFrom = result.getLoadedFrom();exifOrientation = result.getExifOrientation();bitmap = result.getBitmap();// If there was no Bitmap then we need to decode it from the stream.if (bitmap == null) {Source source = result.getSource();try {bitmap = decodeStream(source, data);} finally {try {source.close();} catch (IOException ignored) {}}}}if (bitmap != null) {if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_DECODED, data.logId());}stats.dispatchBitmapDecoded(bitmap);//看看是否需要进行变换前预处理,有的话就处理if (data.needsTransformation() || exifOrientation != 0) {synchronized (DECODE_LOCK) {//通过上锁,每次只有一个进行Decodeingif (data.needsMatrixTransform() || exifOrientation != 0) {bitmap = transformResult(data, bitmap, exifOrientation);if (picasso.loggingEnabled) {log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());}}if (data.hasCustomTransformations()) {bitmap = applyCustomTransformations(data.transformations, bitmap);}}if (bitmap != null) {stats.dispatchBitmapTransformed(bitmap);}}}return bitmap;
}

forRequest


static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {Request request = action.getRequest();List<RequestHandler> requestHandlers = picasso.getRequestHandlers();//遍历已有的requestHandlers,看看能不能有能够处理的requestHandlerfor (int i = 0, count = requestHandlers.size(); i < count; i++) {RequestHandler requestHandler = requestHandlers.get(i);if (requestHandler.canHandleRequest(request)) {return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);}}如果没有的话,重新创建一个return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}

performSubmit


void performSubmit(Action action, boolean dismissFailed) {//如果之前是pause过,那么调用resumeif (pausedTags.contains(action.getTag())) {pausedActions.put(action.getTarget(), action);return;}//通过Key获取BitmapHunterBitmapHunter hunter = hunterMap.get(action.getKey());if (hunter != null) {//将action添加进BitmapHunterhunter.attach(action);return;}if (service.isShutdown()) {//如果线程池关闭,直接返回if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");}return;}//创建一个BitmapHunterhunter = forRequest(action.getPicasso(), this, cache, stats, action);//拿到线程池执行的结果,BitmapHunter肯定是个Runnable或者Futurehunter.future = service.submit(hunter);//存入map中hunterMap.put(action.getKey(), hunter);if (dismissFailed) {failedActions.remove(action.getTarget());}if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());}
}

performPauseTag


void performPauseTag(Object tag) {// 已经paused过,直接返回if (!pausedTags.add(tag)) {return;}// Go through all active hunters and detach/pause the requests// that have the paused tag.for (Iterator<BitmapHunter> it = hunterMap.values().iterator(); it.hasNext(); ) {BitmapHunter hunter = it.next();boolean loggingEnabled = hunter.getPicasso().loggingEnabled;Action single = hunter.getAction();List<Action> joined = hunter.getActions();boolean hasMultiple = joined != null && !joined.isEmpty();// Hunter has no requests, bail early.if (single == null && !hasMultiple) {continue;}//判断当前唯一的Action是否跟暂停的tag相同if (single != null && single.getTag().equals(tag)) {//从hunter中移除hunter.detach(single);//添加进pausedActionspausedActions.put(single.getTarget(), single);}if (hasMultiple) {//如果有多个Actionfor (int i = joined.size() - 1; i >= 0; i--) {Action action = joined.get(i);if (!action.getTag().equals(tag)) {continue;}//跟单个一样hunter.detach(action);pausedActions.put(action.getTarget(), action);}}}
}

performResumeTag


void performResumeTag(Object tag) {// 移除恢复的tagif (!pausedTags.remove(tag)) {return;}List<Action> batch = null;for (Iterator<Action> i = pausedActions.values().iterator(); i.hasNext(); ) {Action action = i.next();if (action.getTag().equals(tag)) {if (batch == null) {batch = new ArrayList<>();}//添加进List集合汇总batch.add(action);//从暂停的集合中移除i.remove();}}if (batch != null) {//切换到主线程,执行resumeActionmainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(REQUEST_BATCH_RESUME, batch));}
}

实际上performResumeTag在Picasso的分析中已经说明,如果没有设置内存缓存,那么相当于重新开启一个请求,如果设置了内存缓存,读取失败也会重新开启一个请求,只有在开启内存缓存并且读取成功performResumeTag才不会重新创建请求。

decodeStream


static Bitmap decodeStream(Source source, Request request) throws IOException {BufferedSource bufferedSource = Okio.buffer(source);boolean isWebPFile = Utils.isWebPFile(bufferedSource);boolean isPurgeable = request.purgeable && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP;BitmapFactory.Options options = RequestHandler.createBitmapOptions(request);boolean calculateSize = RequestHandler.requiresInSampleSize(options);if (isWebPFile || isPurgeable) {byte[] bytes = bufferedSource.readByteArray();if (calculateSize) {BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,request);}return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);} else {InputStream stream = bufferedSource.inputStream();if (calculateSize) {// TODO use an InputStream that buffers with Okio...MarkableInputStream markStream = new MarkableInputStream(stream);stream = markStream;markStream.allowMarksToExpire(false);long mark = markStream.savePosition(1024);BitmapFactory.decodeStream(stream, null, options);//计算Bitmap的尺寸RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,request);markStream.reset(mark);markStream.allowMarksToExpire(true);}Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);if (bitmap == null) {throw new IOException("Failed to decode stream.");}return bitmap;}
}

calculateInSampleSize(RequestHandler的方法)


static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height,BitmapFactory.Options options, Request request) {int sampleSize = 1;if (height > reqHeight || width > reqWidth) {final int heightRatio;final int widthRatio;if (reqHeight == 0) {sampleSize = (int) Math.floor((float) width / (float) reqWidth);} else if (reqWidth == 0) {sampleSize = (int) Math.floor((float) height / (float) reqHeight);} else {heightRatio = (int) Math.floor((float) height / (float) reqHeight);widthRatio = (int) Math.floor((float) width / (float) reqWidth);sampleSize = request.centerInside? Math.max(heightRatio, widthRatio): Math.min(heightRatio, widthRatio);}}options.inSampleSize = sampleSize;options.inJustDecodeBounds = false;
}

通过传入目标宽高以及实际的宽高进行缩放,然后进行缩放得到想要的尺寸

Dispatcher

成员变量


final DispatcherThread dispatcherThread;//HandlerThread,用于在子线程中创建Looper
final Context context;
final ExecutorService service;//线程池
final Downloader downloader;//图片下载器
final Map<String, BitmapHunter> hunterMap;//存放BitmapHunter
final Map<Object, Action> failedActions;//失败的请求
final Map<Object, Action> pausedActions;//暂停的请求
final Set<Object> pausedTags;//暂停的tag集合
final Handler handler;//Dispatcher中消息转发的Handler
final Handler mainThreadHandler;//主线程中的Handler
final Cache cache;
final Stats stats;
final List<BitmapHunter> batch;//BitmapHunter集合
final NetworkBroadcastReceiver receiver;//网络状态监听的广播
final boolean scansNetworkChanges;网络状态是否切换的标志

构造方法


Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,Downloader downloader, Cache cache, Stats stats) {this.dispatcherThread = new DispatcherThread();this.dispatcherThread.start();//启动HandlerThreadUtils.flushStackLocalLeaks(dispatcherThread.getLooper());this.context = context;this.service = service;this.hunterMap = new LinkedHashMap<>();this.failedActions = new WeakHashMap<>();//软引用this.pausedActions = new WeakHashMap<>();//软引用this.pausedTags = new HashSet<>();this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);this.downloader = downloader;this.mainThreadHandler = mainThreadHandler;this.cache = cache;this.stats = stats;this.batch = new ArrayList<>(4);this.airplaneMode = Utils.isAirplaneModeOn(this.context);//如果没有获取到网络状态改变的权限则全部为falsethis.scansNetworkChanges      =hasPermission(context,Manifest.permission.ACCESS_NETWORK_STATE);//创建网络状态监听的广播this.receiver = new NetworkBroadcastReceiver(this);//注册广播receiver.register();
}

Handler

这是区别于MainHandler的一个内部的Handler,用在这里可以让逻辑更加清晰,实际上不用也可以。


private static class DispatcherHandler extends Handler {private final Dispatcher dispatcher;//传入子线程的LooperDispatcherHandler(Looper looper, Dispatcher dispatcher) {super(looper);this.dispatcher = dispatcher;}@Overridepublic void handleMessage(final Message msg) {switch (msg.what) {case REQUEST_SUBMIT: {//提交请求Action action = (Action) msg.obj;dispatcher.performSubmit(action);break;}case REQUEST_CANCEL: {//取消请求Action action = (Action) msg.obj;dispatcher.performCancel(action);break;}case TAG_PAUSE: {//暂停请求Object tag = msg.obj;dispatcher.performPauseTag(tag);break;}case TAG_RESUME: {//恢复请求Object tag = msg.obj;dispatcher.performResumeTag(tag);break;}case HUNTER_COMPLETE: {//请求完成BitmapHunter hunter = (BitmapHunter) msg.obj;dispatcher.performComplete(hunter);break;}case HUNTER_RETRY: {//请求重试BitmapHunter hunter = (BitmapHunter) msg.obj;dispatcher.performRetry(hunter);break;}}
}

NetworkBroadcastReceiver

网络监听的广播


static class NetworkBroadcastReceiver extends BroadcastReceiver {static final String EXTRA_AIRPLANE_STATE = "state";private final Dispatcher dispatcher;NetworkBroadcastReceiver(Dispatcher dispatcher) {this.dispatcher = dispatcher;}void register() {IntentFilter filter = new IntentFilter();filter.addAction(ACTION_AIRPLANE_MODE_CHANGED);if (dispatcher.scansNetworkChanges) {filter.addAction(CONNECTIVITY_ACTION);}dispatcher.context.registerReceiver(this, filter);}void unregister() {dispatcher.context.unregisterReceiver(this);}@SuppressLint("MissingPermission")@Overridepublic void onReceive(Context context, Intent intent) {if (intent == null) {return;}final String action = intent.getAction();if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {if (!intent.hasExtra(EXTRA_AIRPLANE_STATE)) {return; /}dispatcher.dispatchAirplaneModeChange(intent.getBooleanExtra(EXTRA_AIRPLANE_STATE, false));} else if (CONNECTIVITY_ACTION.equals(action)) {ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);//将网络状态的变化通知内部的Handlerdispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());}}
}

dispatchNetworkStateChange最后还是调用了adjustThreadCount


void adjustThreadCount(NetworkInfo info) {if (info == null || !info.isConnectedOrConnecting()) {setThreadCount(DEFAULT_THREAD_COUNT);return;}switch (info.getType()) {case ConnectivityManager.TYPE_WIFI:case ConnectivityManager.TYPE_WIMAX:case ConnectivityManager.TYPE_ETHERNET:setThreadCount(4);break;case ConnectivityManager.TYPE_MOBILE:switch (info.getSubtype()) {case TelephonyManager.NETWORK_TYPE_LTE:  // 4Gcase TelephonyManager.NETWORK_TYPE_HSPAP:case TelephonyManager.NETWORK_TYPE_EHRPD:setThreadCount(3);break;case TelephonyManager.NETWORK_TYPE_UMTS: // 3Gcase TelephonyManager.NETWORK_TYPE_CDMA:case TelephonyManager.NETWORK_TYPE_EVDO_0:case TelephonyManager.NETWORK_TYPE_EVDO_A:case TelephonyManager.NETWORK_TYPE_EVDO_B:setThreadCount(2);break;case TelephonyManager.NETWORK_TYPE_GPRS: // 2Gcase TelephonyManager.NETWORK_TYPE_EDGE:setThreadCount(1);break;default:setThreadCount(DEFAULT_THREAD_COUNT);}break;default:setThreadCount(DEFAULT_THREAD_COUNT);}
}

也就是根据不同的网络状态设置线程池的核心线程数以及最大线程数

performSubmit


void performSubmit(Action action, boolean dismissFailed) {//如果之前是pause过,那么调用resumeif (pausedTags.contains(action.getTag())) {pausedActions.put(action.getTarget(), action);return;}//通过Key获取BitmapHunterBitmapHunter hunter = hunterMap.get(action.getKey());if (hunter != null) {//将action添加进BitmapHunterhunter.attach(action);return;}if (service.isShutdown()) {//如果线程池关闭,直接返回if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");}return;}//创建一个BitmapHunterhunter = forRequest(action.getPicasso(), this, cache, stats, action);//拿到线程池执行的结果,BitmapHunter肯定是个Runnable或者Futurehunter.future = service.submit(hunter);//存入map中hunterMap.put(action.getKey(), hunter);if (dismissFailed) {failedActions.remove(action.getTarget());}if (action.getPicasso().loggingEnabled) {log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());}
}

performCancel


void performCancel(Action action) {String key = action.getKey();//获取hunterBitmapHunter hunter = hunterMap.get(key);if (hunter != null) {从hunter中移除这个Actionhunter.detach(action);if (hunter.cancel()) {hunterMap.remove(key);}}//如果pausedTags中含有此Action,移除掉if (pausedTags.contains(action.getTag())) {pausedActions.remove(action.getTarget());}//尝试着从失败的Action中移除此ActionAction remove = failedActions.remove(action.getTarget());
}

shutdown


void shutdown() {// 关闭线程池if (service instanceof PicassoExecutorService) {service.shutdown();}//关闭下载器downloader.shutdown();//退出handlerdispatcherThread.quit();//移除广播Picasso.HANDLER.post(new Runnable() {@Overridepublic void run() {receiver.unregister();}});
}

总结

上面分析了Picasso的核心类,还有一些比较重要的类,LrucachePicassoExecutorService,由于Lrucache跟Volley的DiskBaseCache并无区别,底层采用LinkedHashMap,通过使用的顺序来进行排序,容量不足时删除最近最少使用的数据,而PicassoExecutorService属于线程池,之前也分析过。

Picasso相对于Volley注释太少,不是很好阅读,所幸命名比较规范,可以根据名字大致猜出这个类是干什么的,整体封装性比Volley好,但是扩展性不如Volley,不过都有很多值得学习的地方,下面简单对比分析一下:

 VolleyPicasso
RequestRequestRequestCreator—>Request—>Action—>BitmapHunter
CacheDiskLruCacheDiskLruCache&DiskDiskLruCache
RequstQueueBlockingQueueThreadPoolExecutor
DispatcherCacheDispatcher& NetworkDispatcherDispatcherThread
TaskActioncancelcancel,pause,resume

参考资料

http://blog.csdn.net/chdjj/article/details/49964901

https://blog.happyhls.me/category/android/picasso/

 

 

这篇关于Picasso源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

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

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