20 Stimmen

AsyncTask Android - Entwurfsmuster und Rückgabewerte

Ich schreibe eine Anwendung, die Anmeldeinformationen auf einem externen Webserver validiert - so habe ich das grundlegende Problem der Erstellung eines Anmeldebildschirms, die, wenn gesendet wird, eine HTTP-Anfrage an einen Server im Hintergrund senden und nicht dazu führen, dass die UI zu hängen - während ein ProgressDialog für den Benutzer bereitstellt.

Mein Problem liegt darin, dass ich eine generische HTTP-Request-Klasse schreiben möchte, die AsyncTask erweitert, so dass ich beim Aufruf von .execute() Ich übergebe dann String-Parameter, die etwas wie "post" enthalten können, und wenn doInBackground aufgerufen wird, sieht diese die Zeichenkette "post" und leitet diese Parameter dann an den entsprechenden Aufruf in meiner Klasse weiter. Der Pseudocode würde etwa so aussehen

public class HTTPOperations extends AsyncTask<String, Void, String>
{
doInBackground(String... string1,additionalParams)
{
  if string1.equals "post"
      response = httpPost(additionalParams)
       return response;
}

httpPost(params)
{
// do http post request
}
}

Dies ist alles, was ich denken konnte, außer der Erstellung einer Klasse für jede HTTP-Post/GET usw. Anforderung, die ich machen möchte und ASyncTask erweitern...

Was führt mich zu meinem nächsten Problem, wenn die HTTP-POST erfolgreich ist und es ein Authentifizierungs-Token zurückgibt, wie kann ich dieses Token zugreifen?

Da new httpOperations.execute() nicht den String von doInBackground zurückgibt, sondern einen Wert vom Typ

Tut mir leid, wenn das keinen Sinn ergibt, aber ich werde aus dem Ganzen nicht schlau. Bitte fragen Sie nach einer Erläuterung, wenn Sie sie brauchen. AsyncTask Entwurfsmuster und Ideen sind sehr willkommen.

23voto

Nick Campion Punkte 10449

Wenn Sie eine wiederverwendbare Aufgabe für etwas wie dieses entwerfen, müssen Sie einen wiederverwendbaren Rückgabetyp festlegen. Dies ist eine Designentscheidung Ihrerseits. Fragen Sie sich: "Sind meine HTTP-Operationen sowohl in den Mechanismen, mit denen sie aufgerufen werden, als auch in der Verarbeitung ihrer Daten ähnlich?" Wenn ja, können Sie eine einzige Klasse entwerfen, die beides tut. Wenn nicht, brauchen Sie wahrscheinlich verschiedene Klassen für Ihre verschiedenen Remote-Operationen.

Bei meiner persönlichen Verwendung habe ich ein Objekt, dem ich Schlüssel-Wert-Paare zuordne, und der gemeinsame Rückgabetyp ist die HttpEntity . Dies ist der Rückgabetyp für HTTP Get und Post, und dies scheint in meinen Szenarien zu funktionieren, da ich in außergewöhnlichen HTTP-Ergebnissituationen, wie 404, Ausnahmen auslöse. Ein weiterer netter Aspekt dieser Einrichtung ist, dass der Code zum Anhängen von Parametern an ein Get oder Post ziemlich ähnlich sind, so dass diese Logik ziemlich einfach zu konstruieren ist.


Ein Beispiel wäre so etwas wie dieses (psuedo):

public interface DownloadCallback {
   void onSuccess(String downloadedString);
   void onFailure(Exception exception);
}

Dann in Ihrem Code, wo Sie gehen, um den Download zu tun:

DownloadCallback dc = new DownloadCallback(){
   public void onSuccess(String downloadedString){
     Log.d("TEST", "Downloaded the string: "+ downloadedString);
   }
   public void onFailure(Exception e){
     Log.d("TEST", "Download had a serious failure: "+ e.getMessage());
   }
 }

 DownloadAsyncTask dlTask = new DownloadAsyncTask(dc);

Speichern Sie dann im Konstruktor von DownloadAsyncTask den DownloadCallback und rufen Sie, wenn der Download abgeschlossen ist oder fehlschlägt, die Methode des Download-Callbacks auf, die dem Ereignis entspricht. Also ...

public class DownloadAsyncTask extends AsyncTask <X, Y, Z>(){
  DownloadCallback dc = null;

  DownloadAsyncTask(DownloadCallback dc){
    this.dc = dc;
  }

  ... other stuff ...

  protected void onPostExecute(String string){
    dc.onSuccess(string);
  }
}

Ich werde wiederholen, dass ich denke, für das Wohl der selbst, sollten Sie zurück HttpEntities übergeben. String kann wie eine gute Idee jetzt scheinen, aber es führt wirklich zu Schwierigkeiten später, wenn Sie mehr anspruchsvolle Logik hinter Ihrem http-Aufrufe tun wollen. Natürlich, das ist bis zu Ihnen. Hoffentlich hilft das.

1voto

HenryChuang Punkte 1429

Nehmen wir an, das Datenformat mit Web-Api ist json, mein Entwurfsmuster :

gemeinsame Klassen
1.MyAsyncTask : erweitert AsyncTask
2.backgroundBase : Parameter für den Server
3.API_Base : Parameter vom Server
4.myTaskCompleted : Rückruf-Schnittstelle

public class MyAsyncTask<BackgroundClass extends BackgroundBase,APIClass extends API_Base> extends AsyncTask<BackgroundClass, Void, APIClass> {
    private ProgressDialog pd ; 
    private MyTaskCompleted listener;
    private Context cxt;
    private Class<APIClass> resultType;
    private String url;
    private int requestCode;    

    public MyAsyncTask(MyTaskCompleted listener, Class<APIClass> resultType, int requestCode, String url){
        this.listener = listener;
        this.cxt = (Context)listener;
        this.requestCode = requestCode;
        this.resultType = resultType;
        this.url = url;
    }
    public MyAsyncTask(MyTaskCompleted listener, Class<APIClass> resultType, int requestCode, String url, ProgressDialog pd){
            this(listener, resultType, requestCode, url);
            this.pd = pd;
            this.pd.show();
    }   

    @Override
    protected APIClass doInBackground(BackgroundClass... params) {
        APIClass result = null;
        try {           
            //do something with url and params, and get data from WebServer api
            BackgroundClass oParams = params[0];
            String sUrl = url + "?d=" + URLEncoder.encode(oParams.getJSON(), "UTF-8");
            String source = "{\"RtnCode\":1, \"ResultA\":\"result aaa\", \"ResultB\":\"result bbb\"}";

            //to see progressdialog
            Thread.sleep(2000);

            result = new com.google.gson.Gson().fromJson(source, resultType);           
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

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

        try {
            if(pd != null && pd.isShowing())
                pd.dismiss();

            API_Base oApi_Base = (API_Base)result;          
            listener.onMyTaskCompleted(result , this.requestCode);                      
        } catch (Exception e) {
            e.printStackTrace();
        }           
    }

}
public class API_Base {
    public int RtnCode;

    public String getJSON(Context context) throws Exception
    {
        return new com.google.gson.Gson().toJson(this);
    }

    public String toString(){
        StringBuilder sb = new StringBuilder();

        for (Field field : this.getClass().getFields()) {
            try {
                field.setAccessible(true); 
                Object value = field.get(this); 
                if (value != null) {
                    sb.append(String.format("%s = %s\n", field.getName(), value));
                }
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }

        }

        return sb.toString();
    }

}
public class BackgroundBase {

    public String getJSON() throws Exception
    {       
        return new com.google.gson.Gson().toJson(this);
    }

}
public interface MyTaskCompleted {
    void onMyTaskCompleted(API_Base oApi_Base, int requestCode) ;
}

Beispiel: Wir rufen zwei Api in einer Aktivität auf
annehmen:
API 1. http://www.google.com/action/a
input params : AktionA
output params : RtnCode, ErgebnisA

API 2. http://www.google.com/action/b
Eingabeparameter : AktionB
output params : RtnCode, ErgebnisB

Klassen mit Beispiel:
1.MyActivity : erweitert Activity und implementiert MyTaskCompleted
2.MyConfig : Dienstprogrammklasse, hier wird requestCode eingestellt
3.backgroundActionA, BackgroundActionB : Modellklassen für api's input params
4.API_ActionA, API_ActionB : Modellklassen für die Ausgabeparameter der API

public class MyActivity extends Activity implements MyTaskCompleted {
    ProgressDialog pd;
    Button btnActionA, btnActionB;
    TextView txtResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_layout);
        btnActionA = (Button)findViewById(R.id.btn_actionA);
        btnActionB = (Button)findViewById(R.id.btn_actionB);
        txtResult = (TextView)findViewById(R.id.txt_result);

        btnActionA.setOnClickListener(listener_ActionA);
        btnActionB.setOnClickListener(listener_ActionB);

        pd = new ProgressDialog(MyActivity.this);
        pd.setTitle("Title");
        pd.setMessage("Loading");
    }

    Button.OnClickListener listener_ActionA = new Button.OnClickListener(){

        @Override
        public void onClick(View v) {
            //without ProgressDialog
            BackgroundActionA oBackgroundActionA = new BackgroundActionA("AAA");
            new MyAsyncTask<BackgroundActionA, API_ActionA>(MyActivity.this, 
                                                            API_ActionA.class, 
                                                            MyConfig.RequestCode_actionA,
                                                            "http://www.google.com/action/a").execute(oBackgroundActionA);
        }

    };
    Button.OnClickListener listener_ActionB = new Button.OnClickListener(){

        @Override
        public void onClick(View v) {
            //has ProgressDialog
            BackgroundActionB oBackgroundActionB = new BackgroundActionB("BBB");
            new MyAsyncTask<BackgroundActionB, API_ActionB>(MyActivity.this, 
                                                            API_ActionB.class, 
                                                            MyConfig.RequestCode_actionB,
                                                            "http://www.google.com/action/b",
                                                            MyActivity.this.pd).execute(oBackgroundActionB);
        }

    };

    @Override
    public void onMyTaskCompleted(API_Base oApi_Base, int requestCode) {
        // TODO Auto-generated method stub
        if(requestCode == MyConfig.RequestCode_actionA){
            API_ActionA oAPI_ActionA = (API_ActionA)oApi_Base;
            txtResult.setText(oAPI_ActionA.toString());

        }else if(requestCode == MyConfig.RequestCode_actionB){
            API_ActionB oAPI_ActionB = (API_ActionB)oApi_Base;
            txtResult.setText(oAPI_ActionB.toString());

        }

    }

}
public class MyConfig {
    public static String LogTag = "henrytest";

    public static int RequestCode_actionA = 1001;
    public static int RequestCode_actionB = 1002;
}
public class BackgroundActionA extends BackgroundBase {
    public String ActionA ;

    public BackgroundActionA(String actionA){
        this.ActionA = actionA;
    }

}
public class BackgroundActionB extends BackgroundBase {
    public String ActionB;

    public BackgroundActionB(String actionB){
        this.ActionB = actionB;
    }
}
public class API_ActionA extends API_Base {
    public String ResultA;
}
public class API_ActionB extends API_Base {
    public String ResultB;
}

Vorteil bei diesem Entwurfsmuster :
1. ein Vorteil für mehrere APIs
2.fügen Sie einfach Modellklassen für neue APIs hinzu, z. B. BackgroundActionA und API_ActionA
3.bestimmen, welche API durch unterschiedlichen requestCode in der Callback-Funktion : onMyTaskCompleted

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X