2014-10-05 32 views
15

Sono nuovo di Android e molto utilizzato per lo sviluppo web. in javascript quando si desidera eseguire un'attività asincrona si passa una funzione come argomento (un callback):Android: passaggio di riferimento alle funzioni AsyncTask

http.get('www.example.com' , function(response){ 
    //some code to handle response 
}); 

Mi chiedevo se siamo in grado di fare lo stesso con Android di AsyncTask, passare una funzione riferimento al metodo onPostExecute() e lo eseguirà.

qualche suggerimento?

+0

Non è così che lo fai^_ ^. Crei un thread in background utilizzando l'attività asincrona e il tuo codice, noto anche come httprequest, va nel metodo doInBackground() e tu ne definisci il returntype. Una volta terminata l'operazione in background, viene chiamato onPostExecute() nel quale si scrive il codice per gestire la risposta poiché ora si arriva ad aggiornare le viste e tutto. Esiste un metodo progressupdate che viene attivato quando viene raggiunto il progresso definito. Otterrò il collegamento della documentazione tra qualche istante – Rico

+0

http://developer.android.com/reference/android/os/AsyncTask.html – Rico

risposta

41

Sì, il concetto di callback esiste anche in Java. In Java si definisce un callback come questo:

public interface TaskListener { 
    public void onFinished(String result); 
} 

Uno sarebbe spesso nido questo tipo di definizioni ascoltatore all'interno del AsyncTask in questo modo:

public class ExampleTask extends AsyncTask<Void, Void, String> { 

    public interface TaskListener { 
     public void onFinished(String result); 
    } 

    ... 
} 

E una completa implementazione della richiamata nella AsyncTask guarderebbe in questo modo:

public class ExampleTask extends AsyncTask<Void, Void, String> { 

    public interface TaskListener { 
     public void onFinished(String result); 
    } 

    // This is the reference to the associated listener 
    private final TaskListener taskListener; 

    public ExampleTask(TaskListener listener) { 
     // The listener reference is passed in through the constructor 
     this.taskListener = listener; 
    } 

    @Override 
    protected String doInBackground(Void... params) { 
     return doSomething(); 
    } 

    @Override 
    protected void onPostExecute(String result) { 
     super.onPostExecute(result); 

     // In onPostExecute we check if the listener is valid 
     if(this.taskListener != null) { 

      // And if it is we call the callback function on it. 
      this.taskListener.onFinished(result); 
     } 
    } 
} 

onPostExecute() è chiamato non appena l'attività in background termina. È possibile utilizzare l'intera cosa come questa:

ExampleTask task = new ExampleTask(new ExampleTask.TaskListener() { 
    @Override 
    public void onFinished(String result) { 
     // Do Something after the task has finished 
    } 
}); 

task.execute(); 

Oppure si può definire la TaskListener completamente a parte in questo modo:

ExampleTask.TaskListener listener = new ExampleTask.TaskListener() { 
    @Override 
    public void onFinished(String result) { 
     // Do Something after the task has finished 
    } 
}; 

ExampleTask task = new ExampleTask(listener);  
task.execute(); 

Oppure si può creare una sottoclasse TaskListener in questo modo:

public class ExampleTaskListener implements TaskListener { 

    @Override 
    public void onFinished(String result) { 

    } 
} 

E quindi utilizzarlo in questo modo:

ExampleTask task = new ExampleTask(new ExampleTaskListener());  
task.execute(); 

È possibile naturalmente solo l'override del metodo del AsyncTaskonPostExecute(), ma questo non è raccomandato e nella maggior parte dei casi, in realtà piuttosto cattiva pratica. Per esempio si potrebbe fare questo:

ExampleTask task = new ExampleTask() { 
    @Override 
    public void onPostExecute(String result) { 
     super.onPostExecute(result); 

     // Your code goes here 
    } 
}; 

questo funzionerà altrettanto bene come l'attuazione di cui sopra con un'interfaccia ascoltatore separato, ma ci sono alcuni problemi con questo:

In primo luogo si può effettivamente rompere il ExampleTask tutti insieme. Tutto si riduce alla chiamata super.onPostExecute() qui sopra. Se come sviluppatore si sostituisce lo onPostExecute() come sopra e si dimentica di includere la super chiamata o semplicemente di eliminarlo per qualsiasi motivo, il metodo originale onPostExecute() nello ExampleTask non verrà più chiamato. Ad esempio, l'intera implementazione listener con TaskListener non funzionerà più poiché la chiamata al callback è implementata in onPostExecute(). È inoltre possibile interrompere TaskListener in molti altri modi influenzando inconsapevolmente o involontariamente lo stato di ExampleTask in modo che non funzioni più.

Se si guarda a ciò che accade realmente quando si ridefinisce un metodo come questo, diventa molto più chiaro cosa sta succedendo.Sovrascrivendo onPostExecute() si sta creando una nuova sottoclasse di ExampleTask. Sarebbe la stessa cosa come fare questo:

public class AnotherExampleTask extends ExampleTask { 

    @Override 
    public void onPostExecute(String result) { 
     super.onPostExecute(result); 

     // Your code goes here 
    } 
} 

Tutto questo è solo nascosto dietro una caratteristica linguaggio chiamato classi anonime. Improvvisamente, un metodo come questo non sembra più così pulito e veloce, vero?

In sintesi:

  • override un metodo come questo in realtà crea una nuova sottoclasse. Non stai semplicemente aggiungendo una richiamata, stai modificando il funzionamento di questa classe e puoi inconsapevolmente interrompere oh così tante cose.
  • Errori di debug come questo possono essere molto più di un semplice dolore nella a **. Perché all'improvviso, ExampleTask potrebbe generare Exceptions o semplicemente non funzionare più senza un motivo apparente, perché non hai mai effettivamente modificato il suo codice.
  • Ogni classe deve fornire implementazioni di listener nei luoghi in cui è appropriata e prevista. Certo, puoi aggiungerli in seguito ignorando lo onPostExecute() ma è sempre molto pericoloso. Anche @flup con la sua reputazione 13k ha dimenticato di includere la chiamata super.onPostExecute() nella sua risposta, immagina cosa potrebbe fare un altro sviluppatore non esperto!
  • Una piccola astrazione non fa mai male a nessuno. Scrivere degli ascoltatori specifici potrebbe essere un po 'più codice, ma è una soluzione molto migliore. Il codice sarà più pulito, più leggibile e molto più gestibile. L'utilizzo di scorciatoie come la sostituzione di onPostExecute() essenzialmente sacrifica la qualità del codice per un po 'di comodità. Non è mai una buona idea e causerà solo problemi a lungo termine.
+0

Questa risposta potrebbe essere migliorata utilizzando un 'interfaccia TaskListener generico ' e 'onFinished (T risultato) ' –

+0

@ cricket_007 Non definirei questo un miglioramento. Ogni attività dovrebbe avere il proprio listener rigorosamente definito. Tutto il resto tende a creare problemi di progettazione e implementazione, anche quando si ignora la cancellazione dei tipi. –

+0

Tuttavia, è più flessibile di limitarsi alle sole risposte 'String'.Ho risposto a un paio di domande usando l'approccio callback su AsyncTask. [Ad esempio] (http://stackoverflow.com/a/36105515/2308683), dici che questo porta a un cattivo design? –

2

In Java, le funzioni sono meno di un cittadino di prima classe che in JavaScript. AsyncTask fornisce il callback come metodo nella classe, che è necessario sovrascrivere.

Vedere Make an HTTP request with android per una sottoclasse di AsyncTask con un'implementazione del doInBackground che effettua una richiesta Web.

Se si desidera eseguire più richieste HTTP con diversi callback, è possibile eseguire l'override di RequestTask e implementare onPostExecute con le diverse implementazioni di callback. È possibile utilizzare una classe anonima per simulare la chiusura di un callback JavaScript utilizza comunemente:

new RequestTask(){ 
    @Override 
    public void onPostExecute(String result) { 
     // Implementation has read only access to 
     // final variables in calling scope. 
    } 
}.execute("http://stackoverflow.com"); 

Come mostra Xaver, è anche possibile creare un'interfaccia in piena regola per l'ascoltatore. Questo mi sembra utile solo se desideri implementare un paio di funzioni predefinite onPostExecute e scegliere una di queste implementazioni predefinite per una particolare chiamata.

+0

Ignorare 'onPostExecute()' come questo raramente è una buona idea. Le classi devono fornire funzionalità di ascolto laddove siano appropriate e previste. Quello che stai facendo è pericoloso in molti modi, soprattutto perché potrebbe spezzare 'RequestTask' tutti insieme. Vedi la parte inferiore della mia risposta. –

+0

@XaverKapeller In realtà, la 'superPostExecute' della super-classe non fa nulla e chiamarla è piuttosto inutile. 'OnPostExecute' * è * il callback, non un metodo di implementazione interno a cui si può accedere in modo nascosto. Il suo scopo è la notifica del risultato al creatore originale dell'attività. – flup

+0

Beh sì è vero se stai implementando un nuovo 'AsyncTask', ma se hai già sottoclasse' AsyncTask' come il 'RequestTask' nella tua risposta suggerisce che potresti rompere qualsiasi funzionalità che era già implementata nel' RequestTask'. Questo è ciò che intendevo nel mio precedente commento. –

Problemi correlati