AsyncTask Android - шаблон проектирования и возвращаемые значения - PullRequest
20 голосов
/ 20 февраля 2011

Я пишу приложение, которое проверяет учетные данные для входа на внешний веб-сервер - поэтому у меня есть основная проблема создания экрана входа в систему, который при отправке отправляет 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 приветствуются.

Ответы [ 2 ]

23 голосов
/ 20 февраля 2011

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

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


Примером может быть что-то вроде этого (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-вызовами.Конечно, это зависит от вас.Надеюсь, это поможет.

1 голос
/ 11 февраля 2015

предположим, что формат данных с веб-интерфейсом 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: служебный класс, я устанавливаю код запроса здесь
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 с помощью другого requestCode в функции обратного вызова: onMyTaskCompleted

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...