[翻译]  ContentProvider insert() always runs on UI thread?

[CHINESE]  ContentProvider insert()总是在UI线程上运行?


I have an app that needs to pull data from a server and insert it into an SQLite database in response to user input. I thought this would be pretty simple - the code that pulls the data from the server is a fairly straightforward subclass of AsyncTask, and it works exactly as I expect it to without hanging the UI thread. I implemented callback functionality for it with a simple interface and wrapped it in a static class, so my code looks like this:

我有一个应用程序需要从服务器提取数据并将其插入SQLite数据库以响应用户输入。我认为这很简单 - 从服务器提取数据的代码是AsyncTask的一个相当简单的子类,它完全按照我的预期工作,而不会挂起UI线程。我使用一个简单的接口为它实现了回调功能,并将其包装在一个静态类中,所以我的代码如下所示:

MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
    @Override
    public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
        // do something with contents
    }
}

All still good. Even if the server takes an hour to retrieve the data, the UI still runs smoothly, because the code in getFolderContents is running in the doInBackground method of an AsyncTask (which is in a separate thread from the UI). At the very end of the getFolderContents method, the onFolderContentsResponse is called and passed the list of FilesystemEntry's that was received from the server. I only really say all this so that it's hopefully clear that my problem is not in the getFolderContents method or in any of my networking code, because it doesn't ever occur there.

一切都还不错。即使服务器需要一个小时来检索数据,UI仍然可以顺利运行,因为getFolderContents中的代码在AsyncTask的doInBackground方法中运行(该方法位于与UI不同的线程中)。在getFolderContents方法的最后,调用onFolderContentsResponse并传递从服务器接收的FilesystemEntry列表。我只是真的说了这一切,所以希望我的问题不在getFolderContents方法或我的任何网络代码中,因为它不会在那里发生。

The problem arises when I try to insert into a database via my subclass of ContentProvider within the onFolderContentsResponse method; the UI always hangs while that code is executing, leading me to believe that despite being called from the doInBackground method of an AsyncTask, the inserts are somehow still running on the UI thread. Here's what the problematic code looks like:

当我尝试通过onFolderContentsResponse方法中的ContentProvider子类插入数据库时​​出现问题;在执行该代码时,UI总是挂起,这让我相信尽管从AsyncTask的doInBackground方法调用,插入仍然在UI线程上运行。以下是有问题的代码:

MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
    @Override
    public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
        insertContentsIntoDB(contents);
    }
}

And the insertContentsIntoDB method:

和insertContentsIntoDB方法:

void insertContentsIntoDB(final List<FilesystemEntry> contents) {
    for (FilesystemEntry entry : contents) {
        ContentValues values = new ContentValues();
        values.put(COLUMN_1, entry.attr1);
        values.put(COLUMN_2, entry.attr2);
        // etc.

        mContentResolver.insert(MyContentProvider.CONTENT_URI, values);
    }
}

where mContentResolver has been previously set to the result of the getContentResolver() method.

其中mContentResolver先前已设置为getContentResolver()方法的结果。

I've tried putting insertContentsIntoDB in its own Thread, like so:

我已经尝试将insertContentsIntoDB放在自己的Thread中,如下所示:

MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() {
    @Override
    public void onFolderContentsResponse(final List<FilesystemEntry> contents) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                insertContentsIntoDB(contents);
            }
        }).run();
    }
}

I've also tried running each individual insert in its own thread (the insert method in MyContentProvider is synchronized, so this shouldn't cause any issues there):

我也尝试在自己的线程中运行每个单独的插入(MyContentProvider中的insert方法是同步的,所以这不应该导致任何问题):

void insertContentsIntoDB(final List<FilesystemEntry> contents) {
    for (FilesystemEntry entry : contents) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                ContentValues values = new ContentValues();
                values.put(COLUMN_1, entry.attr1);
                values.put(COLUMN_2, entry.attr2);
                // etc.
                mContentResolver.insert(MyContentProvider.CONTENT_URI, values);
            }
        }).run();
    }
}

And just for good measure, I've also tried both of those solutions with the relevant code in the doInBackground method of another AsyncTask. Finally, I've explicitly defined MyContentProvider as living in a separate process in my AndroidManifest.xml:

只是为了好的衡量,我还尝试了这两个解决方案与另一个AsyncTask的doInBackground方法中的相关代码。最后,我明确地将MyContentProvider定义为生活在AndroidManifest.xml中的单独进程中:

<provider android:name=".MyContentProvider" android:process=":remote"/>

It runs fine, but it still seems to run in the UI thread. That's the point where I really started tearing my hair out over this, because that doesn't make any sense at all to me. No matter what I do, the UI always hangs during the inserts. Is there any way to get them not to?

它运行正常,但它似乎仍然在UI线程中运行。这就是我真正开始将头发撕裂的重点,因为这对我来说根本没有任何意义。无论我做什么,UI在插入过程中总是挂起。有没有办法让他们不去?

4 个解决方案

#1


52  

Instead of calling mContentResolver.insert(), use AsyncQueryHandler and its startInsert() method. AsyncQueryHandler is designed to facilitate asynchronous ContentResolver queries.

而不是调用mContentResolver.insert(),而是使用AsyncQueryHandler及其startInsert()方法。 AsyncQueryHandler旨在促进异步ContentResolver查询。

#2


3  

I think your original problem may have been that you are calling the run method on your new thread (which causes execution to continue on the current thread) instead of calling the start method. I think this is what Bright Great was trying to say in his/her answer. See Difference between running and starting a thread. It's a common mistake.

我认为你原来的问题可能是你在新线程上调用run方法(导致执行在当前线程上继续),而不是调用start方法。我认为这就是Bright Great在他/她的回答中试图说的。请参阅运行和启动线程之间的区别。这是一个常见的错误。

#3


0  

Man.Relax yourself.And anything would looks better. At first,Start a Thread is Func start not Func run,if you want to start the new Thread not only call the func run.

Man.Relax yourself.And任何东西看起来都会更好。首先,Start a Thread是Func start而不是Func run,如果你想启动新的Thread,不仅要调用func run。

new Thread(Runnable runnable).start();

Then I bet use Handler sometimes would be better than AsyncTask.

然后我打赌使用Handler有时会比AsyncTask更好。

#4


0  

You can run the query in the doInBackground(Integer int) overridden method of the AsynTask, and update the main UI on the onPostExecute(Integer int) method.

您可以在AsynTask的doInBackground(Integer int)重写方法中运行查询,并在onPostExecute(Integer int)方法上更新主UI。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2018 ITdaan.com 粤ICP备14056181号