web-dev-qa-db-ja.com

ContentProvider insert()は常にUIスレッドで実行されますか?

サーバーからデータをプルし、ユーザー入力に応じてSQLiteデータベースに挿入する必要があるアプリがあります。これは非常に単純だと思いました。サーバーからデータをプルするコードはAsyncTaskのかなり単純なサブクラスであり、UIスレッドをハングさせることなく期待どおりに機能します。シンプルなインターフェイスでコールバック機能を実装し、静的クラスにラップしたので、コードは次のようになります。

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

すべてまだ良いです。サーバーがデータを取得するのに1時間かかる場合でも、getFolderContentsのコードはAsyncTaskのdoInBackgroundメソッド(UIとは別のスレッドにあります)で実行されているため、UIはスムーズに実行されます。 getFolderContentsメソッドの最後で、onFolderContentsResponseが呼び出され、サーバーから受信したFilesystemEntryのリストが渡されます。私は本当にこれをすべて言っているので、私の問題がgetFolderContentsメソッドやネットワークコードのいずれにも発生していないことが明らかになることを願っています。

OnFolderContentsResponseメソッド内のContentProviderのサブクラスを介してデータベースに挿入しようとすると、問題が発生します。そのコードの実行中は常にUIがハングし、AsyncTaskのdoInBackgroundメソッドから呼び出されたにもかかわらず、挿入はUIスレッドで実行されていると私は信じています。問題のあるコードは次のようになります。

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

また、念のため、別のAsyncTaskのdoInBackgroundメソッドの関連コードを使用してこれらのソリューションの両方を試しました。最後に、MyContentProviderをAndroidManifest.xmlの別のプロセスに存在するものとして明示的に定義しました。

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

正常に実行されますが、Iスレッドで実行されているようです。それは私が本当にこれで私の髪を引き裂き始めたポイントです、なぜならそれは私には全く意味がないからです。私が何をしても、挿入中は常にUIがハングします。彼らをそうさせない方法はありますか?

35
jcsmnt0

mContentResolver.insert()を呼び出す代わりに、 AsyncQueryHandler とその startInsert() メソッドを使用します。 AsyncQueryHandlerは、非同期のContentResolverクエリを容易にするように設計されています。

52
Gunnar Karlsson

元々の問題は、runメソッドを呼び出す代わりに、新しいスレッドでstartメソッドを呼び出していることである可能性があります(これにより、現在のスレッドで実行が続行されます)。これがブライトグレートが彼/彼女の答えで言おうとしていたことだと思います。 スレッドの実行と開始の違い を参照してください。よくある間違いです。

3
Ellen Spertus

男。リラックスしてください。そして何でも良く見えます。最初は、スレッドの開始はFuncrunではなくFuncstartです。新しいスレッドを開始する場合は、funcrunを呼び出すだけではありません。

new Thread(Runnable runnable).start();

次に、Handlerを使用する方がAsyncTaskよりも優れている場合があると思います。

0
Bright Great

AsyncTaskdoInBackground(Integer int)オーバーライドされたメソッドでクエリを実行し、onPostExecute(Integer int)メソッドのメインUIを更新できます。

0
shivin satyan