androidandroid-imageviewonresume

Why are Images not displayed on app resume?


I have built an app where I'm displaying a series of tabs and each tab has a Listview with images and texts (vertical layout).
I have almost finished and i'm running some final tests and I have noticed the following:

  1. open the application
  2. All images are displayed fine
  3. Hold home button (application is on background)
  4. Go and clear RAM
  5. Hold home button and select my application to bring it forward.
  6. All item list are displayed apart from the images. Even when I'm navigation into the items (onclick) the images are not diplayed again (until I close and re-open the app).

I believe the problem is that the listview isnt re-drawn after the cleared memory. But I cannot find how I will refresh the UI thread on application resume

Any ideas?

I have tried on application resume to invalidate the main_layout, also to notify the view pager for dataset changes..but nothing did the trick. The files are store new File(ctx.getExternalFilesDir(Environment.DIRECTORY_PICTURES) +"/"+ storeImagePath).

---Updating with some code

MainActivity implements ActionBar.TabListener

loads

ArticlesListFragment extends ListFragment

where on create method is the following

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    
         getActivity().getActionBar().setDisplayHomeAsUpEnabled(false);
    
         final String categoryID = getArguments().getString("categoryID");

         View rootView = inflater.inflate(R.layout.items_list, container, false);
    
        final ArrayList<Item> items = new ArrayList<Item>();
        DBAdapter dba = DBAdapter.getSharedObject(getActivity().getApplicationContext());
        
        SparseArray<SparseArray<String>> articlesArray = dba.getArticleDetailsByID(categoryID, getActivity().getApplicationContext());
        
        if (articlesArray!=null && articlesArray.size()>0) {
            for (int i = 0; i < articlesArray.size(); i++) {

            SparseArray<String> article = articlesArray.get(i);
            items.add(ItemFactory.buildItem(ItemType.ARTICLE_ITEM, article.get(0), article.get(1), article.get(2), article.get(4), article.get(3)));
            }
        }
    
        setListAdapter(new LazyImageLoadAdapter(getActivity(), items));
        return rootView;
    }

and the LazyImageLoader is the following

public LazyImageLoadAdapter(Activity a, ArrayList<Item> d) {
    
        activity = a;
        data=d;
        inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         
        // Create ImageLoader object to download and show image in list
        // Call ImageLoader constructor to initialize FileCache
        imageLoader = new ImageLoader(activity.getApplicationContext());
}

It populates the view using this

//DisplayImage function from ImageLoader Class
        imageLoader.DisplayImage(data.get(position).getImagePath(), image);

The imageLoader class

public class ImageLoader {

    // Initialize MemoryCache
    // MemoryCache memoryCache = new MemoryCache();

    FileCache fileCache;

    // Create Map (collection) to store image and image url in key value pair
    private Map<ImageView, String> imageViews = Collections
        .synchronizedMap(new WeakHashMap<ImageView, String>());
    ExecutorService executorService;
    private String resolution;

    // handler to display images in UI thread
    Handler handler = new Handler();

    public ImageLoader(Context context) {

    fileCache = new FileCache(context);
    this.resolution = Utils.checkScreenResolution(context);
    // Creates a thread pool that reuses a fixed number of
    // threads operating off a shared unbounded queue.
    executorService = Executors.newFixedThreadPool(5);

    }

    // default image show in list (Before online image download)
    final int stub_id = R.drawable.no_article;

    public void DisplayImage(String url, ImageView imageView) {
    // /Store image and url in Map
    imageViews.put(imageView, url);
    // queue Photo to download from url
    queuePhoto(url, imageView);
    }

    private void queuePhoto(String url, ImageView imageView) {
    // Store image and url in PhotoToLoad object
    if (url != null && url.contains("null")) {
        url = url.replace("null", resolution);
    }
    PhotoToLoad p = new PhotoToLoad(url, imageView);

    // pass PhotoToLoad object to PhotosLoader runnable class
    // and submit PhotosLoader runnable to executers to run runnable
    // Submits a PhotosLoader runnable task for execution

    executorService.submit(new PhotosLoader(p));
    }

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

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

    class PhotosLoader implements Runnable {

    PhotoToLoad photoToLoad;

    PhotosLoader(PhotoToLoad photoToLoad) {
        this.photoToLoad = photoToLoad;
    }

    @Override
    public void run() {
        try {
        // Check if image already downloaded
        if (imageViewReused(photoToLoad))
            return;
        // download image from web url
        Bitmap bmp = getBitmap(photoToLoad.url);

        // Get bitmap to display
        BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);

        // Causes the Runnable bd (BitmapDisplayer) to be added to the
        // message queue.
        // The runnable will be run on the thread to which this handler
        // is attached.
        // BitmapDisplayer run method will call
        handler.post(bd);

        } catch (Throwable th) {
        th.printStackTrace();
        }
    }
    }

    private Bitmap getBitmap(String url) {
    File f = fileCache.getFile(url);

    // from SD cache
    // CHECK : if trying to decode file which not exist in cache return null
    if (f.exists()) {
        Bitmap b = decodeFile(f);
        if (b != null)
        return b;
    }
    // Download image file from web
    try {

        Bitmap bitmap = null;
        URL imageUrl = new URL(MY_URL);
        HttpURLConnection conn = (HttpURLConnection) imageUrl
            .openConnection();
        conn.setConnectTimeout(30000);
        conn.setReadTimeout(30000);
        conn.setInstanceFollowRedirects(true);
        InputStream is = conn.getInputStream();

        // Constructs a new FileOutputStream that writes to file
        // if file not exist then it will create file
        OutputStream os = new FileOutputStream(f);

        // See Utils class CopyStream method
        // It will each pixel from input stream and
        // write pixels to output stream (file)
        Utils.CopyStream(is, os);

        os.close();
        conn.disconnect();

        // Now file created and going to resize file with defined height
        // Decodes image and scales it to reduce memory consumption
        bitmap = decodeFile(f);

        return bitmap;

    } catch (Throwable ex) {
        ex.printStackTrace();
        if (ex instanceof OutOfMemoryError)
        ;// memoryCache.clear();
        return null;
    }
    }

    // Decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File f) {

    try {

        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        FileInputStream stream1 = new FileInputStream(f);
        BitmapFactory.decodeStream(stream1, null, o);
        stream1.close();

        // Find the correct scale value. It should be the power of 2.

        // Set width/height of recreated image
        final int REQUIRED_SIZE = 180;

        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
        if (width_tmp / 2 < REQUIRED_SIZE
            || height_tmp / 2 < REQUIRED_SIZE)
            break;
        width_tmp /= 2;
        height_tmp /= 2;
        scale *= 2;
        }

        // decode with current scale values
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        FileInputStream stream2 = new FileInputStream(f);
        Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
        stream2.close();
        return bitmap;

    } catch (FileNotFoundException e) {
        System.out.println("----------File wasnt there: "
            + f.getAbsolutePath());
        e.printStackTrace();
    } catch (IOException e) {
        System.out
            .println("----------io exception: " + f.getAbsolutePath());
        e.printStackTrace();
    }
    return null;
    }

    boolean imageViewReused(PhotoToLoad photoToLoad) {

    String tag = imageViews.get(photoToLoad.imageView);
    // Check url is already exist in imageViews MAP
    if (tag == null || !tag.equals(photoToLoad.url))
        return true;
    return false;
    }

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

    public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
        bitmap = b;
        photoToLoad = p;
    }

    public void run() {
        if (imageViewReused(photoToLoad))
        return;

        // Show bitmap on UI
        if (bitmap != null)
        photoToLoad.imageView.setImageBitmap(bitmap);
    }
    }  
}

and the FileCache

private File cacheDir;
 
public FileCache(Context context){
     
    //Find the dir at SDCARD to save cached images
cacheDir =  Utils.isExternalStorageReadable() ? context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) :
    new File(context.getFilesDir() + "/myAppFolder/"); 

    if(!cacheDir.exists()){
        // create cache dir in your application context
        cacheDir.mkdirs();
    }
}
 
public File getFile(String url){
    //Identify images by hashcode or encode by URLEncoder.encode.
    if (url!=null) {
    String filename = String.valueOf(url.hashCode());

    File f = new File(cacheDir, filename);
    return f;
    }else {
        return null;
    }
     
}

Solution

  • All my images are loaded for specific screen, therefore I use a static variable initialized on my Splashscreen which resolves the device resolution. On application resume the variable is not initialized cause splash screen is not displayed.

    Therefore the imagepaths I used were wrong.