3

I am trying to load an image in the background as someone works through my app. The logic I wrote is this:

public class ImageLoader extends AsyncTask <Context, Void, Bitmap>{

    private String URL;
    private int type;

    ImageLoader(String Url, int Type)
    {
        URL = Url;
        type = Type;
    }

    @Override
    protected Bitmap doInBackground(Context... arg0) {

        AssetManager assetMgr = arg0[0].getAssets();
        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeStream(assetMgr.open(URL));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    @Override
    protected void onPostExecute( Bitmap result )  {
          super.onPostExecute(result);

          if (type == 1)
              Inst1 = result;
          else if (type == 2)
              Inst2 = result;
          else if (type == 3)
              Inst3 = result;
    }
}

However when I try to start a new thread like this:

task = new ImageLoader("Instructions_2.png", 3);
task.execute(gameContext);

But within the program I get the error Looper.prepare must be called, followed by the logic looper.quit()

However I seem to break the program when I add Looper.prepare(), and there is no looper.quit() to call.

Am I creating the task correctly?

EDIT:

This is the error log from when I try to run:

task = new ImageLoader(gameContext, "Instructions_3.png", 3);

I have a switch case statement that I put the image loader declaration outside of. Essentially my code is:

ImageLoader task;
switch(foo)
{
    case 0:
       ...
       task = new ImageLoader(gameContext, "Instructions_0.png", 3);
       task.execute();
       break;
    case 1:
       ...
       task = new ImageLoader(gameContext, "Instructions_1.png", 3));
       task.execute();
       break;
    ...
}

And the error log (error occurs every time I hit the task = new ImageLoader(...); line

07-20 14:23:34.276: E/AndroidRuntime(16741): FATAL EXCEPTION: Thread-10
07-20 14:23:34.276: E/AndroidRuntime(16741): java.lang.ExceptionInInitializerError
07-20 14:23:34.276: E/AndroidRuntime(16741):    at com.petronicarts.stormthecastle.MainGamePanel.update(MainGamePanel.java:2578)
07-20 14:23:34.276: E/AndroidRuntime(16741):    at com.petronicarts.stormthecastle.MainThread.run(MainThread.java:63)
07-20 14:23:34.276: E/AndroidRuntime(16741): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
07-20 14:23:34.276: E/AndroidRuntime(16741):    at android.os.Handler.<init>(Handler.java:121)
07-20 14:23:34.276: E/AndroidRuntime(16741):    at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
07-20 14:23:34.276: E/AndroidRuntime(16741):    at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
07-20 14:23:34.276: E/AndroidRuntime(16741):    at android.os.AsyncTask.<clinit>(AsyncTask.java:152)
07-20 14:23:34.276: E/AndroidRuntime(16741):    ... 2 more
Nathan Tornquist
  • 6,468
  • 10
  • 47
  • 72

1 Answers1

6

The problem is that you are trying to access and operate on UI elements from a non UI thread. If you change your AsyncTask as follows, i believe you will be ok:

public class ImageLoader extends AsyncTask <Void, Void, Bitmap>{

private String URL;
private int type;
private Context context;
private InputStream in;

ImageLoader(Context context, String Url, int Type)
{
    URL = Url;
    type = Type;
    ImageLoader.this.context = context;
}

@Override
protected void onPreExecute()
{
   AssetManager assetMgr = context.getAssets();

   try {

       in = assetMgr.open(URL);
   } catch (IOException e) {

       e.printStackTrace();
   }

}

@Override
protected Bitmap doInBackground(Void... arg0) {

    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream(in);
        in.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return bitmap;
}

@Override
protected void onPostExecute( Bitmap result )  {

      if (type == 1)
          Inst1 = result;
      else if (type == 2)
          Inst2 = result;
      else if (type == 3)
          Inst3 = result;
}
}

Also change the call of your AyncTask to something like this:

task = new ImageLoader(gameContext, "Instructions_2.png", 3);
task.execute();
AggelosK
  • 4,313
  • 2
  • 32
  • 37
  • I still am getting an error thrown about not calling Looper.prepare(). Where is the ideal spot to put that? – Nathan Tornquist Jul 20 '12 at 19:20
  • @NathanTornquist Can you post your error log and point out the line that throws the exception in your code? – AggelosK Jul 20 '12 at 19:21
  • @NathanTornquist Are you calling your switch case statement from another `Thread` which is not the `UI Thread`? If that is the case, try calling your `ImageLoader AsyncTask` as follows: `runOnUiThread(new Runnable() { public void run() { task = new ImageLoader(gameContext, "Instructions_0.png",3); task.execute();} });` inside your `case` statements in your `switch` block. – AggelosK Jul 20 '12 at 19:34
  • I have an Activity that sends the onPause, onResume, onDestroy, etc. events to my MainGamePanel. That MainGamePanel is an extended SurfaceView. I have a MainThread that runs the update and draw functions within my MainGamePanel. The thread is started when MainGamePanel is initialized. -- I hope that all makes sense. With that, I don't seem to have access to runOnUiThread. – Nathan Tornquist Jul 20 '12 at 19:41
  • @NathanTornquist You can have access in the `runOnUiThread` method if you call it that way: `yourActivity.runOnUiThread(...);`, where `yourActivity` is a reference to the `Activity` that is being shown in the screen. I do not know if what i suggested will solve your problem for sure, but the error that is occuring to you happens only when you are handling UI elements off the UI thread, so that is what you need to look for. Hope that helps. – AggelosK Jul 20 '12 at 19:47
  • I was only giving myself access to the context in the MainGamePanel() class. Giving myself access to the activity fixed the issue with that statement. You've answered my question, and I'll mark this as the selected answer, but I have a few more questions if you would be willing to answer them. -- I am using this in a scrolling gallery that displays instructions. I wanted to load future images on a thread so that it wouldn't cause any delays. However I am still getting a delay when I load that image, even on a thread. Do you have any ideas why? – Nathan Tornquist Jul 20 '12 at 19:57
  • Also, should I be clearing that bitmap from memory in the thread, or will garbage collection do all that? – Nathan Tornquist Jul 20 '12 at 20:06
  • @NathanTornquist Glad i could help:) As a starting point for efficent handling of multiple images, you can take a look in the android developers site here: http://developer.android.com/training/displaying-bitmaps/index.html . Read all the subsections of the topic, since it suggests ways for handling bitmaps efficiently, which will surely help you. I do not know if you have already checked this. If you have any specific problem or question, i'll gladly help (if i can of course) :) – AggelosK Jul 20 '12 at 20:10
  • 'android.os.AsyncTask' is deprecated as of API 30. – Danny E.K. van der Kolk Mar 07 '23 at 23:38