如何在 Android App 上高效顯示點陣圖
本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃!
為了建立具有視覺魅力的app,顯示影像是必須的。學會在你的Android app上高效地顯示點陣圖,而不是放棄效能。
在Android上顯示影像的痛苦
當工作於開發視覺魅力的app時,顯示影像是必須的。問題是,Android作業系統不能很好地處理影像解碼,從而迫使開發者要小心某些任務以避免搞亂效能。
Google寫了一個有關於高效顯示點陣圖的完整指南,我們可以按照這個指南來理解和解決在顯示點陣圖時Android作業系統的主要缺陷。
Android app效能殺手
按照Google的指南,我們可以列出一些我們在Android apps上顯示影像時遇到的主要問題。
降低影像取樣率
無論檢視大小,Android總是解碼並全尺寸/大小顯示影像。因為這個原因,所以如果你試圖載入一個大影像,那就很容易使你的裝置出現outOfMemoryError。
為了避免這種情況,正如Google所說的那樣,我們應該使用BitmapFactory
解碼影像,為inSampleSize
引數設定一個值。圖象尺寸由inSampleSize劃分,減少儲存器的使用量。
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
你可以手動設定inSampleSize,或使用顯示器的尺寸計算。
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
非同步解碼
即使在使用BitmapFactory時,影像解碼在UI執行緒上完成。這可以凍結app,並導致ANR(“Application Not Responding應用程式沒有響應”)警報。
這個容易解決,你只需要將解碼過程放到工作執行緒上。一種方法是使用非同步任務,正如Google指導中解釋的那樣:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private int data = 0; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference<ImageView>(imageView); } // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { data = params[0]; return decodeSampledBitmapFromResource(getResources(), data, 100, 100)); } // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } }
影像快取
每當對影像進行解碼並放置在一個檢視中的時候,Android作業系統預設重複整個渲染過程,浪費了寶貴的裝置儲存器。如果你打算在不同的地方展示相同的影像,或因為app生命週期或行為要多次重新載入,那麼這可能會特別煩人。
為了避免佔用過多的記憶體,推薦使用記憶體和磁碟快取。接下來,我們將看到這些快取之間的主要區別,以及為什麼同時使用兩者有用的原因。程式碼在這裡顯示的話太複雜了,所以請自行參閱Google指南的點陣圖快取部分以瞭解如何實現記憶體和磁碟的快取。
- 記憶體快取:影像儲存在裝置記憶體中。記憶體訪問快速。事實上,比影像解碼過程要快得多,所以將影像儲存在這裡是讓app更快更穩定的一個好主意。記憶體快取的唯一缺點是,它只存活於app的生命週期,這意味著一旦app被Android作業系統記憶體管理器關閉或殺死(全部或部分),那麼儲存在那裡的所有影像都將丟失。請記住,記憶體快取必須設定一個最大可用的記憶體量。否則可能會導致臭名昭著的outOfMemoryError。
- 磁碟快取:影像儲存在裝置的物理儲存器上(磁碟)。磁碟快取可以一直存活於app啟動期間,安全地儲存圖片,只要有足夠的空間。缺點是,磁碟讀取和寫入操作可能會很慢,而且總是比訪問記憶體快取慢。由於這個原因,因此所有的磁碟操作必須在工作執行緒執行,UI執行緒之外。否則,app會凍結,並導致ANR警報。
每個快取都有其優點和缺點,因此最好的做法是兩者皆用,並從首先可用的地方讀取,通過記憶體快取開始。
最後的思考以及EpicBitmapRenderer
不知道你有沒有注意到,正如我在本文開頭所述,在Android app上顯示圖片真的很讓人頭疼。絕非看上去那麼簡單。
為了避免在每個專案中重複這些任務,我開發了一個100%免費又開源的Android庫,EpicBitmapRenderer
。你可以在EpicBitmapRenderer GitHub repo選擇它,或在EpicBitmapRenderer網站了解更多。
EpicBitmapRenderer
易於使用,並在每個影像解碼操作中自動化了所有這些惱人的任務,這樣你就可以專注於app開發。
你只需要新增增加EpicBitmapRenderer
依賴在你的Gradle上(檢視其他構建工具的替代品,看看EpicBitmapRenderer文件的匯入庫部分)。
compile 'com.isaacrf.epicbitmaprenderer:epicbitmaprenderer:1.0'
在EpicBitmapRenderer
中解碼影像是很容易的:只需要呼叫所需的解碼方法並管理結果。看看下面這個例子,我們從URL獲取圖片並顯示於ImageVIew上。
//Sample 3: Decode Bitmap from URL (Async) EpicBitmapRenderer.decodeBitmapFromUrl( "http://isaacrf.com/wp-content/themes/Workality-Lite-child/images/IsaacRF.png", 200, 200, new OnBitmapRendered() { @Override public void onBitmapRendered(Bitmap bitmap) { //Display rendered Bitmap when ready ImageView imgView = findViewById(R.id.imgSampleDecodeUrl); imgView.setImageBitmap(bitmap); } }, new OnBitmapRenderFailed() { @Override public void onBitmapRenderFailed(Exception e) { //Take actions if Bitmap fails to render Toast.makeText(MainActivity.this, "Failed to load Bitmap from URL", Toast.LENGTH_SHORT).show(); } });
許可證
這篇文章以及任何相關的原始碼和檔案,遵循 The Creative Commons Attribution-Share Alike 3.0 Unported License的授權許可。
譯文連結:http://www.codeceo.com/article/android-app-display-bitmaps.html
英文原文:Displaying Bitmaps Efficiently on Android Apps
翻譯作者:碼農網 – 小峰
[ 轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]
相關文章
- 小技巧:如何在Mac選單欄上顯示名字或者圖示Mac
- Activity背景顯示app圖示APP
- iOS開發如何在google地圖上顯示自己的位置iOSGo地圖
- Android系統耳機圖示顯示Android
- Android用ImageView顯示本地和網上的圖片AndroidView
- 用WindowedMode顯示點陣圖圖象(轉)
- CoordinatorLayout、AppBarLayout實現上滑隱藏圖片,下滑顯示圖片APP
- 微信小程式image載入成功前顯示預設佔點陣圖微信小程式
- IOS APP安裝後不在桌面顯示圖示、改變圖示等iOSAPP
- 如何在Mac上快速顯示桌面以便快速訪問Mac
- echarts如何在每個柱狀圖上都顯示氣泡詳解(好看的柱狀圖)Echarts
- Android圖形顯示系統(一)Android
- Android Bitmap(點陣圖)詳解Android
- 如何在 Android 應用中使用 FontAwesome 圖示Android
- iOS開發 如何在Label中顯示圖片-圖文混排iOS
- opencv圖片上如何顯示兩個小圖片OpenCV
- Android之在app圖示新增角標AndroidAPP
- Pock 讓macOS 的App 圖示顯示在Touch Bar 上,讓你快速一指切換MacAPP
- 設計好的報表是如何在 web 上顯示的Web
- 如何在蘋果Mac上顯示或隱藏“節假日”日曆?蘋果Mac
- Android 防止多次點選,Toast重複顯示AndroidAST
- win10系統如何在桌面顯示我的電腦等圖示Win10
- App在iOS 11下真機顯示正常模擬器上顯示有問題APPiOS
- springmvc中圖片回顯返回位元組流陣列顯示圖片完整案例SpringMVC陣列
- 獲取手機上 APP 或者 APK 圖示APPAPK
- psd圖片不能在網頁上顯示網頁
- django 實現圖片上傳和顯示操作Django
- 顯示地圖地圖
- 點陣圖
- 在visual studio裡圖形化顯示陣列陣列
- android的sd卡上建立目錄不顯示AndroidSD卡
- Android 開源圖片裁剪工具、圖片顯示工具分享Android
- 如何在GitHub上大顯身手?Github
- Android 12(S) 圖形顯示系統 - 開篇Android
- Android中顯示html標籤或者帶圖片AndroidHTML
- win7圖片只顯示圖示不顯示預覽圖解決方案Win7圖解
- vue3實現模擬地圖上,站點名稱按需顯示的功能Vue地圖
- Android實現二值點陣圖識別Android