2011-12-06 98 views
4

我的一個活動中的按鈕調用一個AsyncTask,它更新ListView的SimpleCursorAdapter的底層遊標。每次點擊按鈕,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之一的堆棧跟蹤(這對所有的人都一樣):

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,您會發現它將停止創建新線程,並且會重新使用舊線程。

更新,以解決一些細節:

你可能會認爲,如果有免費的線程池,它不會創造新的,但是這是不完全正確。這個想法是有一定數量的線程有助於處理異步任務。這被稱爲核心池大小。在Android的AsyncTask情況下,他們似乎已經將其設置爲5,如果你看一下文檔ThreadPoolExecutor它說:

當一個新的任務在方法提交執行(Runnable接口),以及少於corePoolSize線程運行時,即使其他工作線程空閒,也會創建一個新線程來處理該請求。

還有一個最大合適的稱爲最大池大小。

+0

這是真的,但如果池中有一些空閒線程,爲什麼要創建新線程?它不應該那樣做。 – inazaruk

+0

增加了一些更多的細節來解決你的問題。 – kabuko

+0

看起來你是對的。 AsyncTask的核心池大小爲5(請參閱http://goo.gl/i2QMa)。 – inazaruk

1

@ kabuko說的是真實的,但我也認爲在開始新任務之前取消任務是個好習慣。如果舊任務中的一個繼續進行,您可能會有一些奇怪的行爲。更重要的是,在你的情況下,你不想多次查詢你的數據庫,這是沒用的。你可以換調用異步任務在這樣的方法:

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

哎呀..再次讀你的代碼,現在它是有道理的。謝謝。 –