2012-01-31 12 views
14

Quando si utilizza AsyncTaskLoader, come si aggiorna una barra di avanzamento che mostra lo stato di aggiornamento? Normalmente si aspetta che il callback venga rimosso una volta terminato, ma come eseguire gli aggiornamenti in esecuzione? Vuoi lasciare che il thread principale (ui) esegua il polling dei dati mentre viene impostato o qualcosa del genere?Aggiorna barra di avanzamento da AsyncTaskLoader?

Modifica: Sto parlando di AsyncTaskLoader, guarda la parte del caricatore. Ecco il link alla classe: http://developer.android.com/reference/android/content/AsyncTaskLoader.html

Voglio usarlo perché è il futuro :), so come farlo con AsyncTask.

risposta

4

Si può fare con caricatori, ma è necessario tenere ed aggiornare un WeakReference sulla vostra attività:

public class LoaderTestActivity extends FragmentActivity implements LoaderCallbacks<Void> { 
    private static final int MAX_COUNT = 100; 

    private ProgressBar progressBar; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_async_task_test); 

     progressBar = (ProgressBar) findViewById(R.id.progressBar1); 
     progressBar.setMax(MAX_COUNT); 
     AsyncTaskCounter.mActivity = new WeakReference<LoaderTestActivity>(this); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     getMenuInflater().inflate(R.menu.activity_async_task_test, menu); 
     return true; 
    } 

    public void onStartButtonClick(View v) { 
     startWork(); 
    } 

    void startWork() { 
     getSupportLoaderManager().initLoader(0, (Bundle) null, this); 
    } 

    static class AsyncTaskCounter extends AsyncTaskLoader<Void> { 
     static WeakReference<LoaderTestActivity> mActivity; 

     AsyncTaskCounter(LoaderTestActivity activity) { 
      super(activity); 
      mActivity = new WeakReference<LoaderTestActivity>(activity); 
     } 

     private static final int SLEEP_TIME = 200; 

     @Override 
     public Void loadInBackground() { 
      for (int i = 0; i < MAX_COUNT; i++) { 
       try { 
        Thread.sleep(SLEEP_TIME); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       Log.d(getClass().getSimpleName(), "Progress value is " + i); 
       Log.d(getClass().getSimpleName(), "getActivity is " + getContext()); 
       Log.d(getClass().getSimpleName(), "this is " + this); 

       final int progress = i; 
       if (mActivity.get() != null) { 
        mActivity.get().runOnUiThread(new Runnable() { 

         @Override 
         public void run() { 
          mActivity.get().progressBar.setProgress(progress); 
         } 
        }); 
       } 
      } 
      return null; 
     } 

    } 

    @Override 
    public Loader<Void> onCreateLoader(int id, Bundle args) { 
     AsyncTaskLoader<Void> asyncTaskLoader = new AsyncTaskCounter(this); 
     asyncTaskLoader.forceLoad(); 
     return asyncTaskLoader; 
    } 

    @Override 
    public void onLoadFinished(Loader<Void> arg0, Void arg1) { 

    } 

    @Override 
    public void onLoaderReset(Loader<Void> arg0) { 

    } 

} 
+2

Penso la maggior parte delle persone che raggiungono questa domanda utilizza un AsyncTaskLoader per leggere alcuni dati dalla rete. In tal caso, si prega di dare un'occhiata a RoboSpice: è molto più vicino alle vostre esigenze di un caricatore quando si tratta di networking: https://github.com/octo-online/robospice – Snicolas

+1

Non abbiamo bisogno di sincronizzare 'mActivity 'Accedere? –

+3

Questo è sbagliato. Loader viene creato solo la prima volta che si chiama initLoader, quindi se si ruota l'attività si perde il riferimento ad esso e con esso qualsiasi progresso. Il modo corretto per gestire ciò è di avere un metodo setActivity (...) sul caricatore che modifica il riferimento debole alla nuova attività e usa getSupportLoaderManager(). GetLoader (), lo gira sul caricatore, lo controlla non null e chiamare il metodo setActivity() su di esso prima di chiamare nuovamente initLoader(). Anche in setActivity() dovresti assicurarti che il caricatore sincronizzi il suo stato con la vista di avanzamento. –

3

Trasmetto un Intento all'attività (ed è ricevuto da un BroadcastReceiver). Non sono molto contento di questa soluzione, ma funziona. AsynTask publishProgress è davvero più facile da usare. Hai trovato qualche altra soluzione?

+1

non ho ancora trovato una soluzione, ma non ho cercato difficile eigther :) Ho fatto questa domanda perché cercavo nella AsyncTaskLoader e ha trovato un settore in cui mancava rispetto a AsyncTask – Warpzit

4

È possibile utilizzare handler, penso che sarà più leggero per il sistema di intenti

public class MyFragmentActivity extends FragmentActivity{ 
private final static int MSGCODE_PROGRESS = 1; 
private final static int MSGCODE_SIZE = 2; 

    private final Handler mHandler = new Handler() { 
    public void handleMessage(Message msg) { 
     Bundle bundle = msg.getData(); 
     if(null != bundle){ 
      int data = msg.getData().getInt(BUNDLE_PROGRESS); 
      if(msg.what == MSGCODE_SIZE){ 
       mProgressBar.setMax(data); 
      } else if(msg.what == MSGCODE_PROGRESS){ 
       mProgressBar.setProgress(data); 
      } 
     } 
    } 
}; 
} 

Set mHandler al costruttore di AsyncTaskLoader e da loadInBackground è possibile aggiornare il progresso

Bundle bundle = new Bundle(); 
bundle.putInt(BUNDLE_PROGRESS, lenghtOfFile); 
Message msg = new Message(); 
msg.setData(bundle); 
msg.what = MSGCODE_SIZE; 
mHandler.sendMessage(msg); 
+0

vi chiedete quali siano succede con cambi di orientamento? il gestore si riferirà al vecchio contesto e perderai. È necessario aggiornare il gestore in qualche modo. – Warpzit

+1

È un'altra domanda. Puoi impostare android: configChanges = "orientation" nella configurazione dell'attività. O definire il gestore in onCreate, quindi il gestore riceverà una nuova istanza su ciascuna rotazione. – garmax1

+0

Questo è il modo in cui mi aspetterei di farlo da solo, volevo solo passarlo sullo stackoverflow per essere sicuro :) – Warpzit

3

sto usando questo con il mio AsyncTaskLoader, all'interno di loadInBackground

runOnUiThread(new Runnable() { 
    public void run() { 
     //UI code 
    } 
}); 

Tuttavia, questo non funziona con una modifica alla configurazione (come la modifica dell'orientamento). Non sono convinto che il AsyncTaskLoader sia il migliore da utilizzare se è necessario aggiornare l'interfaccia utente, ma funziona meglio quando si gestiscono le modifiche alla configurazione. Non so perché hanno creato sia uno AsyncTaskLoader sia uno AsyncTask ciascuno con i propri compromessi. Frustra e confonde gli sviluppatori. Inoltre, lo AsyncTaskLoader ha pochissima documentazione.

+0

Un'altra possibile soluzione, e voi la documentazione puzza (come molte altre aree di Android) :) – Warpzit

0

Ho appena avuto questo problema. Ho usato un AtomicInteger statico nella mia attività per memorizzare i progressi. Il caricatore lo aggiorna tramite una richiamata e l'attività esegue il polling e visualizza i progressi.

Nel callback del caricatore onLoadFinished Nascondo il pannello di avanzamento, che provoca l'uscita del ciclo di polling.

Normalmente eviterei lo stato statico, ma nel complesso questo è più semplice delle alternative. Nel mio caso, ho un layout diverso in orizzontale, quindi sono più felice di lasciare che i cambi di orientamento si comportino normalmente.

private Handler progressHandler; // init with new Handler(getMainLooper()) 
private static AtomicInteger progress = new AtomicInteger(0); 

... 

private void beginProgressUiUpdates() { 
    progress.set(0); 
    displayProgress(); 
    progressHandler.postDelayed(pollProgress, PROGRESS_POLL_PERIOD_MILLIS); 
} 

private Runnable pollProgress = new Runnable() { 
    public void run() { 
     if (findViewById(R.id.progressPanel).getVisibility() == View.VISIBLE) { 
      displayProgress(); 
      progressHandler.postDelayed(pollProgress, PROGRESS_POLL_PERIOD_MILLIS); 
     } 
    } 
}; 

private void displayProgress() { 
    ((ProgressBar)findViewById(R.id.progressBar)).setProgress(progress.get()); 
}