Подтвердить что ты не робот

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"/>

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

4b9b3361

Ответ 1

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

Ответ 2

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

Ответ 3

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

new Thread(Runnable runnable).start();

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

Ответ 4

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