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

本文首发于公众号“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查看更多精彩文章!

AntDream

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/763829.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringSecurity6 | 基于数据库实现登录认证

SpringSecurity6 | 基于数据库认证 ✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringSecurity6 ✨特色专栏: MySQL学习 🥭本文内容: SpringSecurity6 | 基于数据库实现登…

Cell | 泛癌蛋白基因组学分析,揭示癌症治疗靶点(章冰/高强)

– DOI: 10.1016/j.cell.2024.05.039 Pan-cancer proteogenomics expands the landscape of therapeutic targets 留意最新动态&#xff0c;请关注微信公众号&#xff1a;组学之心 最近课题组在写泛癌的综述&#xff0c;刚好这篇相关研究论文在6.24发表&#xff0c;新鲜出炉…

Toshiba东芝TB6612FNG电机驱动IC:释放性能与多功能性

在嵌入式系统和机器人技术领域&#xff0c;电机控制是一个关键方面&#xff0c;对项目的性能和可靠性有着显著影响。东芝的TB6612FNG电机驱动IC作为一个稳健且多功能的解决方案&#xff0c;在驱动双直流电机方面脱颖而出&#xff0c;提供了高性能、可靠性和易用性。本文将深入探…

Java [ 基础 ] 异常处理 ✨

✨探索Java基础 异常处理✨ 在Java编程中&#xff0c;异常处理是一个非常重要的概念&#xff0c;它有助于在程序运行时捕获和处理错误&#xff0c;从而使程序更加健壮和可靠。 本文将介绍Java中的异常基础知识、异常类型、异常处理机制以及最佳实践。 一、什么是异常&#…

SQL语句的案例分析

根据提供的图片内容&#xff0c;这段文字看起来像是一个SQL查询的一部分&#xff0c;特别是一个用于删除数据的语句。以下是对这段SQL的核心内容整理&#xff1a; ### 核心内容整理&#xff1a; 1. **删除操作**&#xff1a; - 使用DELETE语句来删除数据。 2. **子查询**…

惠海 H6900B 2.7V3.7V4.2V5V9V升12V24V48VLED升压恒流芯片IC

惠海H6900B LED升压恒流芯片IC是一款功能丰富的LED驱动解决方案&#xff0c;为高亮度LED灯串设计。以下是针对该产品的进一步分析和解释&#xff1a; 产品特点 高效率&#xff1a;高达95%以上的效率意味着在驱动LED时&#xff0c;只有很少的能量转化为热量&#xff0c;从而提…

轨迹规划 | 图解模型预测控制MPC算法(附ROS C++/Python/Matlab仿真)

目录 0 专栏介绍1 模型预测控制原理2 差速模型运动学3 基于差速模型的MPC控制4 仿真实现4.1 ROS C实现4.2 Python实现4.3 Matlab实现 0 专栏介绍 &#x1f525;附C/Python/Matlab全套代码&#x1f525;课程设计、毕业设计、创新竞赛必备&#xff01;详细介绍全局规划(图搜索、…

“论单元测试方法及应用”写作框架,软考高级论文,系统架构设计师论文

论文真题 1、概要叙述你参与管理和开发的软件项目,以吸你所担的主要工作。 2、结给你参与管理和开发的软件项目&#xff0c;简要叙述单元测试中静态测试和动态测试方法的基本内容。 3、结给你惨与管理和研发的软件项目,体阐述在玩测试过程中,如何确定白盒测试的覆盖标准,及如…

YOLO在目标检测与视频轨迹追踪中的应用

YOLO在目标检测与视频轨迹追踪中的应用 引言 在计算机视觉领域&#xff0c;目标检测与视频轨迹追踪是两个至关重要的研究方向。随着深度学习技术的飞速发展&#xff0c;尤其是卷积神经网络&#xff08;CNN&#xff09;的广泛应用&#xff0c;目标检测与视频轨迹追踪的性能得到…

WAIC | 斯梅尔数学与计算研究院邀您莅临WAIC 2024“数学与人工智能”论坛

当我们谈论起人工智能这一变革性力量时&#xff0c;就不得不提及数学。人工智能作为当今社会的热门话题&#xff0c;从AlphaGo到ChatGPT&#xff0c;从智能制造到数字文旅&#xff0c;它的发展和应用深刻地影响着行业和人们的生活。然而&#xff0c;人工智能的发展和基础离不开…

怎么把视频字幕提取出来?一招教你提取视频字幕

想必大家一定很有同感吧&#xff0c;视频已成为我们获取知识与新闻的主要渠道。 面对如此众多的视频资源&#xff0c;如何迅速筛选出核心信息并进行有效管理&#xff0c;成为了一项迫切需要解决的问题。 视频字幕提取翻译软件的问世&#xff0c;利用尖端的语音识别技术&#…

Kimi 上下文缓存功能开启公测!降低使用费用,加快模型相应速度

7月2日&#xff0c;系统之家发布消息&#xff0c;月之暗面科技有限公司旗下的Kimi开放平台正式推出上下文缓存功能&#xff0c;并已开放公测。这项功能专为处理频繁请求和大量重复引用初始上下文的场景设计&#xff0c;能有效降低使用长文本模型的成本&#xff0c;并显著提升处…

森林防火气象站:守护森林安全的科技利器

在广袤无垠的森林中&#xff0c;火灾一直是威胁森林生态安全的重要因素。为了有效预防和控制森林火灾&#xff0c;科学家们不断研发新技术&#xff0c;而森林防火气象站正是这一领域的重要成果之一。其中&#xff0c;森林防火气象站凭借其强大的功能和独特的设计&#xff0c;在…

laravel对接百度智能云 实现智能机器人

创建API Key和 Secret Key进入网址&#xff1a;百度智能云千帆大模型平台 如下图操作&#xff1a; 填写完毕点击确认后&#xff0c;即可得到sk和ak 后端接口实现代码&#xff1a; //调用百度智能云第三方机器人接口public function run($text) {$curl curl_init();curl_setop…

【基于R语言群体遗传学】-2-模拟基因型(simulating genotypes)

书接上文&#xff0c;我们昨天讨论了遗传的哈代温伯格比例&#xff1a; 【基于R语言群体遗传学】-1-哈代温伯格基因型比例-CSDN博客 接下来&#xff0c;如果我们能够模拟一个过程并观察模拟结果与我们预期的是否相符&#xff0c;这通常有助于指导我们对这个过程的直观感觉。让…

工业读码器与商用扫码器的区别

条码二维码在数字信息化应用越来越广泛&#xff0c;扫码器成为了数据收集和处理的重要工具&#xff0c;无论是工厂生产和物流包裹朔源追踪&#xff0c;还是商场超市扫码收银和餐饮娱乐等场景&#xff0c;均能看到扫码器的辅助&#xff0c;市场上的扫码器种类繁多&#xff0c;在…

C++修饰符类型

一、存储类运算符 auto&#xff08;自动存储类&#xff0c;但在现代C中&#xff0c;它通常用于自动类型推导&#xff09; register&#xff08;建议编译器将变量存储在寄存器中&#xff0c;但现代编译器通常忽略此关键字&#xff09; static&#xff08;静态存储类&#xff…

AD20使用操作Part2

元件的放置 在原理图界面&#xff0c;在右下角&#xff0c;Panels 选择Components 在自己元件库直接拖过来&#xff0c;放到原理图。 器件的复制和对齐 双击边缘&#xff0c;更改为A3纸 画方格&#xff0c;把元件给规划&#xff1a;放置——绘图工具——线 Shift空格&am…

【linux网络(七)】数据链路层详解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux网络 1. 前言2. 认识MAC…

Android --- 新电脑安装Android Studio 使用 Android 内置模拟器电脑直接卡死,鼠标和键盘都操作不了

新电脑安装Android Studio 使用 Android 内置模拟器电脑直接卡死&#xff0c;鼠标和键盘都操作不了 大概原因就是,初始化默认Google的安卓模拟器占用的RAM内存是2048&#xff0c;如果电脑的性能和内存一般的话就可能卡死&#xff0c;解决方案是手动修改安卓模拟器的config文件&…