Android - Displaying images
-
Upload
matteo-bonifazi -
Category
Mobile
-
view
45 -
download
0
Transcript of Android - Displaying images
Displaying ImagesHow to manage images nicely
+RobertoOrgiu@_tiwiz
+MatteoBonifazi@mbonifazi
Pictures are faster than wordsConsider using pictures to explain ideas. They get
people's attention and can be much more efficient than words.
For media rich applications, BITMAPS are everywhere.
Handling BitmapsLoading bitmaps in app is tricky
● Bitmaps can very easily exhaust an app's memory budget.● Loading bitmaps on the UI thread can degrade your app's
performance● If your app is loading multiple bitmaps into memory, you
need to skillfully manage memory and disk caching.
Loading Large Bitmaps Efficiently
Images come in all shapes and sizes. In many cases they are larger than required for a
typical application user interface (UI).
Read bitmap dimensions and type
BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(getResources(), R.id.myimage, options);int imageHeight = options.outHeight;int imageWidth = options.outWidth;String imageType = options.outMimeType;
The BitmapFactory class provides several decoding methods (decodeByteArray(),
decodeFile(), decodeResource(), etc.) for creating a Bitmap from various sources
To avoid java.lang.OutOfMemory exceptions, check the dimensions of a
bitmap before decoding it.
Load a Scaled Down Version into Memory 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) { //Downsize of the resources
final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) >= reqHeight &&
(halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize;}
A power of two value is calculated because the decoder uses a final value by rounding down to the nearest power of two, as per
the inSampleSize documentation.
Decode the images
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);}
If set to true, the decoder will return null (no bitmap), but the out... fields will still be set,
allowing the caller to query the bitmap without having to allocate the memory for its pixels.
Load the image into the ImageView
mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Caching bitmaps
Load a new bitmap for your apps’ social media stream, or whatever, but you're out of memory.
Use a Memory CacheLRU Cache for all of us
This container keeps a list of objects, and ranks them based upon how many times they’ve been accessed. When it’s time to evict one of them (to make space for a new object) the LRUCache already knows which ones to get rid of. All done without you having to worry about any of it.
Suitable size for a LruCache Factors should be taken into consideration
● How memory intensive is the rest of your activity and/or application?
● How many images will be on-screen at once? ● How many need to be available ready to come on-screen?● What is the screen size and density of the device? ● What dimensions and configuration are the bitmaps?● How frequently will the images be accessed?
There is no ONE SOLUTION!
Avoid java.lang.OutOfMemory exceptions
Memory LRU cache
private LruCache<String, Bitmap> mMemoryCache;
protected void onCreate(Bundle savedInstanceState) { final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size will be measured in kilobytes
// rather than number of items. return bitmap.getByteCount() / 1024; } };
Get max available VM memory, exceeding this amount will throw anOutOfMemory exception. Stored in
kilobytes as LruCache takes anint in its constructor.
Memory LRU cache
// Add bitmap to the cache public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); }}//Retrieve the bitmap from the cachepublic Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key);}
Disk LRU cache
private DiskLruCache mDiskLruCache;private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
protected void onCreate(Bundle savedInstanceState) { // Initialize disk cache on background thread File cacheDir = getDiskCacheDir(this, "thumbnails"); new InitDiskCacheTask().execute(cacheDir);}class InitDiskCacheTask extends AsyncTask<File, Void, Void> { protected Void doInBackground(File... params) { synchronized (mDiskCacheLock) { File cacheDir = params[0]; mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE); } ....
Disk LRU cache
public void addBitmapToCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { // Add to memory cache as before mMemoryCache.put(key, bitmap); } synchronized (mDiskCacheLock) { // Also add to disk cache if (mDiskLruCache != null && mDiskLruCache.get(key) == null) { mDiskLruCache.put(key, bitmap); } ...public Bitmap getBitmapFromDiskCache(String key) { synchronized (mDiskCacheLock) { while (mDiskCacheStarting) {// Wait while disk cache is started
try { mDiskCacheLock.wait(); } …. if (mDiskLruCache != null) { return mDiskLruCache.get(key); }....
Disk LRU cache
final String imageKey = String.valueOf(params[0]);
// Check disk cache in background threadBitmap bitmap = getBitmapFromDiskCache(imageKey);
if (bitmap == null) { // Not found in disk cache // Process as normal final Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), params[0], 100, 100));}
// Add final bitmap to cachesaddBitmapToCache(imageKey, bitmap);
Going forward
Youtube Videohttps://www.youtube.com/watch?v=HY9aaXHx8yAhttps://www.youtube.com/watch?v=2TUvmlGoDrwhttps://www.youtube.com/watch?v=_ioFW3cyRV0
Reference Linkhttps://developer.android.com/topic/performance/graphics/index.htmlhttps://developer.android.com/topic/performance/graphics/load-bitmap.htmlhttps://developer.android.com/topic/performance/graphics/manage-memory.html
Can we do it faster?
YesWe can!
● Picasso● Glide
Importing the libraries
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.github.bumptech.glide:glide:3.7.0'
Loading the images simply
Picasso.with(context) .load(urlOfTheImageAsString) .into(imageView);
Loading the images simply
Picasso.with(context) .load(R.drawable.ic_image) .into(imageView);
Loading the images simply
Picasso.with(context) .load(“file:///android_assets/amazing.png”) .into(imageView);
Loading the images simply
Picasso.with(context) .load(new File(...)) .into(imageView);
Managing waiting state and errors
Picasso.with(context) .load(urlOfTheImageAsString) .placeholder(R.drawable.placeholder) .error(R.drawable.error) .into(imageView);
Advanced features
Picasso.with(context) .load(url) .resize(50, 50) .centerCrop() .into(imageView)
+MatteoBonifazi@mbonifazi
Thank You!
+RobertoOrgiu@_tiwiz