Greg Peckory
Greg Peckory

Reputation: 8068

Caching, Passing and Viewing bitmaps efficiently Android

I have a news feed, where news items contain images. Each news item (in the table view) obtains a image url from a server and downloads the images asynchronously / from cache.

I will use a bit of pseudocode / Java to explain my process in the simplest terms possible.

NewsItemAdapter

Map<String, Bitmap> imageCache = new HashMap<String, Bitmap>();  // init cache
String imurl = get image url from appropriate NewsObject;
Bitmap value = imageCache.get(imurl);

if (value != null) {   // if bitmap is in cache

    load bitmap into image view from cache;
    add bitmap to NewsObject for accessing later;

}else {

    execute Asynchronous bitmap download task;

}

Asynchronous bitmap download task (The reason for scaleDownBitmap() is because of OutOfMemory errors I get)

doinBackground(Void...params){

    myBitmap = download bitmap from imurl;
    imageCache.put(imurl, scaleDownBitmap(myBitmap)); // put bitmap into cache
    return scaleDownBitmap(myBitmap);
}

onPostExecute(Bitmap result){

    load result into image view;

}

MainActivity

setOnNewsItemClickListener{

    intent = get intent to mainNewsScreen; //after you click on news item
    intent.putExta("newsBitmap", bitmap from NewsObject); // set in NewsItemAdapter
    startActivity(intent);

}        

MainNewsScreen

onCreate(){

    load bitmap from intent extras into image view;

}

My main problem is if I remove the scaleDownBitmap() method found here I get OutOfMemory errors.

But I am losing a lot of image quality. As you can see I haven't used bitmap.recycle() at all, I'm not entirely sure where I'd use it as I need to keep the images in memory (I would have thought).

Any idea how to make this more efficient. I'm sure this would be helpful to a lot of people attempting to create a similar app.

Upvotes: 1

Views: 720

Answers (1)

JohanShogun
JohanShogun

Reputation: 2976

Consider using an lrucache instead and storing the image on disk when it falls out of the cache.

Best practice is explained here: http://developer.android.com/intl/es/training/displaying-bitmaps/cache-bitmap.html

Consider this code as an idea, some of it copied from the above link:

...
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
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;
    }
    @Override
    protected void entryRemoved (boolean evicted, K key, V oldValue, V newValue) {
       // Save your entry to disc instead
    }
};

Google has made a DiscLruCache that you can simply download and use in your project (its usage is described in the above link):

https://developer.android.com/intl/es/samples/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/DiskLruCache.html

Also don't keep an infinite amount of news / images in the view. You're going to have to remove news items as the user scrolls through them and reads them.

Consider using a Recycler view for this (along with Cards if you want a Material design feel to your app): https://developer.android.com/intl/es/reference/android/support/v7/widget/RecyclerView.html

<!-- A RecyclerView with some commonly used attributes -->
<android.support.v7.widget.RecyclerView
    android:id="@+id/my_recycler_view"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

    // use a linear layout manager
    mLayoutManager = new LinearLayoutManager(this);
    mRecyclerView.setLayoutManager(mLayoutManager)

Cards: https://developer.android.com/intl/es/reference/android/support/v7/widget/CardView.html

<!-- A CardView that contains a TextView -->
<android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/card_view"
    android:layout_gravity="center"
    android:layout_width="200dp"
    android:layout_height="200dp"
    card_view:cardCornerRadius="4dp">

    <TextView
        android:id="@+id/info_text"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</android.support.v7.widget.CardView>

More information about combining a Recycler view with Card views: https://developer.android.com/training/material/lists-cards.html

Upvotes: 1

Related Questions