2012-11-13 7 views
5

La classe AsyncTask avvia molti altri thread perché sto utilizzando una libreria di terze parti. Non sono sicuro di quanti thread apre la libreria, ma voglio aspettare che finiscano tutti prima di compilare la mia listview con i dati che ottengono.La mia classe AsyncTask contiene altri thread. Come posso aspettare che finiscano tutti i suoi thread prima di eseguire un metodo seguente?

Al momento sto dormendo 10000 millisecondi ma questo non è pratico perché non so quanto sia grande la lista.

Qual è la soluzione corretta per questo problema?

task = new mTask(); 
     task.execute(appsList); 
     new Thread(new Runnable() { 
      public void run() { 
       populateList(); 
      } 
     }).start(); 

    } 

    private class mTask extends AsyncTask<List<ApplicationInfo>, Void, Void> { 
     ProgressDialog progress; 

     @Override 
     protected void onPreExecute() { 
      progress = new ProgressDialog(MainActivity.this); 
      progress.setIndeterminate(true); 
      progress.show(); 
      super.onPreExecute(); 
     } 

     @SuppressWarnings("deprecation") 
     @Override 
     protected Void doInBackground(List<ApplicationInfo>... params) { 
      appDataManager = new AppDataManager(MainActivity.this, 
        mySQLiteAdapter, MainActivity.this); 
      appDataManager.work(params[0]); 
      return null; 
     } 

     @Override 
     protected void onPostExecute(Void result) { 
      mySQLiteAdapter.close(); 
      progress.dismiss(); 
      super.onPostExecute(result); 
     } 
    } 

    @SuppressWarnings("deprecation") 
    public void populateList() { 
     try { 
      Thread.sleep(10000) 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     runOnUiThread(new Runnable() { 

      public void run() { 

       Cursor cursor; 
       mySQLiteAdapter.openToRead(); 
       cursor = mySQLiteAdapter.queueAll(); 

       ArrayList<String> appsList = new ArrayList<String>(); 
       if (cursor.moveToFirst()) { 
        do { 
         appsList.add(cursor.getString(1)); 
        } while (cursor.moveToNext()); 
       } 
       cursor.moveToFirst(); 

       ArrayAdapter<String> adp = new ArrayAdapter<String>(
         MainActivity.this, android.R.layout.simple_list_item_1, 
         appsList); 
       listContent.setAdapter(adp); 
       cursor.close(); 
       mySQLiteAdapter.close(); 

       Log.i("finished", "finished"); 
     } 
     }); 

    } 

AppDataManager

public void work(List<ApplicationInfo> appsList) { 
    List<ApplicationInfo> appList = appsList; 
    mySQLiteAdapter.openToWrite(); 
    mySQLiteAdapter.deleteAll(); 
    mySQLiteAdapter.close(); 
    for (int i = 0; i < 5; i++) { 
     String name = appList.get(i).name; 
     String pack = appList.get(i).packageName; 
     // TODO AsyncTask 
     getHtml(pack, name); 
    } 

} 

public void getHtml(final String pack, final String name) { 
    String url = MARKET_URL + pack; 
      //AndroidQuery library. fetch html 
    aq.ajax(url, String.class, 1000, new AjaxCallback<String>() { 
     @Override 
     public void callback(String url, String htm, AjaxStatus status) { 
      Log.i("status", status.getMessage()); 
      parseHtml(htm, pack, name); 

     } 
    }); 
} 
+0

cosa vuol appDataManager fare? – user1049280

+0

@ user1049280 recuperare i dati da internet – code511788465541441

+2

fa l'API per la libreria di terze parti hanno un meccanismo di avocazione per segnalare il completamento dei lavori ? –

risposta

3

Innanzitutto, trasferisco la chiamata allo populateList nel metodo del numero AsyncTask. Vorrei anche riscrivere populateList per rimuovere la sospensione e presumere che sia in esecuzione sul thread dell'interfaccia utente (eliminare la chiamata runOnUiThread e spostare il corpo del metodo run direttamente in populateList).

Ora per impedire a AsyncTask di completare doInBackground finché il AppDataManager termina il suo lavoro. Inizia definendo una bandiera completamento e un oggetto di blocco su cui è possibile eseguire la sincronizzazione:

private class mTask extends AsyncTask<List<ApplicationInfo>, Void, Void> { 
    boolean complete; 
    static Object LOCK = new Object(); 
    . . . 
} 

quindi modificare la classe AppDataManager per fornire un call-back su un altro oggetto quando il lavoro è fatto. Definire un campo callback e aggiornare il vostro api e metodi:

public void work(List<ApplicationInfo> appsList, Runnable callback) { 
    this.callback = callback; // define a field named "callback" 
    List<ApplicationInfo> appList = appsList; 
    mySQLiteAdapter.openToWrite(); 
    mySQLiteAdapter.deleteAll(); 
    mySQLiteAdapter.close(); 
    for (int i = 0; i < 5; i++) { 
     String name = appList.get(i).name; 
     String pack = appList.get(i).packageName; 
     // TODO AsyncTask 
     getHtml(pack, name); 
    } 

} 

public void getHtml(final String pack, final String name) { 
    String url = MARKET_URL + pack; 
      //AndroidQuery library. fetch html 
    aq.ajax(url, String.class, 1000, new AjaxCallback<String>() { 
     @Override 
     public void callback(String url, String htm, AjaxStatus status) { 
      Log.i("status", status.getMessage()); 
      parseHtml(htm, pack, name); 
      if (callback != null) { 
       callback.run(); 
      } 
     } 
    }); 
} 

Ora modificare il metodo di doInBackground aspettare che il flag da impostare:

protected Void doInBackground(List<ApplicationInfo>... params) { 
    appDataManager = new AppDataManager(MainActivity.this, 
      mySQLiteAdapter, MainActivity.this); 
    appDataManager.work(params[0], new Runnable() { 
     public void run() { 
      synchronized (LOCK) { 
       complete = true; 
       LOCK.notifyAll(); 
      } 
     } 
    }); 
    // wait for appDataManager.work() to finish... 
    synchronized (LOCK) { 
     while (!complete) { 
      LOCK.wait(); 
     } 
    } 
    return null; 
} 

Questo dovrebbe fare il lavoro. Tuttavia, probabilmente dovresti approfondire questo argomento per gestire errori di vario genere (ad esempio, fornire un meccanismo di notifica di errore per AppDataManager).

UPDATE Ho finalmente notato che stai facendo cinque transazioni di rete in AppDataManager. Pertanto, anziché eseguire immediatamente la richiamata all'interno del metodo di callback per ajax(), diminuire un contatore e richiamare solo quando il contatore raggiunge 0. Inizializzare il contatore su 5 in work() prima di immettere il ciclo che chiama getHtml(). Poiché il contatore verrà modificato da thread separati, l'accesso ad esso deve essere sincronizzato. (In alternativa, è possibile utilizzare un AtomicInteger per il contatore.

+0

Grazie per quello. Ha funzionato con una leggera modifica. poiché 'getHtml()' viene chiamato in un ciclo, 'complete' viene impostato su true alla prima esecuzione. Ho aggiunto un flag per essere vero quando il ciclo è in esecuzione per l'ultima volta. questo lo ha risolto. – code511788465541441

0

CyclicBarrier sembra la soluzione se siete in attesa di discussioni per raggiungere un punto comune

Inoltre, invece di creare un filo di chiamare populatelist() che è a sua volta creando il thread runOnUiThread, darei un'occhiata a publishProgress()

Problemi correlati