title: Why would ImageView.setImageURI() work in Android 2.2 but not 2.1?
body: <p>I am displaying images and some text in a list view with an adapter.  The images are pulled from the web, then cached locally and displayed.  The images are already small (60px square), and I know their size, so I'm using the <a href="http://www.helloandroid.com/tutorials/how-avoid-outofmemory-oom-exception-using-bitmaps-solved" rel="nofollow noreferrer">advice from here suggesting I use setImageURI instead of decoding the bitmap</a>.  </p>
  
  <p>The class that does the work is a modified version of Fedor's <a href="https://stackoverflow.com/questions/541966/android-how-do-i-do-a-lazy-load-of-images-in-listview">ImageLoader</a></p>
  
  <p>The code attaches a stub drawable to the ImageView until the desired image is downloaded from the web, then loads the cached file from the sdcard.  In Android 2.2 this works just fine.  It's fast, and I don't get OOM crashes.  On 2.1, though, I get the following error:</p>
  
  <pre><code>09-15 11:04:52.993: INFO/System.out(240): resolveUri failed on bad bitmap uri: file:///sdcard/android/data/com.example.myapp/cache/4164137
  </code></pre>
  
  <p>The ImageLoader class is as follows:</p>
  
  <pre><code>/*
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at
  
  http://www.apache.org/licenses/LICENSE-2.0
  
  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.    
  */
  
  public class ImageLoader {
  
      //the simplest in-memory cache implementation. This should be replaced with something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
      private HashMap&lt;String, Uri&gt; cache=new HashMap&lt;String, Uri&gt;();
  
      private File cacheDir;
  
      public ImageLoader(Context context){
          //Make the background thead low priority. This way it will not affect the UI performance
          photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
  
          //Find the dir to save cached images
          if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
              cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"Android/data/com.droidicon.launcherproicons/cache/");
          else
              cacheDir=context.getCacheDir();
          if(!cacheDir.exists())
              cacheDir.mkdirs();
      }
  
      final int stub_id=R.drawable.loading;
      public void DisplayImage(String url, Activity activity, ImageView imageView, int scaleSize)
      {
          if(cache.containsKey(url))
              imageView.setImageURI(cache.get(url));
          else
          {
              queuePhoto(url, activity, imageView, scaleSize);
              imageView.setImageResource(stub_id);
          }    
      }
  
      private void queuePhoto(String url, Activity activity, ImageView imageView, int scaleSize)
      {
          //This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them. 
          photosQueue.Clean(imageView);
          PhotoToLoad p=new PhotoToLoad(url, imageView, scaleSize);
          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();
      }
      private Uri getUri(String url, int scaleSize){
          if(url != ""){
              //I identify images by hashcode. Not a perfect solution, good for the demo.
  //          try{
              String filename=String.valueOf(url.hashCode());
              File f=new File(cacheDir, filename);
  
              //from SD cache
  
              if(f.exists()){
                  Uri b = Uri.fromFile(f);
                  System.out.println(f.toString());
                  return b;
              }
  
              //from web
              try {
                  Uri bitmap=null;
                  InputStream is=new URL(url).openStream();
                  OutputStream os = new FileOutputStream(f);
                  Utils.CopyStream(is, os);
                  os.close();
                  bitmap = Uri.fromFile(f);
                  System.out.println(f.toString());
                  return bitmap;
              } catch (Exception ex){
                 ex.printStackTrace();
                 return null;
              }
              } else {
                  return null;
              }
      }
  
  
      //Task for the queue
      private class PhotoToLoad
      {
          public String url;
          public ImageView imageView;
          public int scaleSize;
          public PhotoToLoad(String u, ImageView i, int ss){
              url=u; 
              imageView=i;
              scaleSize=ss;
          }
      }
  
      PhotosQueue photosQueue=new PhotosQueue();
  
      public void stopThread()
      {
          photoLoaderThread.interrupt();
      }
  
      //stores list of photos to download
      class PhotosQueue
      {
          private Stack&lt;PhotoToLoad&gt; photosToLoad=new Stack&lt;PhotoToLoad&gt;();
  
          //removes all instances of this ImageView
          public void Clean(ImageView image)
          {
              for(int j=0 ;j&lt;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();
                          }
                          Uri bmp=getUri(photoToLoad.url, photoToLoad.scaleSize);
                          cache.put(photoToLoad.url, bmp);
                          if(((String)photoToLoad.imageView.getTag()).equals(photoToLoad.url)){
                              UriDisplayer bd=new UriDisplayer(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();
      class UriDisplayer implements Runnable
      {
          Uri uri;
          ImageView imageView;
          public UriDisplayer(Uri u, ImageView i){uri=u;imageView=i;}
  
  
          public void run() {
              File f = new File(uri.getPath());
              if(f.exists()){
                  imageView.setImageURI(uri);
              } else {
                  imageView.setImageResource(stub_id);
              }
  
          }
  
      }
  
  
      public void clearCache() {
          //clear memory cache
          cache.clear();
  
          //clear SD cache
          File[] files=cacheDir.listFiles();
          for(File f:files)
              f.delete();
      }
  
  }
  </code></pre>
  
  <p>Here is one of the adapters that implements this ImageLoader:</p>
  
  <pre><code>public class ColorAdapter extends ArrayAdapter&lt;Color&gt; {
      private Activity activity;
      private int resource;
      private String response;
      private Context context;
      public ImageLoader imageLoader;
  
  
      public ColorAdapter(Activity a, Context context, int resource, List&lt;Color&gt; items){
          super(context, resource, items);
          this.resource=resource;
          imageLoader=new ImageLoader(context);
          activity = a;
  
      }
  
      @Override
      public View getView(int position, View convertView, ViewGroup parent){
          Color color =  getItem(position);
          String minflater = Context.LAYOUT_INFLATER_SERVICE;
          LayoutInflater inflater;
          inflater = (LayoutInflater)getContext().getSystemService(minflater);
          ViewHolder holder;
          if(convertView==null){
              convertView = inflater.inflate(R.layout.listcolors, parent, false);
              holder=new ViewHolder();
              holder.txtName=(TextView)convertView.findViewById(R.id.txtName);
              //holder.txtUser=(TextView)convertView.findViewById(R.id.txtUser);
              holder.imgColorImg=(ImageView)convertView.findViewById(R.id.imgColorImg);
  
              convertView.setTag(holder);
  
          }
          else
              holder=(ViewHolder)convertView.getTag();
  
          holder.txtName.setText(color.getName());
          //holder.txtUser.setText(dock.getUser());
          holder.imgColorImg.setTag(color.getIconURL());
          imageLoader.DisplayImage(color.getIconURL(), activity, holder.imgColorImg, 84);
          return convertView;
  
  
      }
  
      class ViewHolder{
          TextView txtName;
          //TextView txtUser;
          ImageView imgColorImg;
      }
  
  
  
  }
  </code></pre>