asynctask中的CursorLoader,无法在未调用Looper.prepare的线程内创建处理程序

时间:2012-10-24 14:28:31

标签: android android-asynctask

在我的应用程序中,我打电话给相机应用程序拍照。 在我的活动结果中,我启动了一个asynctask来处理照片。 (旋转并上传照片)。

我还想从图库中删除照片。 为此,我在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();

我收到错误

  

无法在未调用Looper.prepare

的线程内创建处理程序

导致此错误的原因是什么?如何解决? 它是否与我调用loadInBackground这一事实有关,而asynctask已经在后台运行了?

这是我的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)

3 个答案:

答案 0 :(得分:11)

实际上,接受的答案是不正确的。您似乎对发生这种情况的原因感兴趣,它也可能有助于您的应用程序的响应性,所以我会给出另一个答案:

您没有改变用户界面,因此这不是导致此次崩溃的原因。原因是您的后台线程没有与之关联的Looper,因为错误说明了。 looper是偶尔检查消息的消息队列。例如,当您使用Handler时,它就是您使用的。

只需在崩溃应用程序的调用之前添加以下代码行即可添加looper:

Looper.prepare();

在后台线程上而不是在UI线程上执行大量查询有一个很大的优势,因为在加载游标时,UI不会响应。如果响应对您很重要,则应在后台线程上调用db。

现在,在这种情况下,CursorLoader确实没有理由需要looper。就我所知,至少只是笨拙的实现,抛出异常。实际上,如果你使用LoaderManager,使用CursorLoader会更容易。

如果您不想使用加载器和后台线程,那么更简单的方法是使用registerListener(int, OnLoadCompleteListener)设置一个侦听器并使用startLoading()开始加载。这会自动为您处理所有线程,并可能为您节省一些代码。

答案 1 :(得分:2)

您正尝试在后台线程中执行UI操作,您应该在UI线程中运行它。

答案 2 :(得分:2)

您正在攻击MVC模式,因为您正在尝试从AsynTask中的onBackground方法更新UI组件,因此您收到此错误。 将任何UI或初始化移动到Post或Pre execute方法。 看来:

 CursorLoader imageLoader = new CursorLoader(mActivity);

必须放在protected void onPreExecute () method.