android - displaying images

Post on 12-Apr-2017

45 Views

Category:

Mobile

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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);

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

top related