摘要:適配的原則是優(yōu)先黑名單和深灰名單,淺灰名單在官方未有替代之前可以暫時(shí)不適配,在上運(yùn)行也不會(huì)有任何問(wèn)題。除上面兩種適配方式外,音樂(lè)目前采用了另外一種方式。隱私安全保護(hù)標(biāo)識(shí)修改在中,對(duì)隱私保護(hù)又做了更加嚴(yán)格的要求。
歡迎大家前往騰訊云+社區(qū),獲取更多騰訊海量技術(shù)實(shí)踐干貨哦~
本文由QQ音樂(lè)技術(shù)團(tuán)隊(duì)發(fā)表于云+社區(qū)專(zhuān)欄上篇:Android P 行為變更適配
Android P 這次有很多行為變更,其中不乏一些需要亟需適配的變更。
一、全面屏檢測(cè)在 Android 8.0 時(shí)代各個(gè)手機(jī)廠商就開(kāi)始發(fā)布自己的全面屏手機(jī),但是此時(shí) Android 官方并未支持到該功能,所以各個(gè)廠商都各自實(shí)現(xiàn)了一套全面屏判斷邏輯,對(duì)于開(kāi)發(fā)者來(lái)說(shuō)甚是麻煩。終于在 Android P 里官方收歸了該功能的判斷邏輯,Android P 和之后的版本完全可以使用官方 API 來(lái)判斷全面屏,當(dāng)然前提是第三方廠商按照 google 官方接口去實(shí)現(xiàn)。Android P 版本判斷全面屏代碼很簡(jiǎn)單,但是在適配過(guò)程中你可能會(huì)在網(wǎng)上發(fā)現(xiàn)如下判斷代碼:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { decorView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { @RequiresApi(api = 28) @Override public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { if (windowInsets != null) { DisplayCutout cutout = windowInsets.getDisplayCutout(); if (cutout != null) { Listrects = cutout.getBoundingRects(); //通過(guò)判斷是否存在rects來(lái)確定是否全面屏手機(jī) if (rects != null && rects.size() > 0) { isNotchScreen = true; } } } return windowInsets; } }); }
這段代碼確實(shí)可以判斷出全面屏與否,但是會(huì)造成一個(gè)很?chē)?yán)重的后果,就是在某些手機(jī)(pixel 和 vivo x21 均出現(xiàn)該情況)上底部導(dǎo)航欄會(huì)透明,導(dǎo)致應(yīng)用內(nèi)容會(huì)透到導(dǎo)航欄從而被遮擋,大大影響內(nèi)容展示。最后經(jīng)過(guò)仔細(xì)排查發(fā)現(xiàn)僅僅因?yàn)樵谏厦婺嵌未a中調(diào)用了 setOnApplyWindowInsetsListener 函數(shù),該函數(shù)在 Android 官網(wǎng)有詳細(xì)介紹,是用來(lái)在 Android 21 版本之后代替 fitSystemWindows 函數(shù),目的是讓 View 根據(jù) Window 的縮進(jìn)進(jìn)行相應(yīng)處理,調(diào)用后會(huì)影響系統(tǒng)狀態(tài)欄和導(dǎo)航欄對(duì)應(yīng)用內(nèi)容的展示,對(duì)此的介紹資料網(wǎng)上有很多,就不贅述了。真正完美判斷全面屏的代碼如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { WindowInsets windowInsets = decorView.getRootWindowInsets(); if (windowInsets != null) { DisplayCutout displayCutout = windowInsets.getDisplayCutout(); if (displayCutout != null) { List二、非 SDK API 適配詳解 2.1 非 SDK API 名單介紹rects = displayCutout.getBoundingRects(); //通過(guò)判斷是否存在rects來(lái)確定是否劉海屏手機(jī) if (rects != null && rects.size() > 0) { isNotchScreen = true; } } } }
Android P 版本最大最嚴(yán)格的特性變更應(yīng)該非 SDK 接口限制莫屬了。對(duì)于非 SDK API 里面的部分名單來(lái)說(shuō),就算在不修改 targetSdkVersion 的前提下,不管是直接、反射還是通過(guò) JNI 調(diào)用都會(huì)造成調(diào)用失敗、拋出 NoSuchFieldException或 NoSuchMethodException 等嚴(yán)重后果,該行為影響范圍波及所有調(diào)用此接口的應(yīng)用。
非 SDK API 名單總共分為三類(lèi):light grey list (淺灰名單)、dark grey list (深灰名單)、dark list(黑名單),詳情:
2.2 非 SDK API 名單掃描
所以對(duì)于我們應(yīng)用開(kāi)發(fā)者來(lái)說(shuō),當(dāng)前首要任務(wù)是適配深灰名單和黑名單。目前 google 官方提供了一個(gè)可以實(shí)時(shí)查詢(xún)?nèi)齻€(gè)名單里面 API 列表的網(wǎng)站:https://android.googlesource....。在之前 DP 版本時(shí)開(kāi)發(fā)者如果遇到了不得不使用的黑名單或者深灰名單 API,需要向 google 官方及時(shí)提出反饋(反饋url:https://issuetracker.google.c...),申請(qǐng)將其移動(dòng)到淺灰名單中,但是目前正式版本已經(jīng)發(fā)布,未得知該申請(qǐng)通道是否仍有效。
詳細(xì)了解了非 SDK API 之后,下一步當(dāng)然是將應(yīng)用代碼里面的深灰名單和黑名單 API 調(diào)用找出來(lái)一一修改。目前官方提供了一個(gè)非常實(shí)用的掃描工具,該工具可以把應(yīng)用里面三個(gè)類(lèi)型名單的 API 調(diào)用都掃描出來(lái)(但是可能會(huì)有遺漏),使用方法也很簡(jiǎn)單:
打包一個(gè)應(yīng)用 APK,建議使用 release 包,排除一些未使用到的單元測(cè)試類(lèi)或者其他因素的影響,將 APK 放到工具指定目錄下;
執(zhí)行命令 ./appcompat.sh --dex-file=test.apk,在終端上會(huì)輸出三個(gè)名單每個(gè) API 的詳細(xì)調(diào)用處: #1: Linking dark greylist Landroid/os/SystemProperties;->get(Ljava/lang/String;)Ljava/lang/String; use(s): Ltmsdkobf/gv;->a(Ljava/lang/String;)Ljava/lang/String; #2: Linking dark greylist Landroid/os/SystemProperties;->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; use(s): Ltmsdkobf/gp;->b(Landroid/content/Context;)Ljava/lang/String; ....
2.3 非 SDK API 適配經(jīng)過(guò)上一步掃描出應(yīng)用內(nèi)非 SDK API 調(diào)用之后,接下來(lái)就可以直接開(kāi)始適配。適配的原則是優(yōu)先黑名單和深灰名單,淺灰名單在官方未有替代 API 之前可以暫時(shí)不適配,在 Android P 上運(yùn)行也不會(huì)有任何問(wèn)題。掃描完成之后,不出意外大家應(yīng)該會(huì)有三類(lèi)需要適配的 API 調(diào)用:
應(yīng)用代碼本身調(diào)用到了非 SDK API 接口; 針對(duì)應(yīng)用代碼本身調(diào)用到了非 SDK API 接口,用的比較頻繁的例如 SystemProperties.get,就需要去尋找另外一個(gè)可以替代的合法 API,如果找不到就只能認(rèn)為該 API 調(diào)用失敗從而走失敗邏輯,如果實(shí)在必須要用到該 API 就盡早去向 google 申請(qǐng)移動(dòng)到淺灰名單中。
第三方庫(kù)調(diào)用到了非 SDK API 接口; 針對(duì)第三方庫(kù)調(diào)用到了非 SDK API 接口,解決辦法當(dāng)然是直接查詢(xún)相關(guān)資料或者聯(lián)系庫(kù)提供方,確認(rèn)是否有適配 Android P 新版本的 SDK。還有需要提到的一點(diǎn),就算更換適配完成的第三方 SDK 后,仍然可能會(huì)在同一地方掃描出非 SDK API 的調(diào)用,這是因?yàn)檫m配工程師只是在調(diào)用處加了一個(gè) try-catch 保護(hù)邏輯,雖然這樣也勉強(qiáng)叫做適配完成,但是還是強(qiáng)烈建議大家使用如下的適配方式: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { // Android P or above } else { // below Android P } 嚴(yán)格按照上面的適配方案,掃描工具就不會(huì)再掃描出此處的非 SDK API 調(diào)用,我們也無(wú)需每次都去確認(rèn)所有非 SDK API 調(diào)用處都加了保護(hù)邏輯。 當(dāng)然如果第三方庫(kù)沒(méi)有適配也沒(méi)有近期適配的意向,目前有兩種方法:第一種是屏蔽入口;第二種是反編譯 SDK,在關(guān)鍵地方加上適配代碼;
Android 官方庫(kù)調(diào)用到了非 SDK API 接口; 沒(méi)錯(cuò)!Android 官方庫(kù)也會(huì)被掃描出非 SDK API 調(diào)用,針對(duì)這種情況,需要分情況討論:
該 API 調(diào)用查看 v7 support 包源碼可以發(fā)現(xiàn)已經(jīng)被 try-catch 住了,測(cè)試了相關(guān)類(lèi)也可以正常運(yùn)行,而且在適配過(guò)程中升級(jí) rc 版本的 support-v7 包會(huì)導(dǎo)致應(yīng)用編譯不過(guò),所以目前 QQ 音樂(lè)暫時(shí)認(rèn)定無(wú)需升級(jí)到最新版本的 support-v7。除上面介紹的特殊情況之外還是建議更換最新版本的官方 SDK。
三、電源管理改進(jìn) 3.1 應(yīng)用待機(jī)群組Android P 上對(duì)電源管理又做了一系列的改進(jìn)措施,不管應(yīng)用 targetApi 版本是否已經(jīng)升級(jí)到 P,系統(tǒng)都會(huì)依據(jù)應(yīng)用最近的使用時(shí)間和頻率來(lái)給應(yīng)用進(jìn)行待機(jī)分組,然后根據(jù)應(yīng)用所屬群組限制應(yīng)用可以訪(fǎng)問(wèn)的資源,目前總共有五類(lèi)分組:
活躍: 一般為正在使用或者在前臺(tái)運(yùn)行的應(yīng)用,例如:
應(yīng)用啟動(dòng)一個(gè) Activity;
應(yīng)用正在運(yùn)行前臺(tái) Service;
應(yīng)用的同步適配器關(guān)聯(lián)上了一個(gè)前臺(tái)應(yīng)用;
用戶(hù)點(diǎn)擊了應(yīng)用的一個(gè)通知; 系統(tǒng)不會(huì)對(duì)該類(lèi)應(yīng)用有任何的限制;
工作集: 應(yīng)用經(jīng)常運(yùn)行,但是當(dāng)前未屬于活躍狀態(tài)就會(huì)被歸屬于工作集,該群組的應(yīng)用在運(yùn)行作業(yè)和觸發(fā)鬧鐘方面會(huì)被施加輕度的限制;
常用: 應(yīng)用如果被定期使用,但不是每天的話(huà)就會(huì)被歸到該工作群組。該群組的應(yīng)用在運(yùn)行作業(yè)和觸發(fā)鬧鐘方面會(huì)被施加較強(qiáng)的限制,F(xiàn)CM 消息數(shù)量也會(huì)有相關(guān)限制;
極少使用: 應(yīng)用如果不經(jīng)常使用就會(huì)被歸到該工作群組,系統(tǒng)會(huì)對(duì)該群組應(yīng)用運(yùn)行作業(yè)、觸發(fā)鬧鐘和接收高優(yōu)先級(jí)別 FCM 的消息能力方面有嚴(yán)格的限制;
從未使用: 安裝但從未被使用過(guò)的應(yīng)用會(huì)被歸到該工作群組,該工作群組的應(yīng)用會(huì)被施加極其嚴(yán)格的限制;
更加詳細(xì)的表述可以參考官網(wǎng):App Standby Buckets(https://developer.android.com...,不同群組的限制的詳細(xì)表現(xiàn)見(jiàn):Power management restrictions(鏈接:https://developer.android.com...)。系統(tǒng)會(huì)動(dòng)態(tài)的將手機(jī)里面的應(yīng)用分配到這五類(lèi)群組里面,也會(huì)根據(jù)需要變化應(yīng)用群組,同時(shí)借助了機(jī)器學(xué)習(xí)來(lái)將一個(gè)應(yīng)用放到更合適的群組里。目前應(yīng)用可以通過(guò) UsageStatsManager.getAppStandbyBucket() 函數(shù)來(lái)獲取當(dāng)前所屬的應(yīng)用群組,借助這個(gè)結(jié)果來(lái)更好的提升自己的打開(kāi)頻率,同時(shí)可以借助此來(lái)模擬處于不同群組能否正常工作。另外,位于低電耗模式白名單中的應(yīng)用不適用基于應(yīng)用待機(jī)群組的限制。
3.2 省電模式改進(jìn)Android 9 對(duì)省電模式又做了很多改進(jìn),開(kāi)啟省電模式之后會(huì)有如下限制:
系統(tǒng)會(huì)更加積極的將應(yīng)用置于待機(jī)模式,不管應(yīng)用是否空閑;
后臺(tái)執(zhí)行限制將適用于所有應(yīng)用,無(wú)論他們的 targetApi 是多少;
屏幕關(guān)閉時(shí),位置服務(wù)可能被停用;
后臺(tái)應(yīng)用沒(méi)有網(wǎng)絡(luò)訪(fǎng)問(wèn)權(quán)限;
這里需要重點(diǎn)介紹一下后臺(tái)執(zhí)行限制,該限制于 Android O 版本引入,主要是為了優(yōu)化 Android 在多應(yīng)用多服務(wù)運(yùn)行時(shí),系統(tǒng)負(fù)載過(guò)大會(huì)殺死后臺(tái)音樂(lè)播放等服務(wù)導(dǎo)致用戶(hù)體驗(yàn)下降的問(wèn)題,它默認(rèn)只對(duì) targetApi 大于等于 26 的應(yīng)用生效。目前用戶(hù)可以通過(guò)設(shè)置頁(yè)面對(duì)任意應(yīng)用施加后臺(tái)執(zhí)行限制,后臺(tái)執(zhí)行限制會(huì)對(duì)應(yīng)用有兩方面的影響:
后臺(tái)服務(wù)限制: 處于前臺(tái)(可見(jiàn)、具有前臺(tái)服務(wù)或者關(guān)聯(lián)到前臺(tái)應(yīng)用)或臨時(shí)白名單(處理高優(yōu)先級(jí) FCM、接收短信等廣播或者執(zhí)行通知的 PendingIntent)時(shí),應(yīng)用可以自由創(chuàng)建和運(yùn)行前臺(tái)與后臺(tái)服務(wù)。 進(jìn)入后臺(tái)時(shí),在一個(gè)持續(xù)數(shù)分鐘的時(shí)間窗內(nèi),應(yīng)用仍可以創(chuàng)建和使用服務(wù),但是超過(guò)該時(shí)間之后再通過(guò) startService 去啟動(dòng)一個(gè)服務(wù)就會(huì)拋出 java.lang.IllegalStateException: Not allowed to start service Intent 的錯(cuò)誤,解決辦法是使用 startForegroundService 或者 JobIntentService;
廣播限制: 針對(duì) Android O 和之上的應(yīng)用無(wú)法繼續(xù)在其清單中為隱式廣播注冊(cè)廣播接收器。
四、Apache HTTP client 相關(guān)類(lèi)找不到將 compileSdkVersion 升級(jí)到 28 之后,如果在項(xiàng)目中用到了 Apache HTTP client 的相關(guān)類(lèi),就會(huì)拋出找不到這些類(lèi)的錯(cuò)誤。這是因?yàn)楣俜揭呀?jīng)在 Android P 的啟動(dòng)類(lèi)加載器中將其移除,如果仍然需要使用 Apache HTTP client,可以在 Manifest 文件中加入:
或者也可以直接將 Apache HTTP client 的相關(guān)類(lèi)打包進(jìn) APK 中。
除上面兩種適配方式外,QQ 音樂(lè)目前采用了另外一種方式。在音樂(lè)項(xiàng)目中,我們已經(jīng)將使用 Apache HTTP client 的模塊多帶帶抽離到了一個(gè) module 中,所以暫時(shí)只需要保持 module 中的 compileSdkVersion 在 28 以下即可正常編譯運(yùn)行。
五、其余適配 4.1 前臺(tái) Service在 Android P 中,如果 targeSdkVersion 升級(jí)到 28,使用前臺(tái) Service 必須要申請(qǐng) FOREGROUND_SERVICE 權(quán)限,如果沒(méi)有申請(qǐng)?jiān)摍?quán)限,系統(tǒng)會(huì)拋出 SecurityException,該權(quán)限為普通權(quán)限,申請(qǐng)自動(dòng)授予應(yīng)用。
4.2 隱私安全保護(hù)Build.SERIAL 標(biāo)識(shí)修改:在 Android P 中,對(duì)隱私保護(hù)又做了更加嚴(yán)格的要求。在某些應(yīng)用中為了識(shí)別手機(jī)的唯一性可能會(huì)用到 Build.SERIAL 這個(gè)標(biāo)識(shí),但這個(gè)標(biāo)識(shí)在 Android P 中已經(jīng)被設(shè)置成了 UNKNOWN,所以會(huì)直接導(dǎo)致該功能出現(xiàn)異常。
多進(jìn)程 webview 信息訪(fǎng)問(wèn)限制:在 Android P 中為了提升系統(tǒng)的安全性,用戶(hù)無(wú)法在多進(jìn)程的 webview 中共享數(shù)據(jù)目錄,該目錄下存儲(chǔ)的是一些 cookies、Http 緩存和其他一些永久、臨時(shí)的緩存。當(dāng)下不少應(yīng)用會(huì)把 webview 放在另一個(gè)進(jìn)程中打開(kāi)以避免內(nèi)存泄漏,但是他們 cookies 的設(shè)置往往還是在主進(jìn)程中,所以開(kāi)發(fā)者需要仔細(xì)排查自己的應(yīng)用是否有這么使用,webview 相關(guān)運(yùn)行是否正常等。
4.3 com.android.internal 包下某些類(lèi)找不到升級(jí)到 28 之后,應(yīng)用編譯后拋出 com.android.internal 包下面有些類(lèi)找不到的異常,經(jīng)過(guò)查找發(fā)現(xiàn)這些類(lèi)已經(jīng)從 SDK 中移除。針對(duì)這種情況目前有兩種處理辦法:
移除該類(lèi)的調(diào)用邏輯;
在應(yīng)用中新建一個(gè)同名類(lèi),將被移除類(lèi)的所有代碼邏輯復(fù)制到新建類(lèi)中(必要時(shí)可能需要將被移除類(lèi)相關(guān)類(lèi)同時(shí)拷貝一份到應(yīng)用中),然后將應(yīng)用中所有相關(guān) import 引用直接修改成新建類(lèi)的包名引用即可;
下篇:Android P 實(shí)用新特性Android P 這次當(dāng)然也有很多豐富的特性,總結(jié)了兩個(gè)對(duì)于第三方應(yīng)用開(kāi)發(fā)者比較實(shí)用的特性
。
一、HEIF 圖片格式支持HEIF(High Efficiency Image Format),高幀率圖片格式,采用的是 HEVC 編碼格式。蘋(píng)果于 iOS11 版本開(kāi)始支持該圖片格式,而 Android 則是在 Android O MR1 版本開(kāi)始支持 HEIF 靜態(tài)圖的軟解碼,在 P 版本上完全支持該格式的軟編解碼。HEIF 格式的壓縮率是 JPEG 的 2.39 倍,同等大小質(zhì)量的圖片可節(jié)省 50% 的空間和網(wǎng)絡(luò)傳輸流量,而且支持動(dòng)圖。HEIF 格式比起 GIF 格式來(lái)說(shuō)有著更好的圖片展示效果,所以 HEIF 格式圖片的目標(biāo)是用來(lái)代替 JPEG 成為主流的圖片壓縮格式。HEIF 格式圖片的擴(kuò)展名為 .heif 或者 .heic:
HEIF | WebP | JPEG | |
---|---|---|---|
最大尺寸 | 無(wú)上限 | 16383x16383 | 65535x65535 |
編碼 | HEVC | VP8 | JPEG |
是否支持其他編碼 | YES | NO | NO |
支持音頻/文字 | YES | NO | NO |
支持多圖片 | YES | YES | NO |
支持裁剪 | YES | NO | NO |
支持透明 | YES | YES | NO |
支持縮略圖 | YES | NO | YES |
分塊加載 | YES | NO | NO |
看上去很美好,但是目前還不是所有的 Android P 機(jī)型都會(huì)支持 HEIF 格式硬編解碼,因?yàn)檫@需要特殊的硬件支持同時(shí)還需要繳納一定的專(zhuān)利費(fèi),所以在編解碼效率上就會(huì)有機(jī)型差異,同時(shí) Android P 軟編解碼也只能支持靜態(tài) HEIF 格式圖片。目前開(kāi)發(fā)者可以通過(guò)版本來(lái)判斷是否支持 HEIF 編解碼,判斷邏輯如下:
fun supportHEIF() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
解碼代碼也很簡(jiǎn)單,支持將 HEIF 格式圖片解碼成 Bitmap 和 Drawable:
@TargetApi(28) fun decodeHEIFDrawable(filePath: String): Drawable? { if (!supportHEIF()) { return null } var source: ImageDecoder.Source = ImageDecoder.createSource(File(filePath)) return ImageDecoder.decodeDrawable(source) } @RequiresApi(28) fun decodeHEIFBitmap(filePath: String): Bitmap? { if (!supportHEIF()) { return null } var source: ImageDecoder.Source = ImageDecoder.createSource(File(filePath)) return ImageDecoder.decodeBitmap(source) }
另外掃描本地圖片則繼續(xù)使用 ContentResolver 即可,如果設(shè)備支持 HEIF 格式,系統(tǒng)會(huì)自動(dòng)掃描上 HEIF 格式的圖片:
var cursor : Cursor = getContext().getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null)
但是這樣還遠(yuǎn)遠(yuǎn)沒(méi)有適配完成,第三方應(yīng)用適配 HEIF 格式圖片有一個(gè)很困難的地方是本地雖然可以識(shí)別解碼 HEIF 格式的圖片,但是如果某個(gè)用戶(hù)將其設(shè)置為頭像上傳到后臺(tái),后臺(tái)將其下發(fā)給其他不支持 HEIF 圖片格式解碼的手機(jī),這些手機(jī)就肯定有展示問(wèn)題。解決這個(gè)問(wèn)題目前有兩種思路:
終端在上傳之前將其轉(zhuǎn)碼成 JPEG 格式的圖片,但是這樣就根本沒(méi)有充分利用到 HEIF 圖片的高壓縮率的優(yōu)勢(shì);
在到達(dá)后端之后,后端將其轉(zhuǎn)碼成 JPEG 圖片,同時(shí)保存一份 HEIF 和 JEPG,到時(shí)候根據(jù)用戶(hù)是否可以解碼 HEIF 下發(fā)不同格式圖片。該方案可以充分利用 HEIF 的優(yōu)點(diǎn),但是大大增加了后端存儲(chǔ)空間和開(kāi)發(fā)工作量。
二、ImageDecoder上面已經(jīng)介紹到了 ImageDecoder 在解碼 HEIF 圖片中的應(yīng)用,但是實(shí)際它的功能完全不僅于此,在 Android P 中它可以完全替代 BitmapFactory 和 BitmapFactory.Options 相關(guān)類(lèi)。ImageDocoder 類(lèi)可以通過(guò)字節(jié)數(shù)據(jù)、文件和 URI 來(lái)解碼一張圖片。用法和之前一樣,首先通過(guò) createSource 方法創(chuàng)建一個(gè)圖片文件的 ImageDecoder.Source 對(duì)象,然后調(diào)用 decodeDrawable 或者 decodeBitmap 方法傳入之前的 ImageDecoder.Source 對(duì)象就能生成圖片的 Drawable 或者 Bitmap 對(duì)象引用。ImageDecoder 支持 PNG、JPEG、WEBP、GIF 和 HEIF 多種格式圖片的解碼,另外解碼 GIF 或者 WEBP 格式圖片得到的是一個(gè) AnimatedImageDrawable 對(duì)象,AnimatedImageDrawable 類(lèi)的工作原理和 AnimatedVectorDrawable 類(lèi)似,都是使用一個(gè)工作線(xiàn)程來(lái)解碼,所以解碼線(xiàn)程和顯示線(xiàn)程互不干擾。AnimatedImageDrawable 用法也很簡(jiǎn)單:
var drawable: Drawable = ImageDecoder.decodeDrawable(source); if (drawable is AnimatedImageDrawable){ image.setImageDrawable(drawable) drawable.start() }
ImageDecoder 除了基礎(chǔ)的解碼功能之外,還有很多非常實(shí)用的方法,比如通過(guò)設(shè)置 OnHeaderDecodedListener 就可以在解析圖片之前獲取到圖片的寬高等信息,同時(shí)還可以根據(jù)需要設(shè)置采樣率:
val listener = object : OnHeaderDecodedListener { fun onHeaderDecoded(decoder: ImageDecoder, info: ImageInfo, source: Source) { decoder.setTargetSampleSize(2) } } val drawable = ImageDecoder.decodeDrawable(source, listener)
另外還可以通過(guò) setPostProcessor 方法來(lái)添加一些自定義的效果,比如最常用的切圓角:
var drawable = ImageDecoder.decodeDrawable(source) { decoder, info, src -> decoder.setPostProcessor { canvas -> val path = Path() path.setFillType(Path.FillType.INVERSE_EVEN_ODD) val width = canvas.getWidth() val height = canvas.getHeight() path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW) val paint = Paint() paint.setAntiAlias(true) paint.setColor(Color.TRANSPARENT) paint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.SRC)) canvas.drawPath(path, paint) PixelFormat.TRANSLUCENT } }
非常便捷。用法遠(yuǎn)不僅于此,有了 Canvas 對(duì)象,開(kāi)發(fā)者完全可以發(fā)揮想象去實(shí)現(xiàn)自己想要的炫酷效果。另外如果解碼的圖片不完整或者包含錯(cuò)誤,一般情況下會(huì)拋出 DecodeException,但是如果這個(gè)時(shí)候通過(guò) setOnPartialImageListener 函數(shù)傳遞一個(gè) OnPartialImageListener 對(duì)象,并且在 onPartialImage 函數(shù)中返回 true,則圖片就會(huì)只展示解析成功的一部分而不會(huì)拋出 DecodeException:
var drawable = ImageDecoder.decodeDrawable(source) { decoder, info, src -> decoder.setOnPartialImageListener { e: ImageDecoder.DecodeException -> true } }引用
https://developer.android.goo... https://mp.weixin.qq.com/s/03ospQEdY5HLdYqxEiDX1g https://blog.csdn.net/GenlanF... https://developer.android.com/about/versions/pie/power https://segmentfault.com/a/11...問(wèn)答
Android - 如何修復(fù)權(quán)限異常?
相關(guān)閱讀
Android音頻系統(tǒng)
Android 基本常識(shí)
Android全局異常處理
【每日課程推薦】機(jī)器學(xué)習(xí)實(shí)戰(zhàn)!快速入門(mén)在線(xiàn)廣告業(yè)務(wù)及CTR相應(yīng)知識(shí)
此文已由作者授權(quán)騰訊云+社區(qū)發(fā)布,更多原文請(qǐng)點(diǎn)擊
搜索關(guān)注公眾號(hào)「云加社區(qū)」,第一時(shí)間獲取技術(shù)干貨,關(guān)注后回復(fù)1024 送你一份技術(shù)課程大禮包!
海量技術(shù)實(shí)踐經(jīng)驗(yàn),盡在云加社區(qū)!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98222.html
摘要:不努力不奮斗,可能就會(huì)在基層一輩子止步不前。不過(guò),只一句,如果你還在做這一行,還是一名程序猿媛,想走上坡路的你,也許我這到手的十幾家一線(xiàn)互聯(lián)網(wǎng)公司性能優(yōu)化項(xiàng)目實(shí)戰(zhàn)可能會(huì)對(duì)你有所幫助。 ...
摘要:學(xué)會(huì)提出正確的問(wèn)題數(shù)據(jù)分析的目標(biāo)并不是數(shù)據(jù)本身,而是解決問(wèn)題。當(dāng)好的數(shù)據(jù)分析師能夠同時(shí)以旁觀者和業(yè)務(wù)方的角度來(lái)提問(wèn)時(shí),會(huì)更容易定位真正的問(wèn)題所在。這些都是一個(gè)數(shù)據(jù)分析師最有效率的成長(zhǎng)方式。 經(jīng)常有小伙伴在各種渠道問(wèn)我,數(shù)據(jù)分析師怎么入門(mén)?應(yīng)該讀什么書(shū)?如何能成為被大公司認(rèn)可的數(shù)據(jù)分析師? Facebook 數(shù)據(jù)分析師鄒昕曾分享過(guò)這樣一張數(shù)據(jù)分析核心技能地圖: showImg(https...
閱讀 3501·2023-04-25 19:39
閱讀 3839·2021-11-18 13:12
閱讀 3661·2021-09-22 15:45
閱讀 2458·2021-09-22 15:32
閱讀 767·2021-09-04 16:40
閱讀 3759·2019-08-30 14:11
閱讀 1915·2019-08-30 13:46
閱讀 1592·2019-08-29 15:43