サーバーからデータをプルし、ユーザー入力に応じて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の挿入メソッドは同期されているため、問題が発生することはありません)。
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"/>
正常に動作しますが、それでもUIスレッドで動作しているようです。それは私が本当にこれで私の髪を引き裂き始めたポイントです、なぜならそれは私には全く意味がないからです。私が何をしても、挿入中は常にUIがハングします。彼らをそうさせない方法はありますか?