У меня есть приложение, которое должно извлекать данные с сервера и вставлять их в базу данных 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();
}
}
Я также пробовал запускать каждую отдельную вставку в отдельном потоке (, метод вставки в 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"/>
. Он работает нормально, но по-прежнему работает в потоке пользовательского интерфейса . Это тот момент, когда я действительно начал рвать на себе волосы из-за этого, потому что это не имеет для меня никакого смысла. Что бы я ни делал, UI всегда зависает во время вставок. Есть ли способ заставить их не делать этого?