前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android面试题经典之Glide取消加载以及线程池优化

Android面试题经典之Glide取消加载以及线程池优化

作者头像
AntDream
发布2024-07-10 16:03:27
570
发布2024-07-10 16:03:27
举报
文章被收录于专栏:程序员修炼之路

Glide通过生命周期取消加载

生命周期回调过程 onStop —>RequestManager.onStop –>RequestTracker.pauseRequest –> SingleRequest.pause –> SingleRequest.cancel —> Engine.cancel —> EngineJob.removeCallback —> 如果所有的回调监听都移除了 –> DecodeJob.cancel

代码语言:javascript
复制
//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标记在停止流程
代码语言:javascript
复制
//DecodeJob.class
public void cancel() {
  	isCancelled = true;
  	DataFetcherGenerator local = currentGenerator;
  	if (local != null) {
  		local.cancel();
  	}
}

上面最终会调用到具体的获取图片的任务,比如从网络获取图片的HttpUrlFetcher

代码语言:javascript
复制
 //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

代码语言:javascript
复制
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拦截的

代码语言:javascript
复制
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,主要的一个功能是实现限制部分线程网络访问,利用的是严苛模式

代码语言:javascript
复制
@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做了线程池优化,核心线程也会遵循超时策略

代码语言:javascript
复制
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);
}

END

点亮【赞和在看】,让钱和爱都流向你。

心里种花,人生才不会荒芜,如果你也想一起成长,请点个关注吧。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AntDream 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Glide中的线程池
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档