本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点
Glide通过生命周期取消加载
生命周期回调过程
onStop
—>RequestManager.onStop
–>RequestTracker.pauseRequest
–> SingleRequest.pause
–> SingleRequest.cancel
—> Engine.cancel
—> EngineJob.removeCallback
—> 如果所有的回调监听都移除了
–> DecodeJob.cancel
//EngineJob.class
synchronized void removeCallback(ResourceCallback cb) {
stateVerifier.throwIfRecycled();
cbs.remove(cb);
if (cbs.isEmpty()) {
cancel();
boolean isFinishedRunning = hasResource || hasLoadFailed;
if (isFinishedRunning && pendingCallbacks.get() == 0) {
release();
}
}
}
- 如果任务没有执行,就从队列里移除,取消任务
- 如果任务已经执行,就利用isCancelled标记在停止流程
//DecodeJob.class
public void cancel() {
isCancelled = true;
DataFetcherGenerator local = currentGenerator;
if (local != null) {
local.cancel();
}
}
上面最终会调用到具体的获取图片的任务,比如从网络获取图片的HttpUrlFetcher
//HttpUrlFetcher.class
private volatile boolean isCancelled;
@Override
public void cancel() {
// TODO: we should consider disconnecting the url connection here, but we can't do so
// directly because cancel is often called on the main thread.
isCancelled = true;
}
//内部是通过HttpURLConnection来从网络获取数据
private InputStream loadDataWithRedirects(
URL url, int redirects, URL lastUrl, Map<String, String> headers) throws HttpException {
...
urlConnection = buildAndConfigureConnection(url, headers);
try {
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
} catch (IOException e) {
...
}
if (isCancelled) {
return null;
}
final int statusCode = getHttpStatusCodeOrInvalid(urlConnection);
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField(REDIRECT_HEADER_FIELD);
...
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
...
}
}
由于cancel方法一般是在主线程调用的,所以这里采用的是volatile关键字这种方式,在正式获取网络数据时会进行拦截;
- 如果拦截到了,那直接返回null;如果没拦截到,就获取到数据
- 以上最终都会回调到DecodeJob的onDataFetcherReady方法
onDataFetcherReady —> decodeFromRetrievedData —> decodeFromData
private void decodeFromRetrievedData() {
...
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
...
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource, isLoadingFromAlternateCacheKey);
} else {
//任务被拦截,尝试其他的加载方式
runGenerators();
}
}
decodeFromData中会进行判断,如果data为Null就直接返回Null(被拦截时会是null),这个时候会执行runGenerators方法
runGenerators方法实际上就是加载流程的流转,比如先从文件中加载,文件中没有,就去网络加载,一个个去试这样。当然这里面肯定也有cancel拦截的
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
//被cancel拦截就不会尝试其他加载方式,直接任务取消
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
所以当加载数据的流程被拦截了,比如网络请求返回null,那到这里这个流程就拦截下来了,直接
notifyFailed任务失败了。
那如果网络加载的时候没有拦截住呢?data就不会为null,就会走notifyEncodeAndRelease方法,最终会一直走到EngineJob的notifyCallbacksOfResult方法,这个方法里面又会有cancel拦截。这样也就是数据加载前加载后都被拦截下来了
Glide中的线程池
自定义ThreadFactory,主要的一个功能是实现限制部分线程网络访问,利用的是严苛模式
@Override
public Thread newThread(@NonNull final Runnable runnable) {
Thread newThread =
delegate.newThread(
new Runnable() {
@Override
public void run() {
if (preventNetworkOperations) {
StrictMode.setThreadPolicy(
new ThreadPolicy.Builder().detectNetwork().penaltyDeath().build());
}
try {
runnable.run();
} catch (Throwable t) {
uncaughtThrowableStrategy.handle(t);
}
}
});
newThread.setName("glide-" + name + "-thread-" + threadNum.getAndIncrement());
return newThread;
}
线程池主要有这几种:
-
AnimationExecutor:加载动画相关,禁止访问网络;如果CPU核心数大于4,就是2个线程,否则是一个线程,核心线程数和最大线程数相同
-
diskCacheExecutor:从磁盘加载图片,禁止访问网络;线程数为1,核心线程数和最大线程数相同
-
sourceExecutor:可以访问网络,线程数跟CPU核心数有关,不大于4,核心线程数和最大线程数相同
-
newUnlimitedSourceExecutor:用于网络请求图片,没有核心线程,只有非核心线程,类似CacheThreadPoll
Glide做了线程池优化,核心线程也会遵循超时策略
public GlideExecutor build() {
if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException(
"Name must be non-null and non-empty, but given: " + name);
}
ThreadPoolExecutor executor =
new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
/*keepAliveTime=*/ threadTimeoutMillis,
TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(),
new DefaultThreadFactory(
threadFactory, name, uncaughtThrowableStrategy, preventNetworkOperations));
if (threadTimeoutMillis != NO_THREAD_TIMEOUT) {
executor.allowCoreThreadTimeOut(true);
}
return new GlideExecutor(executor);
}
欢迎关注我的公众号AntDream查看更多精彩文章!