Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CursorLoader in asynctask, can't create handler inside thread that has not called Looper.prepare

in my application I call the stock camera app to take a picture. In my activity result I start a asynctask to process the photo. (Rotate and upload the photo).

I also want to remove the photo from the gallery. To do this I exectute the following code in my asynctask.

// Delete image from gallery
        String[] imageColumns = { MediaStore.Images.Media._ID };
        String imageOrderBy = MediaStore.Images.Media._ID + " DESC";
        String imageWhere = MediaStore.Images.Media._ID + ">?";
        String[] imageArguments = { Integer.toString(captureLastID) };

        CursorLoader imageLoader = new CursorLoader(mActivity);

        imageLoader.setUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        imageLoader.setProjection(imageColumns);
        imageLoader.setSelection(imageWhere);
        imageLoader.setSelectionArgs(imageArguments);
        imageLoader.setSortOrder(imageOrderBy);

        Cursor imageCursor = imageLoader.loadInBackground();

        if (imageCursor.getCount() > 0) {
            while (imageCursor.moveToNext()) {
                int id = imageCursor.getInt(imageCursor
                        .getColumnIndex(MediaStore.Images.Media._ID));

                ContentResolver cr = mActivity.getContentResolver();
                cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        MediaStore.Images.Media._ID + "=?",
                        new String[] { Long.toString(id) });
                break;
            }
        }

        imageCursor.close();

I get the error

can't create handler inside thread that has not called Looper.prepare

What causes this error and how can I fix it? Does it has something to do with the fact that I call loadInBackground, while asynctask already runs in the background?

This is my logcat:

java.lang.RuntimeException: An error occured while executing doInBackground()
    at android.os.AsyncTask$3.done(AsyncTask.java:200)
    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
    at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
    at java.lang.Thread.run(Thread.java:1019)


   Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    at android.os.Handler.<init>(Handler.java:121)
    at android.support.v4.content.Loader$ForceLoadContentObserver.<init>(Loader.java:52)
    at android.support.v4.content.CursorLoader.<init>(CursorLoader.java:96)
    at tasks.ProcessPictureTask.doInBackground(ProcessPictureTask.java:78)
    at tasks.ProcessPictureTask.doInBackground(ProcessPictureTask.java:1)
    at android.os.AsyncTask$2.call(AsyncTask.java:185)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
    ... 4 more
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    at android.os.Handler.<init>(Handler.java:121)
    at android.support.v4.content.Loader$ForceLoadContentObserver.<init>(Loader.java:52)
    at android.support.v4.content.CursorLoader.<init>(CursorLoader.java:96)
    at tasks.ProcessPictureTask.doInBackground(ProcessPictureTask.java:78)
    at tasks.ProcessPictureTask.doInBackground(ProcessPictureTask.java:1)
    at android.os.AsyncTask$2.call(AsyncTask.java:185)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
    at java.lang.Thread.run(Thread.java:1019)
like image 669
Robby Smet Avatar asked Oct 24 '12 14:10

Robby Smet


People also ask

What does Looper prepare do?

Creating Looper and MessageQueue in Thread. Looper. prepare() identifies the calling thread and creates Looper and MessageQueue object and associate the thread with them in LocalThread storage class. Looper. loop() must be called to start the associated looper and Looper.

Is it possible to start AsyncTask from background thread?

To start an AsyncTask the following snippet must be present in the MainActivity class : MyTask myTask = new MyTask(); myTask. execute(); In the above snippet we've used a sample classname that extends AsyncTask and execute method is used to start the background thread.

Which thread will process a message posted to a handler?

Android handles all the UI operations and input events from one single thread which is known as called the Main or UI thread. Android collects all events in this thread in a queue and processes this queue with an instance of the Looper class.

Does AsyncTask run on UI thread?

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params , Progress and Result , and 4 steps, called onPreExecute , doInBackground , onProgressUpdate and onPostExecute .


2 Answers

Actually, the accepted answer is incorrect. You seem to be interested in the reason to why this happen, and it might also help the responsiveness of your app, so I will give another answer:

You are not altering the UI, thus this is not the reason for this crash. The reason is that your background thread does not have a Looper associated with it, as the error states. A looper is a message queue that is occasionally checked for messages. For example, it is what you use when you use a Handler.

You can add a looper simply by adding this line of code before the call that crashes your app:

Looper.prepare();

There is a big advantage to doing heavy queries on a background thread instead of on the UI thread, since your UI will not be responsive while the cursor is loading. A call to the db should be done on a background thread if responsiveness is important to you.

Now, there really is no reason for the CursorLoader to need a looper in this case. It is just clumsy implementation that the exception is thrown, as far as I can tell at least. Really, using CursorLoader gets a lot easier if you use a LoaderManager with it.

If you don't want to mess around with loaders and background-threads, then an easier approach is to set a listener with registerListener(int, OnLoadCompleteListener) and start the loading with startLoading(). This automatically handles all threading for you, and might spare you some code.

like image 97
pgsandstrom Avatar answered Sep 27 '22 22:09

pgsandstrom


You are hacking the MVC pattern, you are getting this error because you are trying to update UI component from your onBackground Method in the AsynTask. Move any UI or initialisation to Post or Pre execute method. it seems that :

 CursorLoader imageLoader = new CursorLoader(mActivity);

must be place on the protected void onPreExecute () method.

like image 37
Anis BEN NSIR Avatar answered Sep 27 '22 23:09

Anis BEN NSIR