user1012050
user1012050

Reputation: 1

Android: what is the best way to load images from web without using AsyncTask?

The common task in Android development is loading images from web and binding with ImageView in Activity. In code that I had been saw programmers often use AsyncTask to run loading in background and post result in context. This approach lead us to craching aplication in some cases sucn as orientation changes. The cause is context. Because activity could be destroyed and created again our context also would be lost, but in running AsyncTask context is not update. So when postExectute will be invoked AsyncTask try to post result to Activity that no more exist.

I want to know what approaches you are using to load data as images and then posting into Activity?

Upvotes: 0

Views: 599

Answers (2)

Hitendra
Hitendra

Reputation: 3226

Here is the complete code that load the images in background and stores it to sdcard.if the image is already there then it wont make request to server. package com.packsmooch.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Stack;


import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;

public class Albumartloader 
{
    private HashMap<String, Bitmap> cache = new HashMap<String, Bitmap>();

    private File cacheDir;
    private Bitmap  useThisBitmap;

    public Albumartloader(Context context) 
    {   
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
            cacheDir = new File(android.os.Environment.getExternalStorageDirectory(),"Streaming_Service/AlbumArt/");
        else
            cacheDir = context.getCacheDir();

        if (!cacheDir.exists())
            cacheDir.mkdirs();
    }

    public void DisplayImage(String url, Activity activity, ImageView imageView) 
    {
         if(!url.equals(""))
         {
             if (cache.containsKey(url))
             {
                 imageView.setImageBitmap(cache.get(url));
             }
             else
             {
                queuePhoto(url, activity, imageView);
            }
         }
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView) 
    {
        photosQueue.Clean(imageView);
        PhotoToLoad p = new PhotoToLoad(url, imageView);

        synchronized (photosQueue.photosToLoad) 
        {
            photosQueue.photosToLoad.push(p);
            photosQueue.photosToLoad.notifyAll();
        }

        // start thread if it's not started yet
        if (photoLoaderThread.getState() == Thread.State.NEW)
            photoLoaderThread.start();
    }

    public Bitmap getBitmap(String url) 
    {
        try
        {
            // I identify images by hashcode. Not a perfect solution, good for the
            // demo.
            String filename = String.valueOf(url.hashCode());
            File f = new File(cacheDir, filename);

            // from SD cache
            Bitmap b = decodeFile(f);
            if (b != null)
                return b;

            // from web
            try {
                Bitmap bitmap = null;       
                if(!url.equals("")){
                InputStream is = new URL(url).openStream();
                OutputStream os = new FileOutputStream(f);
                Utils.CopyStream(is, os);
                os.close();
                bitmap = decodeFile(f);
                }
                return bitmap;
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
        catch(Exception e)
        {
            return null;    
        }
    }

    /*decodes image and scales it to reduce memory consumption
     * @param file path
     * @throws FileNotFoundException
     * @return bitmap
     * */
    private Bitmap decodeFile(File f){
        Bitmap b = null;
        try {

            useThisBitmap = null;
            //Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            final int IMAGE_MAX_SIZE =50;
            BitmapFactory.decodeStream(new FileInputStream(f), null, o);
            int scale = 2;
            if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
                scale = 2 ^ (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5));
            }

            //Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();

            o2.inSampleSize = scale;
            b = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
            useThisBitmap = b;

        } catch (FileNotFoundException e) {

        }
        catch(Exception e)
        {

        }
        finally{
            System.gc();
        }
        return useThisBitmap;
    }


    // Task for the queue
    private class PhotoToLoad 
    {
        public String url;
        public ImageView imageView;

        public PhotoToLoad(String u, ImageView i) {
            url = u;
            imageView = i;
        }
    }

    PhotosQueue photosQueue = new PhotosQueue();

    public void stopThread() 
    {
        photoLoaderThread.interrupt();
    }

    // stores list of photos to download
    class PhotosQueue {
        private Stack<PhotoToLoad> photosToLoad = new Stack<PhotoToLoad>();

        // removes all instances of this ImageView
        public void Clean(ImageView image) {
            for (int j = 0; j < photosToLoad.size();) {
                if (photosToLoad.get(j).imageView == image)
                    photosToLoad.remove(j);
                else
                    ++j;
            }
        }
    }

    class PhotosLoader extends Thread {
        public void run() {
            try {
                while (true) {
                    // thread waits until there are any images to load in the
                    // queue
                    if (photosQueue.photosToLoad.size() == 0)
                        synchronized (photosQueue.photosToLoad) {
                            photosQueue.photosToLoad.wait();
                        }
                    if (photosQueue.photosToLoad.size() != 0) {
                        PhotoToLoad photoToLoad;
                        synchronized (photosQueue.photosToLoad) {
                            photoToLoad = photosQueue.photosToLoad.pop();
                        }
                        Bitmap bmp = getBitmap(photoToLoad.url);
                        cache.put(photoToLoad.url, bmp);
                        if (((String) photoToLoad.imageView.getTag())
                                .equals(photoToLoad.url)) {
                            BitmapDisplayer bd = new BitmapDisplayer(bmp,
                                    photoToLoad.imageView);
                            Activity a = (Activity) photoToLoad.imageView
                                    .getContext();
                            a.runOnUiThread(bd);
                        }
                    }
                    if (Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                // allow thread to exit
            }
        }
    }

    PhotosLoader photoLoaderThread = new PhotosLoader();

    // Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable {
        Bitmap bitmap;
        ImageView imageView;

        public BitmapDisplayer(Bitmap b, ImageView i) {
            bitmap = b;
            imageView = i;
        }

        public void run() 
        {
            if (bitmap != null)
                imageView.setImageBitmap(bitmap);
        }
    }

    public void clearCache() 
    {
        // clear memory cache
        cache.clear();
        // clear SD cache
        File[] files = cacheDir.listFiles();
        for (File f : files)
            f.delete();
    }
}

Here is the usage:

Albumartloader  albumartloader=new Albumartloader(this);
imageview.settag(urlofimage);
albumartloader.DisplayImage(url of image,activity context,imageview);

Best usage:-you can use it for listview when you need to display number of images from server and dont want to wait for all images to download.

Upvotes: 0

Kurtis Nusbaum
Kurtis Nusbaum

Reputation: 30825

You should try using what's called and AsyncTaskLoader. It's like an AsycnTask, except that the whole "need to delete the AsyncTask when the activity dies" bit is take care of for you. Loaders in general were designed almost specifically to improve the task of asynchronously loading data for listviews. In fact, they have quickly become one of my favorite new android classes to use. For more information on loaders, checkout this documentation.

As a final note, loaders weren't introduced until API level 10. That said, you can still access them from lesser api levels using the android Support Package.

Upvotes: 4

Related Questions