サーバーからデータをプルし、ユーザー入力に応じて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がハングします。彼らをそうさせない方法はありますか?
mContentResolver.insert()
を呼び出す代わりに、 AsyncQueryHandler
とその startInsert()
メソッドを使用します。 AsyncQueryHandler
は、非同期のContentResolver
クエリを容易にするように設計されています。
元々の問題は、run
メソッドを呼び出す代わりに、新しいスレッドでstart
メソッドを呼び出していることである可能性があります(これにより、現在のスレッドで実行が続行されます)。これがブライトグレートが彼/彼女の答えで言おうとしていたことだと思います。 スレッドの実行と開始の違い を参照してください。よくある間違いです。
男。リラックスしてください。そして何でも良く見えます。最初は、スレッドの開始はFuncrunではなくFuncstartです。新しいスレッドを開始する場合は、funcrunを呼び出すだけではありません。
new Thread(Runnable runnable).start();
次に、Handlerを使用する方がAsyncTaskよりも優れている場合があると思います。
AsyncTask のdoInBackground(Integer int)
オーバーライドされたメソッドでクエリを実行し、onPostExecute(Integer int)
メソッドのメインUIを更新できます。