【Android】使用PackageManager讀取APK ICON時候的大坑
先來看一段常用的讀取APK Icon的程式碼
public Drawable parseApkIcon(Context context, String apkFilePath){
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(apkFilePath, 0);
if(packageInfo == null){
return null;
}
packageInfo.applicationInfo.sourceDir = apkFilePath;
packageInfo.applicationInfo.publicSourceDir = apkFilePath;
Drawable iconDrawable = packageInfo.applicationInfo.loadIcon(packageManager);
return iconDrawable;
}
上述程式碼表面看沒啥問題能夠順利的讀取到APK檔案的ICON,但是PackageManager卻隱藏了一個細節,就是當無法讀取到APK的Icon的時候PackageManager就會返回了一個公用的預設綠色機器人圖示(相信凡是做Android應用開發的都見過這個圖示)。
如果我們不考慮Bitmap回收的話這個是沒有一點問題的(我們可能不考慮嗎),當一旦考慮到Bitmap回收就會出問題,例如如下場景:
我們的應用要掃描所有本地APK檔案,然後用列表顯示給使用者,列表中顯示APP的名稱和圖示。
列表滾動的時候我們會依次啟動非同步任務去讀取APK的圖示,如果在這個列表中有多個APK都無法順利讀取到圖示,那麼這多個任務最終拿到的就會是同一張圖片(不同的BitmapDrawable卻指向同一個Bitmap)。
接下來在繼續滾動的過程中如果其中某個請求由於某些原因取消了並且回收了它拿到的Bitmap,那麼其它請求的Bitmap就也被回收了,但是其它請求並不知道自己拿到的Bitmap已經被回收了,解析來其它請求在顯示圖片的時候就會崩潰。
解決這個問題的關鍵點就是怎麼判斷拿到的icon是不是公用的預設圖片,是的話就不要了。
通過檢視loadIcon方法的原始碼我們發現,當讀不到APK的icon的時候會通過packageManager.getDefaultActivityIcon()獲取公用的默圖示並返回,這下就好辦了,我們只需比較一下就可以了,修改後的程式碼如下:
public Drawable parseApkIcon(Context context, String apkFilePath){
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(apkFilePath, 0);
if(packageInfo == null){
return null;
}
packageInfo.applicationInfo.sourceDir = apkFilePath;
packageInfo.applicationInfo.publicSourceDir = apkFilePath;
Drawable iconDrawable = packageInfo.applicationInfo.loadIcon(packageManager);
if(iconDrawable == null){
return null;
}
if(iconDrawable instanceof BitmapDrawable && ((BitmapDrawable) iconDrawable).getBitmap() == ((BitmapDrawable) packageManager.getDefaultActivityIcon()).getBitmap()){
return null;
}
return iconDrawable;
}
過濾了預設的圖示問題就結束了嗎?當然不!
首先PackageManager會快取apk icon ,然後ActivityThread持有一個ResourcesManager,ResourcesManager會快取Resources,而Resources又會快取Drawable,於是乎在重重快取之下即使我們拿到的是正常的ape icon 我們也不能回收它,因為你下次拿到的很可能是被你已回收的Drawable。
那麼最終的解決辦法就是拿到drawable後直接在本地建立快取檔案,將icon快取到本地,下次就從本地讀取,然後drawable用完就不管了,不做任何處理。
相關文章
- 【Android PackageManager】使用PackageManager讀取APK ICON時候的大坑AndroidPackageAPK
- Android中獲取應用程式(包)的資訊-----PackageManager的使用(一)AndroidPackage
- Android中獲取應用程式(包)的大小-----PackageManager的使用(二)AndroidPackage
- android 在擷取指定View的時候坑AndroidView
- Andriod PackageManager使用Package
- nomount的時候是必須讀取pfile或者spfile檔案的。
- APK瘦身-是時候給App進行減負了APKAPP
- android 反編譯APK取原始碼。Android編譯APK原始碼
- 使用Android studio建立apkAndroidAPK
- Android NFC的初次使用——公交卡資訊讀取Android
- # 2019年是時候使用svg-sprite作為網站icon圖示的主流了!!!SVG網站
- Entrust 在使用 Redis 做快取引擎的時候,快取不更新的問題RustRedis快取
- Dockerfile生成映象的時候是如何快取的?Docker快取
- 在讀取csv檔案時候,數字中帶有逗號,如 ‘1,231’,表示1231,但是讀取時候,python不認怎麼辦?Python
- 是時候 Get 新技能了:使用 Java 爬取網頁資訊Java網頁
- 【Android APK】解析SD卡上的APK檔案AndroidAPKSD卡
- android 使用ANT批量打包apk步驟AndroidAPK
- Android中的icon適配Android
- Android Apk 打包AndroidAPK
- 使用jsp:useBean的時候出錯!JSBean
- DNS預讀取的使用DNS
- Android使用Ant進行apk多渠道打包AndroidAPK
- request.getParameter("name")什麼時候獲取的引數是null,什麼時候為""空字串Null字串
- rman備份的時候讀取v$session_longops失敗導致備份失敗SessionGo
- 讀取的img的格式是uint8什麼時候轉化為float32是合理的?UI
- 到底什麼時候使用mqMQ
- Android讀取配置檔案的方法Android
- thinkphp自帶Page類使用時候setconfig() name=last的時候不生效PHPAST
- Android APK打包流程AndroidAPK
- 「Android」 APK瘦身探索AndroidAPK
- [求助]Laravel使用presence的時候,Unable to join channelLaravel
- rman恢復的時候可以使用LASTSCNAST
- Android探索之旅 | 用ADB獲取應用APK名AndroidAPK
- react-native Android使用阿里icon font圖示ReactAndroid阿里
- 使用flask的時候遇到的問題及其解答Flask
- android apk巢狀 從一個apk啟動另外一個apkAndroidAPK巢狀
- Android中處理Touch Icon的方案Android
- Android開發:APK的反編譯(獲取程式碼和資原始檔)AndroidAPK編譯