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