Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android BitmapFactory.decodeFile intermittently returning null

Tags:

android

bitmap

I'm using android 4.0.4, kernel 3.0.8+

Every so often BitmapFactory.decodeFile returns a null bitmap.

Note that if the bitmap factory fails to load the bitmap I immediately try again, up to 4 times, and this often works (!)

There are a lot of people complaining about this. Most of the questions have answers involving where the bitmap is put, or the nature of the flushed input stream / http connection / whatever. I have ruled these out - in fact, I have reduced my problem to an android application of such ridiculous simplicity that it could be called a test case.

My app has a single activity, which contains a single button, which when pushed starts a thread that loops through the external files directory trying to load everything in it into bitmaps. I don't use the bitmap, or keep it, or anything, I just load and forget:

public class MainActivity extends Activity {

  private static final String TAG = "bmpbash";

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

  public void onGo(View view) {
    view.setVisibility(View.GONE);
    start();
  }

  /**
   * Start the thread.
   */
  public void start() {
    Runnable r = new Runnable() {
      @Override
      public void run() {
        mainLoop();
      }
    };
    Thread thread = new Thread(r);
    thread.start();
  }

  public void mainLoop() {
    int index = 0;
    File top = getExternalFilesDir(null);
    while (true) {
      File[] files = top.listFiles();
      if (files.length < 1) {
        Log.e(TAG, "no files found");
      } else {
        if (files.length <= index) {
          index = 0;
        }
        File file = files[index];

        //byte[] data = readFile(file);

        try {
          boolean ok = false;
          for (int i = 0; i < 4 && !ok; ++i) {
            //Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
            if (bitmap == null) {
              Log.e(TAG, file.getName() + "[" + i + "] - NULL bitmap");
            } else {
              ok = true;
              Log.w(TAG, file.getName() + "[" + i + "] - OK");
            }
          }
        } catch (Exception e) {
          Log.e(TAG, file.getName() + " - DIED", e);
        } catch (OutOfMemoryError oom) {
          Log.e(TAG, file.getName() + " - OOM");
        }
        ++index;
      }
    }
  }
}    

I will see output like this:

10-22 17:27:57.688: W/bmpbash(1131): translucent.png[0] - OK
10-22 17:27:57.698: W/bmpbash(1131): fearthecow.png[0] - OK
10-22 17:27:57.798: W/bmpbash(1131): gui2.png[0] - OK
10-22 17:27:57.888: W/bmpbash(1131): gui.png[0] - OK
10-22 17:27:58.058: W/bmpbash(1131): boot.png[0] - OK
10-22 17:27:58.218: E/bmpbash(1131): trainer2.png[0] - NULL bitmap
10-22 17:27:58.378: W/bmpbash(1131): trainer2.png[1] - OK

You will see in the above code there is a commented out alternative load sequence, in which rather than using decodeFile I load the file into a byte[] and then use decodeByteArray. This has the same effect (decodeByteArray fails and then immediately succeeds on exactly the same byte array!), but I do note that the failures are much less common.

In the decodeFile case, perhaps 1 in 10 attempts returns null. In the decodeByteArray case, perhaps only 1 in 100. It is not always the same file that fails, but some files do seem to fail more often than others.

My best hunch is that the png decoder has a failure which is more likely to occur if it runs over a longer period of time, but after that I'm a bit lost. If anyone has any light to shed on the issue, or alternative approaches to loading png files, I'd really appreciate it!

like image 685
Andy Newman Avatar asked Oct 22 '13 10:10

Andy Newman


1 Answers

Further experiments show that this occurs on every device I have (I have quite a lot) which is running a particular flavour of 4.0.4, but does not occur even if left running overnight on a 4.1.1 device. Given the simplicity of the code, and that a later android version doesn't replicate it, my inclination is to mark this as a bug in Android which has, at some point, been fixed. With that information to help, I am going to assume that this is a bug in SKIA which is argued about to the nth degree in this thread here:

http://code.google.com/p/android/issues/detail?id=6066

My overall opinion is that this is a rarely occurring bug the existence of which is obscured by the enormous number of people who don't handle their input streams carefully enough. The only answer I've seen anywhere for this problem is to attempt to load the bitmap in a loop (OMG). However, if the hundreds of people who find this question because they have these symptoms could please make 100% sure that they are using a viable flushed input stream before resorting to such an unholy hack, then I think we'd all appreciate it!

Thanks

P.S. is it OK to feel guilty about calling this an "answer" ?

like image 99
Andy Newman Avatar answered Nov 12 '22 10:11

Andy Newman