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

6

在我的应用程序中,我调用库存相机应用程序拍照。在我的活动结果中,我启动一个异步任务来处理照片(旋转和上传照片)。

我还想从图库中删除照片。为此,在我的异步任务中执行以下代码。

// 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的线程中创建处理程序

这个错误是由什么引起的?我应该如何解决它? 这是否与我在后台运行asynctask的同时调用loadInBackground有关?

这是我的日志信息:

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)

可能是重复的问题,参考 https://dev59.com/fG445IYBdhLWcg3wKHAJ - sandrstar
3个回答

13

实际上,被接受的答案是不正确的。你好像对为什么会发生这种情况感兴趣,这也可能有助于提高应用程序的响应性,因此我将给出另一个答案:

你没有改变UI,因此这不是导致崩溃的原因。原因是你的后台线程没有与其关联的Looper,正如错误所述。Looper是一个定期检查消息的消息队列。例如,当你使用Handler时就会用到它。

你可以通过在导致应用程序崩溃的调用之前添加以下代码来添加一个Looper:

Looper.prepare();

在后台线程上执行重型查询比在UI线程上执行要有很大的优势,因为当光标正在加载时,您的UI将无响应。如果对响应性很重要,应该在后台线程上调用数据库。

现在,在这种情况下,CursorLoader真的没有必要需要一个looper。据我所知,抛出异常只是笨拙的实现方式。如果使用LoaderManager,使用CursorLoader会变得更加容易。

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


谢谢您的额外解释,我会研究LoaderManager类。添加Looper.prepare()代码行已经修复了错误。 - Robby Smet
添加 Looper.prepare(); 将会将您的线程附加到 UI 线程。在这种情况下不再使用 AsynTask。改用示例线程或处理程序。 - Anis BEN NSIR

3

您正在入侵MVC模式,因为您正尝试在AsynTask的onBackground方法中更新UI组件,所以导致此错误。请将任何UI或初始化移至Post或Pre execute方法。

看起来是这样:

 CursorLoader imageLoader = new CursorLoader(mActivity);

必须放置在protected void onPreExecute ()方法中。

2

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


https://dev59.com/iVfUa4cB1Zd3GeqPHFha 包含了一些关于如何实现你所尝试的功能的好信息。 - mfsiega

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接