【问题标题】:AsyncTask Android - Design Pattern and Return ValuesAsyncTask Android - 设计模式和返回值
【发布时间】:2023-03-10 08:10:01
【问题描述】:

我正在编写一个在外部网络服务器上验证登录凭据的应用程序 - 所以我遇到了创建登录屏幕的基本问题,当提交时会在后台向服务器发送 HTTP 请求并且不会导致 UI 挂起- 同时向用户提供 ProgressDialog。

我的问题在于,我想编写一个扩展 AsyncTask 的通用 HTTP 请求类,所以当我调用 .execute() 时,我将传递可能包含类似“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 成功并且它返回一个身份验证令牌,我如何访问这个令牌?

因为new httpOperations.execute(),不会从doInBackground返回字符串,而是返回一个类型的值

对不起,如果这没有意义,我根本想不通。如果您需要,请要求详细说明。 AsyncTask 设计模式和想法非常受欢迎。

【问题讨论】:

    标签: android http design-patterns android-asynctask


    【解决方案1】:

    如果您正在为这样的事情设计一个可重用的任务,您需要确定一个可重用的返回类型。这是您的设计决定。问问自己,“我的 HTTP 操作在调用它们的机制和处理它们的数据的机制上是否相似?”如果是这样,您可以设计一个类来完成这两项工作。如果没有,您可能需要针对不同的远程操作使用不同的类。

    在我个人使用中,我有一个对象,我将键值对附加到,常见的返回类型是HttpEntity。这是 HTTP Get 和 Post 的返回类型,这在我的场景中似乎可以正常工作,因为我在异常 HTTP 结果情况下抛出异常,例如 404。此设置的另一个不错的方面是将参数附加到 get 的代码或者 post 非常相似,所以这个逻辑很容易构建。


    一个例子是这样的(伪):

    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 调用后面做更复杂的逻辑时,它确实会导致麻烦。当然,这取决于你。希望这会有所帮助。

    【讨论】:

    • 感谢您的快速回复。常见的返回类型是字符串,我的所有调用要么希望收到包含 XML 数据的响应,要么是我可以解析的简单状态代码。我该如何使用 AsyncTask 执行此操作,我仍然不太明白在使用上述情况时我从哪里获得返回值。
    • 在您的异步任务中,您覆盖了onPostExecute(&lt;Z&gt; result) 方法。当 doInBackground 完成并将 doInBackground 返回的值传递给它时,AsyncTask 会自动调用 onPostExecute。要从 AsyncTask 中“取出”,我建议创建一个回调接口。此接口类型将被传递到您的 asynctask 构造函数中。然后,在 onPostExecute 中,您将调用接口方法。这将允许您构建条件逻辑来处理您的 http 下载结果。
    • 再次感谢。我不熟悉在实际示例中使用接口,我从未完全理解这个概念。你能解释一下你的意思吗?也许有一些伪代码。因为我不确定我如何实际访问 HTTPOperations AsyncTask 中的构造函数,因为它是由 HTTPOperations().execute(strings...) 调用的
    • 在我尝试将我的 HTTPOperations 类用作 Singleton 类时,可能还值得注意...
    • 我编辑了我的回复。无论是否单例,您都需要考虑处理响应的机制。回调是一种方式。你当然可以使用路由器类型的响应处理程序来做到这一点,但我已经看到它变得非常混乱,我没有一个简单的例子可以分享。
    【解决方案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 : 扩展 Activity 并实现 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.one 多 api 的优势
    2.只需为新的 api 添加模型类,例如:BackgroundActionA 和 API_ActionA
    3.通过回调函数中不同的requestCode判断哪个API:onMyTaskCompleted

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多