2012-11-13 13 views
5

Klasa AsyncTask uruchamia wiele innych wątków, ponieważ korzystam z biblioteki innej firmy. Nie wiem, ile wątków otwiera biblioteka, ale chcę poczekać, aż wszystkie się zakończą, zanim zapełni mój listview danymi, które pobiorą.Moja klasa AsyncTask zawiera inne wątki. Jak mogę poczekać na zakończenie wszystkich wątków przed wykonaniem poniższej metody?

W tej chwili śpię 10000 milisekund, ale to nie jest praktyczne, ponieważ nie wiem, jak duża jest to lista.

Jakie jest prawidłowe rozwiązanie tego problemu?

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

co robi appDataManager zrobić? – user1049280

+0

@ user1049280 pobierać dane z internetu – code511788465541441

+2

Czy API dla biblioteki trzecich posiadają mechanizm wywołania zwrotnego do zasygnalizowania zakończenia prac ? –

Odpowiedz

3

Najpierw przeniosę połączenie na populateList na metodę onPostExecute twojego AsyncTask. Przepisałbym również populateList w celu usunięcia trybu uśpienia i założenia, że ​​działa on na wątku UI (wyeliminuj wywołanie runOnUiThread i przenieś treść metody run bezpośrednio do populateList).

Teraz uniemożliwić AsyncTask ukończenie doInBackground do momentu zakończenia pracy AppDataManager. Zacznij od zdefiniowania flagę zakończenia blokady i obiektu, na którym można synchronizować:

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

Następnie zmodyfikować klasę AppDataManager dostarczenie oddzwonienie do innego obiektu, gdy praca jest wykonywana. Zdefiniować pole callback i zaktualizować API i metody:

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

Teraz zmodyfikować metodę doInBackground czekać na flagę, aby być ustawione:

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

To powinno załatwić sprawę. Prawdopodobnie jednak powinieneś rozwinąć tę kwestię, aby poradzić sobie z błędami różnego rodzaju (np. Dostarczyć mechanizm powiadamiania o błędach dla AppDataManager).

UPDATE W końcu zauważyłem, że wykonujesz pięć transakcji sieciowych w AppDataManager. Dlatego zamiast wywołania zwrotnego bezpośrednio w metodzie wywołania zwrotnego dla ajax(), zmniejsz licznik i oddzwoń tylko, gdy licznik osiągnie 0. Przed wprowadzeniem pętli, która wywołuje getHtml(), zainicjuj licznik na 5 w work(). Ponieważ licznik będzie modyfikowany osobnymi wątkami, dostęp do niego musi być zsynchronizowany. (Alternatywnie, można użyć AtomicInteger za ladą.

+0

Dzięki za to. Działało z niewielką modyfikacją. ponieważ 'getHtml()' zostaje wywołane w pętli, 'complete' zostaje ustawione na true przy pierwszym uruchomieniu. Dodałem flagę, aby była prawdziwa, gdy pętla jest wykonywana po raz ostatni. to rozwiązało. – code511788465541441

0

CyclicBarrier wydaje się rozwiązanie, jeśli czekasz na nici, aby osiągnąć wspólny punkt

Ponadto, zamiast tworzenia wątku zadzwonić populatelist() który jest itselt tworząc wątek runOnUiThread, będę patrzeć na publishProgress()

Powiązane problemy