2013-01-17 14 views
24

Il "Login" degli esempi Android ha implementato AsyncTask come classe interna non statica. Tuttavia, secondo Commonsguys, questa classe dovrebbe essere statica e utilizzare un riferimento debole all'attività esterna see this.Qual è il modo corretto di implementare AsyncTask? classe nidificata statica o non statica?

Quindi qual è il modo corretto di implementare AsyncTask? Statico o non statico?

Commonsguy Attuazione
https://github.com/commonsguy/cw-android/tree/master/Rotation/RotationAsync/

Login esempio da Google

package com.example.asynctaskdemo; 

import android.animation.Animator; 
import android.animation.AnimatorListenerAdapter; 
import android.annotation.TargetApi; 
import android.app.Activity; 
import android.os.AsyncTask; 
import android.os.Build; 
import android.os.Bundle; 
import android.text.TextUtils; 
import android.view.KeyEvent; 
import android.view.Menu; 
import android.view.View; 
import android.view.inputmethod.EditorInfo; 
import android.widget.EditText; 
import android.widget.TextView; 

/** 
* Activity which displays a login screen to the user, offering registration as 
* well. 
*/ 
public class LoginActivity extends Activity { 
    /** 
    * A dummy authentication store containing known user names and passwords. 
    * TODO: remove after connecting to a real authentication system. 
    */ 
    private static final String[] DUMMY_CREDENTIALS = new String[] { "[email protected]:hello", "[email protected]:world" }; 

    /** 
    * The default email to populate the email field with. 
    */ 
    public static final String EXTRA_EMAIL = "com.example.android.authenticatordemo.extra.EMAIL"; 

    /** 
    * Keep track of the login task to ensure we can cancel it if requested. 
    */ 
    private UserLoginTask mAuthTask = null; 

    // Values for email and password at the time of the login attempt. 
    private String mEmail; 
    private String mPassword; 

    // UI references. 
    private EditText mEmailView; 
    private EditText mPasswordView; 
    private View mLoginFormView; 
    private View mLoginStatusView; 
    private TextView mLoginStatusMessageView; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setContentView(R.layout.activity_login); 

     // Set up the login form. 
     mEmail = getIntent().getStringExtra(EXTRA_EMAIL); 
     mEmailView = (EditText) findViewById(R.id.email); 
     mEmailView.setText(mEmail); 

     mPasswordView = (EditText) findViewById(R.id.password); 
     mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { 
      @Override 
      public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { 
       if (id == R.id.login || id == EditorInfo.IME_NULL) { 
        attemptLogin(); 
        return true; 
       } 
       return false; 
      } 
     }); 

     mLoginFormView = findViewById(R.id.login_form); 
     mLoginStatusView = findViewById(R.id.login_status); 
     mLoginStatusMessageView = (TextView) findViewById(R.id.login_status_message); 

     findViewById(R.id.sign_in_button).setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       attemptLogin(); 
      } 
     }); 
    } 

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

    /** 
    * Attempts to sign in or register the account specified by the login form. 
    * If there are form errors (invalid email, missing fields, etc.), the 
    * errors are presented and no actual login attempt is made. 
    */ 
    public void attemptLogin() { 
     if (mAuthTask != null) { 
      return; 
     } 

     // Reset errors. 
     mEmailView.setError(null); 
     mPasswordView.setError(null); 

     // Store values at the time of the login attempt. 
     mEmail = mEmailView.getText().toString(); 
     mPassword = mPasswordView.getText().toString(); 

     boolean cancel = false; 
     View focusView = null; 

     // Check for a valid password. 
     if (TextUtils.isEmpty(mPassword)) { 
      mPasswordView.setError(getString(R.string.error_field_required)); 
      focusView = mPasswordView; 
      cancel = true; 
     } 
     else if (mPassword.length() < 4) { 
      mPasswordView.setError(getString(R.string.error_invalid_password)); 
      focusView = mPasswordView; 
      cancel = true; 
     } 

     // Check for a valid email address. 
     if (TextUtils.isEmpty(mEmail)) { 
      mEmailView.setError(getString(R.string.error_field_required)); 
      focusView = mEmailView; 
      cancel = true; 
     } 
     else if (!mEmail.contains("@")) { 
      mEmailView.setError(getString(R.string.error_invalid_email)); 
      focusView = mEmailView; 
      cancel = true; 
     } 

     if (cancel) { 
      // There was an error; don't attempt login and focus the first 
      // form field with an error. 
      focusView.requestFocus(); 
     } 
     else { 
      // Show a progress spinner, and kick off a background task to 
      // perform the user login attempt. 
      mLoginStatusMessageView.setText(R.string.login_progress_signing_in); 
      showProgress(true); 
      mAuthTask = new UserLoginTask(); 
      mAuthTask.execute((Void) null); 
     } 
    } 

    /** 
    * Shows the progress UI and hides the login form. 
    */ 
    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 
    private void showProgress(final boolean show) { 
     // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow 
     // for very easy animations. If available, use these APIs to fade-in 
     // the progress spinner. 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { 
      int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); 

      mLoginStatusView.setVisibility(View.VISIBLE); 
      mLoginStatusView.animate().setDuration(shortAnimTime).alpha(show ? 1 : 0).setListener(new AnimatorListenerAdapter() { 
       @Override 
       public void onAnimationEnd(Animator animation) { 
        mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE); 
       } 
      }); 

      mLoginFormView.setVisibility(View.VISIBLE); 
      mLoginFormView.animate().setDuration(shortAnimTime).alpha(show ? 0 : 1).setListener(new AnimatorListenerAdapter() { 
       @Override 
       public void onAnimationEnd(Animator animation) { 
        mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 
       } 
      }); 
     } 
     else { 
      // The ViewPropertyAnimator APIs are not available, so simply show 
      // and hide the relevant UI components. 
      mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE); 
      mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 
     } 
    } 

    /** 
    * Represents an asynchronous login/registration task used to authenticate 
    * the user. 
    */ 
    public class UserLoginTask extends AsyncTask<Void, Void, Boolean> { 
     @Override 
     protected Boolean doInBackground(Void... params) { 
      // TODO: attempt authentication against a network service. 

      try { 
       // Simulate network access. 
       Thread.sleep(2000); 
      } 
      catch (InterruptedException e) { 
       return false; 
      } 

      for (String credential : DUMMY_CREDENTIALS) { 
       String[] pieces = credential.split(":"); 
       if (pieces[0].equals(mEmail)) { 
        // Account exists, return true if the password matches. 
        return pieces[1].equals(mPassword); 
       } 
      } 

      // TODO: register the new account here. 
      return true; 
     } 

     @Override 
     protected void onPostExecute(final Boolean success) { 
      mAuthTask = null; 
      showProgress(false); 

      if (success) { 
       finish(); 
      } 
      else { 
       mPasswordView.setError(getString(R.string.error_incorrect_password)); 
       mPasswordView.requestFocus(); 
      } 
     } 

     @Override 
     protected void onCancelled() { 
      mAuthTask = null; 
      showProgress(false); 
     } 
    } 
} 

se dipende da una situazione specifica, poi con ListView articoli (testo + plus bitmap) caricati dal internet usando HttpClient, come devo implementare il mio AsyncTask?

risposta

13

In generale, consiglierei l'implementazione statica (sebbene entrambi siano accettabili).

L'approccio di Google richiede meno codice, ma il tuo asynctask sarà strettamente associato alla tua attività (il che significa che non è facilmente riutilizzabile). Ma a volte questo approccio è più leggibile.

Con l'approccio di CommonsGuy, richiederà più sforzo (e più codice) per disaccoppiare attività e asynctask, ma alla fine si avrà un codice più modulare, più riutilizzabile.

+1

Dalla mia comprensione, una classe nidificata non statica mantiene un riferimento ad essa al di fuori della classe. Se l'utente annulla improvvisamente l'attività corrente (premere il pulsante Indietro) mentre ci sono diverse attività ancora accodate nei pool di thread. Quindi questo approccio (non statico) potenzialmente creerà una perdita di memoria poiché il GC non sarà in grado di recuperare la memoria per quell'attività. Sto comprendendo questo correttamente? Btw, grazie mille. – Chan

+1

@Chan AsyncTasks sono soggetti a perdita, sia quelli interni che quelli statici. Se il dispositivo cambia configurazione e l'attività viene ricreata, è facile dimenticare di annullare la vecchia attività e quindi di rimanere in esecuzione nella bg. –

+0

@MisterSmith: Grazie. Quindi c'è qualche altro metodo alternativo? – Chan

2

L'articolo collegato già dice

Ciò sottolineare, però, che si desidera doInBackground() del vostro AsyncTask per essere completamente slegato dalla Activity. Se tocchi solo la tua attività sul thread principale dell'applicazione, il tuo AsyncTask può sopravvivere al cambiamento di orientamento intatto.

Non toccare l'attività (ad esempio i suoi membri) dal AsyncTask, che è in linea con Static Nested Classes

classi annidate statiche
Come con i metodi e le variabili di classe, una statica nidificato la classe è associata alla sua classe esterna. E come i metodi di classi statiche, una classe nidificata statica non può fare riferimento direttamente alle variabili di istanza o ai metodi definiti nella sua classe di inclusione, ma può usarli solo attraverso un riferimento a un oggetto.

Sebbene gli esempi da Android, AsyncTask reference e Using AsyncTask utilizzano ancora classi annidate non statici.

E secondo questo Static nested class in Java, why?, vorrei prima andare con la classe interna statica e ricorrere alla versione non-statica solo se è veramente necessario.

14

Non esiste un unico modo "corretto" per implementare AsyncTask. Ma ecco i miei due centesimi:

Questa classe ha lo scopo di eseguire lavori "leggeri" nel contesto di un'attività.Ecco perché ha i metodi onPreExecute, onProgressUpdate,in esecuzione nel thread dell'interfaccia utente, in modo che possano accedere rapidamente ai campi e aggiornare la GUI. Qualsiasi attività che potrebbe richiedere più tempo per essere completata e non è destinata ad aggiornare un'attività specifica deve essere spostata su un servizio.

Questi metodi sono principalmente utilizzati per aggiornare la GUI. Poiché la GUI è qualcosa correlata all'istanza Activity (i campi sono probabilmente dichiarati come variabili membro private), è più conveniente implementare AsyncTask come classe nidificata non statica. È anche il modo più naturale secondo me.

Nel caso in cui l'attività verrà riutilizzata in altre attività, penso che dovrebbe essere consentito avere una propria classe. Ad essere onesti, non sono un fan delle classi nidificate statiche, specialmente nelle viste interne. Se è una classe significa che è concettualmente diversa dall'attività. E se è statico, significa che non è collegato a questa istanza concreta dell'attività. Ma dato che sono nidificate, queste classi sono visivamente all'interno della classe genitore che rende più difficile la lettura, e possono passare inosservate nel explorer del pacchetto di progetto in quanto mostra solo i file. E nonostante sia meno accoppiato rispetto alle classi interne, questo non è davvero così utile: se la classe cambia, devi unire/impegnare l'intero file genitore al controllo della versione. Se sei in grado di riutilizzarlo, dovrai accedervi come Parent.Nested ovunque. Pertanto, per non associare altre attività alla classe Parent, è probabile che si desideri effettuare il refactoring ed estrarre la classe nidificata nel proprio file.

Quindi per me la domanda sarebbe Inner Class vs Classe di primo livello.

Problemi correlati