AsyncTask Android - шаблон дизайна и возвращаемые значения

Я пишу приложение, которое проверяет учетные данные для входа на внешний веб-сервер, поэтому у меня есть основная проблема создания экрана входа, который при отправке отправит HTTP-запрос на сервер в фоновом режиме и не приведет к зависанию пользовательского интерфейса - обеспечивая пользователю ProgressDialog.

Моя проблема заключается в том, что я хочу написать общий класс HTTP-запроса, который расширяет AsyncTask, поэтому, когда я вызываю .execute(), я передам параметры String, которые могут содержать что-то вроде "post", и когда вызывается doInBackground это увидит строку "post", а затем переадресует эти параметры на соответствующий вызов в моем классе. Псевдокод будет что-то вроде

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

Это все, о чем я мог подумать, кроме создания класса для каждого запроса HTTP Post/GET и т.д. Я хочу создать и расширить ASyncTask...

Что приводит меня к моей следующей проблеме, если HTTP POST успешна и возвращает токен аутентификации, как мне получить доступ к этому токену?

Поскольку новый httpOperations.execute() не возвращает строку из doInBackground, но значение типа

Извините, если это не имеет смысла, я не могу понять это вообще. Если вам это нужно, спросите его. Проекты и идеи дизайна AsyncTask очень приветствуются.

Ответы

Ответ 1

Если вы разрабатываете многоразовую задачу для чего-то вроде этого, вам нужно идентифицировать возвращаемый тип повторного использования. Его дизайнерское решение с вашей стороны. Спросите себя: "Являются ли мои HTTP-операции одинаковыми как в механизмах, с которыми они вызываются, так и в процессе обработки их данных?" Если это так, вы можете создать один класс для обоих. Если нет, вам, вероятно, нужны разные классы для разных удаленных операций.

В моем личном использовании у меня есть объект, который я присоединяю к ключевым парам значений, а общий тип возврата - HttpEntity. Это тип возвращаемого значения для HTTP Get и Post, и это, похоже, работает нормально в моих сценариях, потому что я бросаю исключения в исключительных ситуациях с результатом HTTP, например 404. Другим приятным аспектом этой настройки является то, что код для присоединения параметров к get или сообщение довольно похожи, поэтому эту логику довольно легко построить.


Примером может быть что-то вроде этого (psuedo):

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

Затем в вашем коде, где вы собираетесь выполнять загрузку:

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

Затем внутри конструктора DownloadAsyncTask сохраните DownloadCallback и, когда загрузка будет завершена или завершится неудачно, вызовите метод в обратном вызове загрузки, который соответствует событию. Так что...

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

Я еще раз повторю, что думаю на благо себя, вы должны передать HttpEntities. Теперь строка может показаться хорошей идеей, но на самом деле это приводит к проблемам позже, когда вы хотите сделать более сложную логику своих HTTP-вызовов. Конечно, это зависит от вас. Надеюсь, это поможет.

Ответ 2

Предположим, что формат данных с web api - json, мой шаблон дизайна:

общие классы
1.MyAsyncTask: расширяет AsyncTask
2.BackgroundBase: параметры для сервера
3.API_Base: параметры с сервера
4.MyTaskCompleted: интерфейс обратного вызова

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


Например, позвольте двум api в одном действии
предположим:
API 1. http://www.google.com/action/a
входные параметры: ActionA
выходные параметры: RtnCode, ResultA

API 2. http://www.google.com/action/b
входные параметры: ActionB
выходные параметры: RtnCode, ResultB

классы с примером:
1.MyActivity: расширяет активность и реализует MyTaskCompleted
2.MyConfig: класс утилиты, я устанавливаю requestCode здесь
3.BackgroundActionA, BackgroundActionB: классы моделей для параметров ввода api
4.API_ActionA, API_ActionB: классы моделей для параметров выхода 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;
}


Преимущество с этим шаблоном проектирования:
1. Преимущество для нескольких api
2. просто добавьте классы моделей для новых api, например: BackgroundActionA и API_ActionA
3. определить, какой API по другому запросуCode в функции обратного вызова: onMyTaskCompleted