I've got the following Async task, which should just load up an image from a given URL. The images do exist and I have access to them
private class FetchVehicleImage extends AsyncTask<String, Integer, Bitmap>
{
private ProgressBar mSpinner;
private ImageView mImage;
private String imagesBaseUrl = "http://mywebsite.net/images/";
private URL url = null;
@Override
protected void onPreExecute()
{
mImage = (ImageView) findViewById(R.id.vehicle_image);
mSpinner = (ProgressBar) findViewById(R.id.vehicle_image_progress_bar);
mSpinner.setIndeterminate(true);
mSpinner.setVisibility(View.VISIBLE);
mImage.setVisibility(View.GONE);
}
@Override
protected Bitmap doInBackground(String... strings)
{
Bitmap bm = null;
try
{
url = new URL(imagesBaseUrl + strings[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bm = BitmapFactory.decodeStream(is);
}
catch (Exception e)
{
e.printStackTrace();
}
return bm;
}
protected void onPostExecute(final Bitmap result)
{
if (result != null)
{
mImage.setImageBitmap(result);
}
mImage.setVisibility(View.VISIBLE);
mSpinner.setVisibility(View.GONE);
}
}
I never see an exception in the doInBackground
, however sometimes bm
is returned as null, but its very intermittent. I have 4 images, 3 of those load perfectly fine every time, but one only loads if I hit a breakpoint on the bm
assignment, supposedly giving it enough time to do its work?
I thought that doInBackground
should run on a background thread therefore I should always either get the image, or get an exception?
N.B. If your app is merely running out of native backing memory for its bitmaps, then this approach will not help. If you are having hard-to-explain problems with bitmaps, especially pre-Honeycomb, I cannot overstate the importance of understanding the relationship of Dalvik heap to native backing memory. Mr. Dubroy's discussion of this was exceptionally helpful for me -- worth listening all the way through Dubroy's Heap Presentation
And, then my attempted answer to your question above . . . I can't prove it, but I have a very strong suspicion that it is not thread-safe. I hit this when I do image manipulation after fetching. As with your example above, when I request more than one image file, and process them as they arrive, I get
OutOfMemory
errors, which I catch, only to discover that both the heap and the available native backing memory are fine (>100k and >100M respectively). And, sometimes the fetch works (as you describe), but sometimes not. On some devices it is more robust than others. When called upon to invent a story for why this is, I imagine to myself that there may be image processing hardware (e.g. jpg encoders) on some devices and not others, which the native libraries of the OS may or may not avail themselves of. Then, I proceed immediately to blame those hardware bottlenecks for being not thread safe -- all without the smallest shred of anything resembling evidence. Anyway, the only approach that I found works on all the devices in my test stable (about a dozen) -- reliably -- is to isolate the bitmap manipulation parts and single-thread.In your example above, you would still use the
AsyncTask
to actually fetch the files from the network, and to write them to storage someplace (the raw byte stream). When theAsyncTask
completes (i.e. calls its delegate foronPostExecution
), then you might do something like my Poster class below.In my activity (where I make the multiple download requests), I create a global executor in the class which is instantiated initially in the UI-Thread:
And then initialize it:
Then, I dispense with using
AsyncTask
, to gain control of the number of threads in theThread
pool. My async bits look like this, instead:Then, some bits you probably don't care about . . . and then some initialization stuff, like how to set our delegate (you will want a PosterImageDelegate interface, of course):
And then, the bits that do image manipulation, and as a side-effect, use the
BitmapFactory
(andDrawable
) classes. To use this, you instantiate the PosterImage object, set yourself as the delegate, and then call this fellow:When this returns, the calling object (in the UI-Thread) has a 'busy' drawable. When the delegate gets called (after the file downloads and is converted to a Drawable by this thread, it is ready for loading into into whatever Drawable receiver you designate. Any number of images can be downloaded in parallel, and this guarantees that the background thread will only ever process one image at a time. Happily, it does not tie up the UI thread to do the image processing
(N.B. you still need a
Handler
in your calling class (the one that sets itself as the delegate) to have the UI thread actually put the Drawable into the receivingView
/Layout
/whatever). For attempted completeness, that might look like:Perhaps all this helps, perhaps not. But I would love to hear a definitive answer to the excellent question you pose.