2011-12-06 8 views
4

私のアクティビティの1つのボタンは、ListViewのSimpleCursorAdapterの基になるカーソルを更新するAsyncTaskを呼び出します。ボタンをクリックするたびに、AsyncTaskの新しいスレッドが追加され、タスクが完了します(「待機」状態になります)。ボタンを5回以上クリックすると、5つのAsyncTasksが「待機」状態でそこに座ります。これは正常ですか、どこかでメモリリークがありますか?AsyncTaskは待機し続けますか?

AsyncTask

private class updateAdapter extends AsyncTask<Void, Void, Void> { 

    @Override 
    protected Void doInBackground(Void... params) { 
     // Open database connection 
     if(_db == null || !_db.isOpen()) _db = new DatabaseWrapper(ActivityShowWOD.this).getWritableDatabase(); 
     Cursor WODcursor; 

     // Check if a wod_id is set 
     if(_wod_id == -1) { 
      // Grab filters from preferences and at the same time build SQLselection string 
      SharedPreferences prefs = getSharedPreferences("Preferences", 0); 
      String[] filterNames = getResources().getStringArray(R.array.filters_values); 
      boolean[] filterValues = new boolean[filterNames.length]; 
      String SQLselection = ""; 

      for (int i = 0; i < filterNames.length; i++) { 
       filterValues[i] = prefs.getBoolean(filterNames[i], false); 

       // Build SQL query 
       if(filterValues[i] == true) { 
        SQLselection += filterNames[i] + " = 1 OR " + filterNames[i] + " = 0"; 
       } else { 
        SQLselection += filterNames[i] + " = 0"; 
       } 

       // Add an "AND" if there are more filters 
       if(i < filterNames.length - 1) SQLselection += " AND "; 
      } 

      // Get all WODs matching filter preferences 
      WODcursor = _db.query(DatabaseConstants.TBL_WORKOUTS, 
             new String[] { DatabaseConstants.WORKOUTS_ID, DatabaseConstants.WORKOUTS_NAME, 
                DatabaseConstants.WORKOUTS_NOTES, DatabaseConstants.WORKOUTS_CFID }, 
             SQLselection, null, null, null, null); 

      // Move the Cursor to a random position 
      Random rand = new Random(); 
      WODcursor.moveToPosition(rand.nextInt(WODcursor.getCount())); 

      // Store wod_id 
      _wod_id = WODcursor.getInt(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_ID)); 
     } else { 
      // Get the cursor from the wod_id 
      WODcursor = _db.query(DatabaseConstants.TBL_WORKOUTS, 
           new String[] { DatabaseConstants.WORKOUTS_ID, DatabaseConstants.WORKOUTS_NAME, 
               DatabaseConstants.WORKOUTS_NOTES, DatabaseConstants.WORKOUTS_CFID }, 
           DatabaseConstants.WORKOUTS_ID + " = " + _wod_id, null, null, null, null); 

      WODcursor.moveToFirst(); 
     } 

     // Store WOD information into class instance variables and close cursor 
     _wod_cfid = WODcursor.getInt(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_CFID)); 
     _wod_name = WODcursor.getString(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_NAME)); 
     _wod_notes = WODcursor.getString(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_NOTES)); 
     WODcursor.close(); 

     // Return all exercises pertaining to this WOD 
     _excCursor = _db.query(DatabaseConstants.TBL_EXERCISES, 
           new String[] { DatabaseConstants.EXERCISES_ID, DatabaseConstants.EXERCISES_EXERCISE, 
              DatabaseConstants.EXERCISES_REPS, DatabaseConstants.EXERCISES_NOTES }, 
           DatabaseConstants.EXERCISES_WOD_ID + " = " + _wod_id, null, null, null, 
           DatabaseConstants.EXERCISES_ID + " ASC"); 
     return null; 
    } 

    @Override 
    protected void onPostExecute(Void result) { 
     _adapter.changeCursor(_excCursor); 
     _adapter.notifyDataSetChanged(); 
     _WODlist.setOnItemClickListener(new WODlistClickListener()); 
    } 

} 

と(活動が最初にロードされた)タスク呼び出し、私のonCreateのコード:

upAdapter = new updateAdapter().execute(); 

、ボタンonClickListener中を:

  // Reset wod_id 
      _wod_id = -1; 

      // Update the underlying SimpleCursorAdapter 
      upAdapter = new updateAdapter().execute(); 

AsyncTaskの1のスタックトレース(それはそれらのすべてに同じです):

Object.wait(long, int) line: not available [native method] 
Thread.parkFor(long) line: 1535 
LangAccessImpl.parkFor(long) line: 48 
Unsafe.park(boolean, long) line: 317  
LockSupport.park() line: 131  
AbstractQueuedSynchronizer$ConditionObject.await() line: 1996 
LinkedBlockingQueue.take() line: 359  
ThreadPoolExecutor.getTask() line: 1001 
ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1061 
ThreadPoolExecutor$Worker.run() line: 561 
Thread.run() line: 1096 
+0

実行しているコードの一部を表示できますか?各 'AsyncTask'で' execute() 'を呼び出すか、オブジェクトを作成するだけですか? – Devunwired

+0

スリープ状態の 'AsynTask'スレッドに対して完全なスタックトレースを投稿できますか?私の経験から、彼らの多くは駐車してはいけません。 – inazaruk

+0

また、どのAndroidバージョンでこれをテストしていますか? – inazaruk

答えて

8

AsyncTaskボンネットの下にはThreadPoolExecutorを使用しています。これらのスレッドは、スレッドをあまりにも頻繁に作成して引き裂くことを無駄にしてしまうため、少し離れてしまうことはありません。しばらくすると、AsyncTasksをさらに作成すると、新しいスレッドの作成が停止し、古いスレッドが再利用されます。いくつかの詳細に対処するための

更新:

あなたは無料のスレッドがプール内に存在する場合、それは新しいものを作成していないと思うだろうが、これはまさに真実ではありません。考え方は、非同期タスクを処理し続けるのに便利なスレッドがあることです。これはコアプールサイズと呼ばれます。 corePoolSizeスレッドがあるよりも、新しいタスクが(Runnableを)executeメソッドで送信されると

を、より少ない:AndroidのAsyncTask場合、彼らはあなたがThreadPoolExecutorのドキュメントを見れば、それは言う5に設定しているように見えます他のワーカースレッドがアイドル状態であっても、要求を処理するための新しいスレッドが作成されます。

また、最大プールサイズと呼ばれる最大限のものもあります。

+0

これは当てはまりますが、プールに空きスレッドがいくつかある場合、新しいプールを作成するのはなぜですか?それは実際にはそうすべきではありません。 – inazaruk

+0

あなたの質問に対処するための詳細をいくつか追加しました。 – kabuko

+0

あなたが正しいように見えます。 AsyncTaskのコアプールサイズは5です(http://goo.gl/i2QMa参照)。 – inazaruk

1

@ kabukoが言っていることは本当ですが、新しいものを開始する前にタスクをキャンセルすることをお勧めします。あなたは古いタスクの1つが継続している場合に備えて、いくつかの奇妙な振る舞いを持つことができます。さらに、あなたのケースでは、dbを複数回クエリしたくない場合、それは役に立たないでしょう。

AsyncDataLoading loaderTask = null; 

private void runTask(){ 
    if (loaderTask!=null && loaderTask.getStatus().compareTo(Status.FINISHED)!=0) { 
     loaderTask.cancel(true); 
    } 
    loaderTask = new AsyncDataLoading(); 
    loaderTask.execute(); 
} 

それもボタンを無効にして、非同期タスクが実行されたときに、それを再度有効にすることをお勧め:あなたはこのような方法で非同期タスクへの呼び出しをラップすることができます。

とにかく、このソリューションはアーキテクチャに適していない可能性があります。私はコードについて十分に分かりません。とにかく助けてくれることを願っています。

+0

アドバイスありがとう、私は(AsyncTaskは私のために完全に新しいです)あなたが投稿したコードを理解していない、AsyncDataLoadingはAndroidライブラリから来ていないようですか? –

+1

問題ありません。 AsyncDataLoadingは、AsyncTaskを拡張するクラスの名前だけです。あなたのケースでは "updateAdapter" ... – gwa

+0

おっと..あなたのコードをもう一度読んで、それは今や理にかなっています。ありがとう。 –

関連する問題