Glide 的超時控制相關處理

作者:newki

前言

Glide 相信大家都不陌生,各種源碼分析,使用介紹大家應該都是爛熟于心。但是設置 Glide 的超時問題大家遇到過沒有。

我遇到了,并且掉坑里了,情況是這樣的。

  1. 調用接口從網絡拉取用戶頭像,目前數據量不大,大致1000多個人。(用了自定義隊列)
  2. 使用 Glide 下載頭像到本地沙盒 File (為了方便的緩存下次更快)。
  3. 識別頭像中的人臉信息,并生成人臉Bitmap,(本身有成功失敗的處理與重試機制)
  4. 生成人臉對應的特征,并保存人臉特征數據和人臉特征圖片到沙盒 File 。
  5. 封裝人臉對象并加載到內存中保持全局單例。
  6. 場景業務:與Camera的預覽畫面中獲取到的活體人臉進行人臉比對。

開始并沒有設置超時時間,導致 Glide下載圖片的自定義隊列常常會出現卡死的情況,導致整個隊列執行緩慢甚至都無法繼續執行,整個注冊服務被阻塞,新進來的用戶一直等待時間過長甚至無法注冊。

問題嘛,就是圖片加載的問題,有些圖片無法加載,有些圖片太大加載時間過長,有些根本就不是圖片,有些網絡慢,不穩定,或者干脆就無網,有些是訪問權限問題,為了讓圖片下載隊列能正常運轉加入了 Glide 的超時機制,踩坑之路由此展開。

一、問題復現

Glide的使用,大家應該都清除,如何加timeout,這里給出一個示例代碼:

依賴:

implementation 'com.github.bumptech.glide:glide:4.15.1'
implementation 'com.github.bumptech.glide:annotations:4.15.1'
kapt 'com.github.bumptech.glide:compiler:4.15.1'

下載的方法使用一個擴展方法封裝了一下 :

fun Any.extDownloadImage(context: Context?, path: Any?, block: (file: File) -> Unit) {var startMillis = 0Lvar endMillis = 0LGlideApp.with(context!!).load(path).timeout(15000)  // 15秒.downloadOnly(object : SimpleTarget<File?>() {override fun onLoadStarted(placeholder: Drawable?) {startMillis = System.currentTimeMillis()YYLogUtils.w("開始加載:$startMillis")super.onLoadStarted(placeholder)}override fun onLoadFailed(errorDrawable: Drawable?) {endMillis = System.currentTimeMillis()YYLogUtils.w("Glide-onLoadFailed-Drawable,一共耗時:${endMillis - startMillis}")super.onLoadFailed(errorDrawable)}override fun onResourceReady(resource: File, transition: Transition<in File?>?) {endMillis = System.currentTimeMillis()YYLogUtils.w("Glide-onResourceReady-Drawable,一共耗時:${endMillis - startMillis}")block(resource)}})}

大家使用工具類或者直接 Glide 寫都是一樣的效果,不影響最終的結果。

使用:

val url = "https://s3.ap-southeast-1.amazonaws.com/yycircle-ap/202307/11/KZ8xIVsrlrYtjhw3t2t2RTUj0ZTWUFr2EhawOd4I-810x1080.jpeg"extDownloadImage(this@MainActivity, url, block = { file ->YYLogUtils.w("file:${file.absolutePath}")})

以亞馬遜云服務的圖片地址為例,不同的網絡情況,不同的網絡加載框架情況下,分別有什么不同。

1.1 HttpURLConnection 沒網的情況

原生 Glide 的網絡請求源碼在 HttpUrlFetcher 類中。

具體方法:

就算我們在 buildAndConfigureConnection 中設置了超時時間,但是 connect 方法直接就報錯了,也不會走timeout的邏輯

com.bumptech.glide.load.HttpException: Failed to connect or obtain data, status code: -1

1.1 HttpURLConnection 有網的但是不通

那如果有網,但是網不通呢?

這下確實會等待一小會了,由于我們設置的超時時間是15秒,打印Log看看。

class com.bumptech.glide.load.HttpException: Failed to connect or obtain data, status code: -1

錯誤和上面一樣,但是超時時間是10秒:

喂,玩我是吧。那我改 Glide 的超時時間為 5000, 也就是5秒,但是最終的結果還是10秒。

這是為什么呢?雖然連上了WIFI,但是沒網,還是無法解析hostname,而 HttpURLConnection 內部定義的這一階段的超時就是 10 秒。

我們可以把 Glide 的網絡請求源碼拷過來試試!

class HttpTest {private final HttpUrlConnectionFactory connectionFactory = new DefaultHttpUrlConnectionFactory();public HttpTest() {}public HttpURLConnection buildAndConfigureConnection(URL url, Map<String, String> headers) throws HttpException {HttpURLConnection urlConnection;try {urlConnection = connectionFactory.build(url);} catch (IOException e) {throw new RuntimeException("URL.openConnection threw");}for (Map.Entry<String, String> headerEntry : headers.entrySet()) {urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());}urlConnection.setConnectTimeout(7000);urlConnection.setReadTimeout(7000);urlConnection.setUseCaches(false);urlConnection.setDoInput(true);urlConnection.setInstanceFollowRedirects(false);return urlConnection;}interface HttpUrlConnectionFactory {HttpURLConnection build(URL url) throws IOException;}private static class DefaultHttpUrlConnectionFactory implements HttpUrlConnectionFactory {DefaultHttpUrlConnectionFactory() {}@Overridepublic HttpURLConnection build(URL url) throws IOException {return (HttpURLConnection) url.openConnection();}}
}

為了和之前的區別開,我們設置7秒的超時,看看結果有什么變化?

java.net.UnknownHostException: Unable to resolve host “s3.ap-southeast-1.amazonaws.com”: No address associated with hostname

錯誤已經很明顯了,哎

1.1 HttpURLConnection 有網通了,但是沒訪問權限

那我現在把網連上,把授權關掉,雖然能解析域名,但是沒有訪問權限,還是無法獲取圖片,此時又會出現什么情況。

我們還是設置為15秒的超時:

 GlideApp.with(context!!).load(path).apply(options).timeout(15000).into(object : SimpleTarget<Drawable>() {override fun onLoadStarted(placeholder: Drawable?) {startMillis = System.currentTimeMillis()YYLogUtils.w("開始加載:$startMillis")super.onLoadStarted(placeholder)}override fun onLoadFailed(errorDrawable: Drawable?) {endMillis = System.currentTimeMillis()YYLogUtils.w("Glide-onLoadFailed-Drawable,一共耗時:${endMillis - startMillis}")super.onLoadFailed(errorDrawable)}override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {endMillis = System.currentTimeMillis()YYLogUtils.w("Glide-onResourceReady-Drawable,一共耗時:${endMillis - startMillis}")block(resource)}})

出錯的信息,這次網絡請求確實是通了,確實是走到 timeout 里面了。

但是這個時間為什么是30秒?

如果我們設置超時時間是20秒?那么結果就是40秒!

是 HttpURLConnection 的問題?我們還是用上一步的 7秒超時的原生 HttpURLConnection 代碼訪問試試!

可以看到結果是符合我們預期的7秒超時。

那為什么 Glide 默認的 HttpURLConnection 會是兩倍的超時時間呢?

是因為 Glide 內部對 HttpURLConnection 的請求做了重試處理。

當它第一次超時的時候,會走到錯誤回調中,但是并沒有回調出去,而是自己處理了一遍。

真的太迷了,我自己不會學重試嗎,要你多管閑事…

1.1 換成 OkHttp3

如果擺脫這一套 HttpURLConnection 的邏輯與重試邏輯,Glide 也提供了第三方網絡請求的接口,例如我們常用的用 OkHttp 來加載圖片。

大家應該是不陌生的,加入依賴庫即可:

implementation 'com.github.bumptech.glide:okhttp3-integration:4.15.1'

此時已經換成OkHttp加載了,它默認的超時時間就是10秒,此時我們修改Glide的超時時間是無效的。

    GlideApp.with(context!!).load(path).apply(options).timeout(20000) .into(object : SimpleTarget<Drawable>() {override fun onLoadStarted(placeholder: Drawable?) {startMillis = System.currentTimeMillis()YYLogUtils.w("開始加載:$startMillis")super.onLoadStarted(placeholder)}override fun onLoadFailed(errorDrawable: Drawable?) {endMillis = System.currentTimeMillis()YYLogUtils.w("Glide-onLoadFailed-Drawable,一共耗時:${endMillis - startMillis}")super.onLoadFailed(errorDrawable)}override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {endMillis = System.currentTimeMillis()YYLogUtils.w("Glide-onResourceReady-Drawable,一共耗時:${endMillis - startMillis}")block(resource)}})

別說改成20秒,改成100秒也無效!因為這些配置是修改的默認的 HttpURLConnection 的超時時間的。OkHttp的加載根本就不走那一套了。

打印 Log 如下:

哎,真的是頭都大了,不是說好的開箱即用嗎,咋個這么多問題,還分這么多情況,真不知道該如何是好。

二、問題解決1,使用 OkHttp3 的自定義 Client

既然我們使用 OkHttp 之后,無法在 Glide 中修改超時時間,那么我們直接修改 OkHttp 的超時時間可不不可以?

大家或多或少都配置過,這里直接貼代碼:

@GlideModule
public final class HttpGlideModule extends AppGlideModule {@Overridepublic void registerComponents(Context context, Glide glide, Registry registry) {// 替換自定義的Glide網絡加載registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(GlideOkHttpUtils.getHttpClient()));}
}

實現我們自己的 OkHttpClient 類:

public class GlideOkHttpUtils {public static OkHttpClient getHttpClient() {OkHttpClient.Builder builder = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS).addInterceptor(new LoggingInterceptor())  //打印請求日志,可有可無.sslSocketFactory(getSSLSocketFactory()).hostnameVerifier(getHostnameVerifier());return builder.build();}/*** getSSLSocketFactory、getTrustManagers、getHostnameVerifier* 使OkHttpClient支持自簽名證書,避免Glide加載不了Https圖片*/private static SSLSocketFactory getSSLSocketFactory() {try {SSLContext sslContext = SSLContext.getInstance("SSL");sslContext.init(null, getTrustManagers(), new SecureRandom());return sslContext.getSocketFactory();} catch (Exception e) {throw new RuntimeException(e);}}private static TrustManager[] getTrustManagers() {return new TrustManager[]{new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}}};}private static HostnameVerifier getHostnameVerifier() {return new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}};}}

可以看到我們設置了15秒的超時,打印的結果如下:

想設置幾秒就是幾秒,沒有重試導致時間不對一說。這確實是一種方案。

三、問題解決2,使用協程timeout

另一種方案就是使用協程的超時來控制,由于 Glide 的加載圖片與回調的處理是匿名函數實現的,內部回調的處理我們先用協程處理鋪平回調。

之前講過,這里直接上代碼

suspend fun Any.downloadImageWithGlide(imgUrl: String): File {return suspendCancellableCoroutine { cancellableContinuation ->GlideApp.with(commContext()).load(imgUrl).timeout(15000)  //設不設都一樣,反正不靠你.diskCacheStrategy(DiskCacheStrategy.DATA).downloadOnly(object : SimpleTarget<File?>() {override fun onResourceReady(resource: File, transition: Transition<in File?>?) {cancellableContinuation.resume(resource)}override fun onLoadFailed(errorDrawable: Drawable?) {super.onLoadFailed(errorDrawable)cancellableContinuation.resumeWithException(RuntimeException("加載失敗了"))}})}
}

使用起來我們就是協程的 timeout 函數,不管底層是什么實現的,直接上層的超時攔截。

    launch{...try {val file = withTimeout(15000) {downloadImageWithGlide(userInfo.avatarUrl)}YYLogUtils.e("注冊人臉服務-圖片加載成功:${file.absolutePath}")//下載成功之后賦值本地路徑到對象中userInfo.avatarPath = file.absolutePath//去注冊人臉registerHotelMember(userInfo)} catch (e: TimeoutCancellationException) {YYLogUtils.e("注冊人臉服務-圖片加載超時:${e.message}")checkImageDownloadError(userInfo)} catch (e: Exception) {YYLogUtils.e("注冊人臉服務-圖片加載錯誤:${e.message}")checkImageDownloadError(userInfo)}}

這也是比較方便的一種方案。

后記

如果是網絡請求,不管是接口的Http或者是Glide的圖片加載,我們可以使用OkHttp加載,可以設置 OkHttpClient 的 Timeout 屬性來設置超時。

如果是其他的異步操作,我們也可以使用協程的 timeout 函數直接在上層超時取消協程,也能達到目的。

兩種方法都是可以的,我個人是選擇了協程 timeout 的方式,因為我發現有些情況下就算設置 OkHttp 的超時,偶爾還是會長時間超時。如網絡連接較慢或不穩定,如服務端沒有及時響應或響應時間過長,那么超時機制將無法起作用。所以為了保險起見還是使用協程 timeout 直接上層處理了,更新之后目前運行狀況良好。

Android 學習筆錄

Android 性能優化篇:https://qr18.cn/FVlo89
Android 車載篇:https://qr18.cn/F05ZCM
Android 逆向安全學習筆記:https://qr18.cn/CQ5TcL
Android Framework底層原理篇:https://qr18.cn/AQpN4J
Android 音視頻篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(內含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源碼解析筆記:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知識體:https://qr18.cn/CyxarU
Android 核心筆記:https://qr21.cn/CaZQLo
Android 往年面試題錦:https://qr18.cn/CKV8OZ
2023年最新Android 面試題集:https://qr18.cn/CgxrRy
Android 車載開發崗位面試習題:https://qr18.cn/FTlyCJ
音視頻面試題錦:https://qr18.cn/AcV6Ap

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/39757.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/39757.shtml
英文地址,請注明出處:http://en.pswp.cn/news/39757.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

3.微服務概述

1.大型網絡架構變遷 SOA與微服務最大的差別就是服務拆分的細度&#xff0c;目前大多數微服務實際上是SOA架構&#xff0c;真正的微服務應該是一個接口對應一個服務器&#xff0c;開發速度快、成本高&#xff1b; 微服務SOA能拆分的就拆分是整體的&#xff0c;服務能放一起的都…

自動駕駛HMI產品技術方案

版本變更 序號 日期 變更內容 編制人 審核人 文檔版本 1 2 1.

【計算機網絡】13、ARP 包:廣播自己的 mac 地址和 ip

機器啟動時&#xff0c;會向外廣播自己的 mac 地址和 ip 地址&#xff0c;這個即稱為 arp 協議。范圍是未經過路由器的部分&#xff0c;如下圖的藍色部分&#xff0c;范圍內的設備都會在本地記錄 mac 和 ip 的綁定信息&#xff0c;若有重復則覆蓋更新&#xff08;例如先收到 ma…

【Spring】深入理解 Spring 事務及其傳播機制

文章目錄 一、Spring 事務是什么二、Spring 中事務的實現方法2.1 Spring 編程式事務&#xff08;手動&#xff09;2.1.1 編程式事務的使用演示2.1.2 編程式事務存在的問題 2.2 Spring 聲明式事務&#xff08;自動&#xff09;2.2.1 Transactional 作用范圍2.2.2 Transactional …

騰訊云GPU服務器GN7實例NVIDIA T4 GPU卡

騰訊云GPU服務器GN7實例搭載1顆 NVIDIA T4 GPU&#xff0c;8核32G配置&#xff0c;系統盤為100G 高性能云硬盤&#xff0c;自帶5M公網帶寬&#xff0c;系統鏡像可選Linux和Windows&#xff0c;地域可選廣州/上海/北京/新加坡/南京/重慶/成都/首爾/中國香港/德國/東京/曼谷/硅谷…

安卓純代碼布局開發游戲二:Android Studio開發環境搭建

1.Android Studio下載&#xff1a; Download Android Studio & App Tools - Android Developers 2.安裝 安裝過程非常簡單&#xff0c;找到下載包&#xff0c;一直點Next即可。 3.下載Android SDK 第一次進入Android Studio默認會先下載Android SDK,筆者下載的Android SDK存…

零售行業供應鏈管理核心KPI指標(三)

完美訂單滿足率和退貨率 完美訂單滿足率有三個方面的因素影響&#xff1a;訂單按時、足量、無損交貨。通常情況下零售企業追求線上訂單履行周期慢慢達到行業平均水平&#xff0c;就是交付的速度變快了&#xff0c;這個肯定是一件好事情&#xff0c;趨勢越來越好。 同時&#…

歐拉公式

文章目錄 歐拉公式e歐拉恒等式歐拉公式歐拉公式 推導2步驟1: 使用泰勒級數展開步驟2: 將 i x i x ix 代入 e x e^x ex 復平面上推導歐拉公式步驟1&#xff1a;復平面上的復數表示步驟2&#xff1a;定義復數的指數形式步驟3&#xff1a;求導步驟4&#xff1a;連接兩種形式步驟…

ubuntu安裝opencv4

apt 安裝 sudo apt install libopencv-dev python3-opencvpkg-config查看安裝 sudo apt install pkg-configpkg-config --modversion opencv4pkg-config --libs --cflags opencv4參考 如何在 Ubuntu 20.04 上安裝 OpenCV pkg-config 詳解

spark yarn 開啟動態資源分配

概念 不需要指定并發&#xff0c;只需要指定內存&#xff0c; 程序在運行后會動態調節并發數量&#xff0c;我們只需要設置一個上線即可 在spark 配置文件設置&#xff1a; spark.dynamicAllocation.enabled true spark.shuffle.service.enabled true 準備shuffer jar 將spar…

星際爭霸之小霸王之小蜜蜂(一)

目錄 前言 一、安裝pygame庫 1、pygame庫簡介 2、在windows系統安裝pygame庫 二 、搭建游戲框架 1、創建游戲窗口 2、改變窗口顏色 總結 前言 大家應該都看過或者都聽說過python神書“大蟒蛇”&#xff0c;上面有一個案例是《外星人入侵》&#xff0c;游戲介紹讓我想起了上…

炫酷UI前端效果的CSS生成工具

提升設計人員和前端開發人員的工作 推薦炫酷UI前端效果的CSS生成工具1.Neumorphism2.帶有漸變的圖標3.Interactions4.大型數據庫5.動畫6.Mask7.動畫按鈕8. 自定義形狀分隔線9.背景圖案10. SVG波浪推薦炫酷UI前端效果的CSS生成工具 1.Neumorphism 地址:https://neumorphism.i…

【Nginx17】Nginx學習:目錄索引、字符集與瀏覽器判斷模塊

Nginx學習&#xff1a;目錄索引、字符集與瀏覽器判斷模塊 今天要學習的內容有幾個還是大家比較常見的&#xff0c;所以學習起來也不會特別費勁。對于目錄的默認頁設置大家都不會陌生&#xff0c;字符集的設置也比較常見&#xff0c;而瀏覽器的判斷這一塊&#xff0c;可能有同學…

深入源碼分析kubernetes informer機制(二)Reflector

[閱讀指南] 這是該系列第二篇 基于kubernetes 1.27 stage版本 為了方便閱讀&#xff0c;后續所有代碼均省略了錯誤處理及與關注邏輯無關的部分。 文章目錄 Reflector是什么整體結構工作流程list拉取數據緩存resync操作watch監聽操作 總結 Reflector是什么 reflector在informer…

RocketMQ雙主雙從同步集群部署

&#x1f388; 作者&#xff1a;互聯網-小啊宇 &#x1f388; 簡介&#xff1a; CSDN 運維領域創作者、阿里云專家博主。目前從事 Kubernetes運維相關工作&#xff0c;擅長Linux系統運維、開源監控軟件維護、Kubernetes容器技術、CI/CD持續集成、自動化運維、開源軟件部署維護…

學習筆記十九:Pod常見的狀態和重啟策略

Pod常見的狀態和重啟策略 常見的pod狀態第一階段&#xff1a;第二階段&#xff1a;擴展&#xff1a; pod重啟策略測試Always重啟策略正常停止容器內的tomcat服務非正常停止容器里的tomcat服務 測試never重啟策略正常停止容器里的tomcat服務非正常停止容器里的tomcat服務 測試On…

Mac安裝opencv后無法導入cv2的解決方法

前提條件&#xff1a;以下兩個插件安裝成功 pip install opencv-python pip install --user opencv-contrib-python 注&#xff1a;直接用pip install opencv-contrib-python如果報錯&#xff0c;就加上“–user" 第一步&#xff1a; 設置–添加python解釋器 第二步&am…

go語言惡意代碼檢測系統--對接前端可視化與算法檢測部分

Malware Detect System 1 產品介紹 惡意代碼檢測系統。 2 產品描述 2.1 產品功能 功能點詳細描述注冊賬號未注冊用戶注冊成為產品用戶&#xff0c;從而具備享有產品各項服務的資格登錄賬號用戶登錄產品&#xff0c;獲得產品提供的各項服務上傳惡意樣本用戶可以將上傳自己的…

uniapp微信小程序消息訂閱快速上手

一、微信公眾平臺小程序開通消息訂閱并設置模板 這邊的模板id和詳細內容后續前后端需要使用 二、uniapp前端 需要是一個button觸發 js&#xff1a; wx.getSetting({success(res){console.log(res)if(res.authSetting[scope.subscribeMessage]){// 業務邏輯}else{uni.request…

智安網絡|深入比較:Sass系統與源碼系統的差異及選擇指南

隨著前端開發的快速發展&#xff0c;開發人員需要使用更高效和靈活的工具來處理樣式表。在這個領域&#xff0c;Sass系統和源碼系統是兩個備受關注的選項。 Sass系統 Sass&#xff08;Syntactically Awesome Style Sheets&#xff09;是一種CSS預處理器&#xff0c;它擴展了CS…