ContentProvider insert () всегда работает в потоке пользовательского интерфейса?

У меня есть приложение, которое должно извлекать данные с сервера и вставлять их в базу данных SQLite в ответ на ввод пользователя. Я думал, что это будет довольно просто – код, который извлекает данные с сервера, является довольно простым подклассом AsyncTask, и он работает точно так, как я ожидаю, чтобы он не висел в потоке пользовательского интерфейса. Я реализовал функцию обратного вызова для него с помощью простого интерфейса и завернул его в статический класс, поэтому мой код выглядит так:

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

Все по-прежнему хорошо. Даже если серверу требуется час для извлечения данных, пользовательский интерфейс все равно выполняется гладко, потому что код в getFolderContents запущен в методе doInBackground для AsyncTask (который находится в отдельном потоке от пользовательского интерфейса). В самом конце метода getFolderContents вызывается onFolderContentsResponse и передается список файлов FilesystemEntry, полученных с сервера. Я просто говорю все это так, что, надеюсь, ясно, что моя проблема заключается не в методе getFolderContents, а в любом из моих сетевых кодов, потому что там никогда не бывает.

Проблема возникает, когда я пытаюсь вставить в базу данных через мой подкласс ContentProvider в методе onFolderContentsResponse; Пользовательский интерфейс всегда зависает во время выполнения этого кода, заставляя меня поверить, что, несмотря на вызов из метода doInBackground для AsyncTask, вставки как-то все еще работают в потоке пользовательского интерфейса. Вот как выглядит проблематичный код:

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

И метод 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); } } 

Где mContentResolver ранее был установлен на результат метода getContentResolver ().

Я пробовал помещать insertContentsIntoDB в свой собственный поток, например:

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

Я также попытался запустить каждую отдельную вставку в своем потоке (метод insert в MyContentProvider синхронизирован, поэтому это не должно вызывать никаких проблем):

 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(); } } 

И только для хорошей меры я также попробовал оба этих решения с соответствующим кодом в методе doInBackground другого AsyncTask. Наконец, я явно определил MyContentProvider как живущий в отдельном процессе в моем AndroidManifest.xml:

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

Он работает нормально, но он все еще работает в потоке пользовательского интерфейса . Это тот момент, когда я действительно начал вырывать волосы из-за этого, потому что это не имеет никакого смысла для меня. Независимо от того, что я делаю, пользовательский интерфейс всегда висит во время вставок. Есть ли способ заставить их не делать этого?

Solutions Collecting From Web of "ContentProvider insert () всегда работает в потоке пользовательского интерфейса?"

Вместо вызова mContentResolver.insert() используйте AsyncQueryHandler и его startInsert() . AsyncQueryHandler предназначен для облегчения асинхронных запросов ContentResolver .

Я думаю, что ваша первоначальная проблема, возможно, заключалась в том, что вы вызываете метод run в своем новом потоке (который вызывает выполнение для продолжения в текущем потоке) вместо вызова метода start . Я думаю, это то, что Яркий Великий пытался сказать в его / ее ответе. См. Раздел Разница между запуском и запуском потока . Это распространенная ошибка.

Man.Relax yourself.And все будет выглядеть лучше. Сначала «Начать поток» – это запуск Func, а не запуск Func, если вы хотите запустить новый поток, вызывается не только запуск func.

 new Thread(Runnable runnable).start(); 

Тогда я уверен, что использование Handler иногда было бы лучше, чем AsyncTask.

Вы можете запустить запрос в переопределенном методе doInBackground(Integer int) AsynTask и обновить основной пользовательский интерфейс onPostExecute(Integer int) .