35

私は、サーバーからデータを取り出し、ユーザーの入力に応じてSQLiteデータベースに挿入する必要のあるアプリケーションを持っています。私はこれがかなりシンプルであると考えました。サーバーからデータを取得するコードは、AsyncTaskのかなり単純なサブクラスであり、UIスレッドを掛けることなく期待通りに機能します。私はシンプルなインターフェイスでコールバック機能を実装し、静的なクラスでラップしたので、私のコードは次のようになります:ContentProvider insert()は常にUIスレッドで実行されますか?

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

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

問題は、onFolderContentsResponseメソッド内のContentProviderのサブクラスを介してデータベースに挿入しようとすると発生します。 AsyncTaskのdoInBackgroundメソッドから呼び出されているにもかかわらず、挿入が何らかの形でUIスレッド上で実行されていると私は思っています。

MyServerCaller.getFolderContents(folderId, new OnFolderContentsResponseListener() { 
    @Override 
    public void onFolderContentsResponse(final List<FilesystemEntry> contents) { 
     insertContentsIntoDB(contents); 
    } 
} 

そしてinsertContentsIntoDB方法:mContentResolverが以前getContentResolver()メソッドの結果に設定されている

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

ここでは、問題のコードは次のようになります。

私はそうのように、独自のスレッドで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メソッドが同期されるので、このshouldnを実行しようとしました「トンがそこにすべての問題を引き起こす):

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メソッドでは、関連するコードでこれらのソリューションの両方を試してみました。最後に、私は明示的に私のAndroidManifest.xmlに別のプロセスに住むようMyContentProviderを定義しました:

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

それは細かい動作しますが、はまだUIスレッドで実行するようです。それが私にとって本当に意味をなさないので、これは私が本当にこの上に私の髪を引き裂き始めたポイントです。私が何をするにしても、UIは挿入時に常にハングします。それをしないようにする方法はありますか?

+0

仮説を検証するために、ContentProviderから現在のスレッドID /名前を出力します。そして/またはデバッガを使用して、実行中のスレッドをチェックします。 –

答えて

52

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

+0

本当にありがとう、それは私が探していたものです! – jcsmnt0

+0

@ jcsmnt0助けてくれてうれしいよ♪ –

+0

ありがとう! D – passsy

0

Man.Relax yourself.And何かが良く見えます。 新しいスレッドを開始したい場合、最初にスレッドを開始することはFuncを起動しません。 はfuncを呼び出すだけでなく、実行します。

new Thread(Runnable runnable).start(); 

次に、ハンドラがAsyncTaskよりも優れていると思います。

+0

ハンドラは常に親スレッドで実行され、バックグラウンドでは実行されません。 –

0

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

3

startメソッドを呼び出すのではなく、新しいスレッド(実行を現在のスレッドで続行する)でrunメソッドを呼び出している可能性があります。私はこれがブライト・グッドが彼/彼女の答えで言っていることだと思う。 Difference between running and starting a threadを参照してください。それはよくある間違いです。

関連する問題