如何建模Android AsyncTask类?

时间:2012-01-07 22:40:34

标签: java android oop http asynchronous

我决定学习Scala / Play(服务器端),并决定同时学习 Android (客户端)游戏开发,以增加开发。
我有一个关于如何在Android中为HTTP请求设计好的问题 根据我的理解,最好的方法是将HTTP请求委托给扩展抽象AsyncTask类的类 您是否必须为您覆盖的AsyncTask方法中的每个不同逻辑执行doInBackground的新扩展?
对我来说,为每个请求逻辑创建一个类并不是很自然,而是在一个类中封装几个连贯的方法。

我刚刚开始玩一点,但我对设计不满意,因为我不喜欢doInBackground(Object... params)中设计的varargs对象。
 通过这种设计,我放松了类型安全性,并且params对象远非直观和直观,这是我在我的代码中努力的目标。

以下是我想要改进的代码。

public class GameActivity extends Activity {

    private class MyCellListener implements ICellListener {
        public void onCellSelected() {
            ServerProxy.postSelectedCell(row, col, player.getUser());    
            ...
            // ServerProxy.other();

public class ServerProxy extends AsyncTask<Object, Void, Void>{

    private static final String TAG = ServerProxy.class.getSimpleName();
    private static final String SERVER_ADDRESS = "http://127.0.0.1";

    // Prevent external instantiation
    private ServerProxy(){};

    public static void postSelectedCell(int row, int cell, User user){
         List<NameValuePair> postParameters = new ArrayList<NameValuePair>(3);
         postParameters.add(new BasicNameValuePair("row", String.valueOf(row)));
         postParameters.add(new BasicNameValuePair("cell", String.valueOf(cell)));
         postParameters.add(new BasicNameValuePair("userName", user.getUserName()));
         new ServerProxy().doInBackground("setSelectedCell" , postParameters);
    }

//    public static void postOther() {
//      new ServerProxy().doInBackground("other" , //some parameters); 
//    }

    /**
     * @param postParameters First object URL postfix<br/>
     * Second parameter is post parameters inform of {@code List<NameValuePair>}
     * @return null
     */
    @SuppressWarnings("unchecked")
    @Override
    protected Void doInBackground(Object... params) {

        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost(SERVER_ADDRESS +"/" + params[0]);
        httppost.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);

        try {
            httppost.setEntity(new UrlEncodedFormEntity((List<NameValuePair>) params[1]));
            httpclient.execute(httppost);
        } catch (ClientProtocolException e) {
         Log.e(TAG,e.toString());
        } catch (IOException e) {
         Log.e(TAG,e.toString());
        }
        return null;
    }
}

2 个答案:

答案 0 :(得分:5)

我认为你不明白AsyncTask的作用。你无法在单个AsyncTask子类上定义postLastName,postFirstName,postWhatever等方法,并让它从UI线程执行该方法调用。 AsyncTask旨在使您更容易执行需要更新UI的后台作业,而不必强制您直接处理线程。

为了与您正在使用HttpClient的服务器通信,调用HttpClient.execute()将阻塞,直到服务器的响应返回。这可能需要很长时间,特别是如果服务器繁忙,死机或电池无线电发生故障。当该调用时间过长时,您不希望UI停止响应用户。你想展示一个旋转器,让用户知道发生了什么。如果您使用了您提供的代码,那么您将在返回的HttpClient.execute()调用之前使用您的UI来进行绘制,因为您在UI线程上调用它。

解决这个问题的方法是将此UI调用关闭到另一个线程。让该线程等待响应,然后让它通知UI线程完成它并使用UI线程用新数据更新UI。为什么不能让后台线程更新UI?因为这会违反Android的线程规则,该规则只表示UI线程可以更新UI。

AsyncTask允许您在UI线程(doInBackground())中运行某些内容,并将返回值发布到UI线程(onPostExecute()),以便它可以安全地更新UI而不违反Android的规则。你不直接调用doInBackground()或onPostExecute(),而是调用AsyncTask.execute(),AsyncTask中的代码将在后台线程上调用doInBackground(),当它完成时,它将在UI线程上调用onPostExecute() 。这样你就不会因为自己完成所有的线程而纠缠不清。

现在你想通过单个子类运行多个请求是不行的,因为AsyncTask将服务调用的两个部分联系在一起:执行服务调用以获取响应的细节,以及如何处理响应更新UI。如果调用postFastName()方法,则在返回该响应之后执行的操作可能与您调用postLastName()有所不同。而且因为它的不同意味着你不能只为所有这些不同的调用定义一个AsyncTask。您可以通过组合或子类化基类来共享代码,但是您必须为要在服务器上执行的每个唯一操作创建子类。因此,对每次调用都要考虑Class而不是Method。

您不必使用传递给doInBackground()的参数。如果您有多种类型的参数传递给AsyncTask。在构造函数中传递它们。因为您无法重用AsyncTask实例(即每个实例不能多次调用AsyncTask.execute())。他们的生命周期必须是实例化,执行()和折腾。这意味着在构造函数中传递输入不会损害您使用AsyncTask的能力。

我编写了自己的AsyncTask版本,它分离出三个回调方法:success(),用于处理从doInBackground()返回的结果;当从doInBackground()抛出任何异常时调用handleException();和doFinally()调用,无论doInBackground()返回什么或引发异常。默认情况下,无论错误或成功如何,都会调用asyncTask onPostExecute()。它看起来像这样:

public class MyTask extends EnhancedAsyncTask<Param,Integer,MyResult> {
    MyParam1 param1;
    MyParam2 param2;

    public MyTask( MyParam1 param1, MyParam2 param2 ) {
        this.param1 = param;
        this.param2 = param2;
    }

    protected MyResult doInBackground( Param... params ) {
        // do server work here
        server.send( param1, param2 );
    }

    protected void success( MyResult result ) {
        // do Update of the UI
    }

    protected void handleException( Exception ex ) {
       // show an error here
    }
}

// Now to use this AsyncTask you would do something like this:

MyTask task = new MyTask( param1, param2 ).execute();

我经常将其作为匿名内部类来执行此操作,因此不需要传递对UI的引用,但如果不执行Anon类,则可以将它们传递给构造函数。如果活动被销毁(例如用户翻转屏幕),您必须小心不要触摸UI。

答案 1 :(得分:0)

在设计之前,有些事情看起来你做错了。

  • 您不应该像在这里一样直接致电doInBackground

    new ServerProxy()。doInBackground(“setSelectedCell”,postParameters);

    相反它应该调用任何version of execute

至于设计,你可以试试这个

  • 您可以创建ServerProxy类,但不要从AsyncTask扩展它。只需用它来封装你的逻辑。
  • 现在,您可以创建一个ServerProxyAsync,其中ServerProxy将作为构图。
  • 现在您可以选择一个选项

    公共类ServerProxy扩展了AsyncTask

    此处String可能是您要在ServerProxy个实例上调用的方法名称,您可以使用反射。

注意:此问题可能还有其他问题,这只是问题的第一次尝试