简体   繁体   English

如何为Android AsyncTask类建模?

[英]How to modell an Android AsyncTask class?

I have decided to learn Scala / Play (Server side) and decided to learn Android (Client) game development at the same time to spice up the development. 我决定学习Scala / Play(服务器端),并决定同时学习Android (客户端)游戏开发,以使开发更加有趣。
I have a question regarding how to do a nice design for HTTP Request in Android. 我有一个关于如何在Android中为HTTP请求做一个不错的设计的问题。
From what I have understood the best way is to delegate the HTTP Request to an class extending the abstract AsyncTask class. 据我了解,最好的方法是将HTTP请求委托给扩展抽象AsyncTask类的类。
Do you have to do a new extension of the AsyncTask for each different logic in the doInBackground method you override? 您是否需要为您重写的doInBackground方法中的每个不同逻辑做AsyncTask的新扩展?
For me it doesn't fell natural to have a class for each request logic but rather encapsulate several coherent methods in one class. 对我而言,为每个请求逻辑都拥有一个类而不是将几个连贯的方法封装在一个类中并不自然。

I have just started to play a little but I'm not satisfied with the design as I don't like my design of an varargs Object in doInBackground(Object... params) . 我刚刚开始玩一些游戏,但是我对设计不满意,因为我不喜欢doInBackground(Object... params)中的varargs Object设计。
With this design I loose the type safety and the params object is far from intuitive and intuitive is something I strive for in my code. 通过这种设计,我失去了类型安全性,并且params对象远非直观,而我在代码中力求做到这一点。

Here is the code which I want to improve on. 这是我要改进的代码。

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

I think you don't understand what AsyncTask does. 我认为您不了解AsyncTask的功能。 You can't define methods like postLastName, postFirstName, postWhatever on a single AsyncTask subclass and have it execute that method call off the UI thread. 您无法在单个AsyncTask子类上定义诸如postLastName,postFirstName,postWhatever之类的方法,并让其执行该方法以调用UI线程。 AsyncTask is meant to make it easier to do background jobs that need to update the UI without forcing you to handling threading directly. AsyncTask的目的是使执行需要更新UI的后台作业变得更容易,而不会强迫您直接处理线程。

In order to communicate with the server you're using HttpClient, and the call HttpClient.execute() will block until the server's response comes back. 为了与服务器通信,您使用的是HttpClient,调用HttpClient.execute()将阻塞,直到服务器的响应返回为止。 That could take a long time, especially if the server is busy, dies, or the cell radio goes down. 这可能会花费很长时间,尤其是在服务器繁忙,死机或蜂窝无线电关闭的情况下。 When that call takes too long you don't want your UI to stop responding to the user. 当该调用花费的时间太长时,您不希望您的UI停止响应用户。 You want to show a spinner spinning to let the user know something is happening. 您想显示一个微调器,让用户知道正在发生的事情。 If you used the code you provided YOUR UI WOULD CEASE TO PAINT UNTIL THE HttpClient.execute() call returned because you're calling it on the UI thread. 如果您使用的代码是您提供的,则您的UI将会停止打印,直到返回HttpClient.execute()调用为止,因为您正在UI线程上对其进行调用。

The work around to this problem is move this call OFF the UI Thread onto another Thread. 解决此问题的方法是将此调用从UI线程移到另一个线程上。 Let that thread wait for a response, then have it notify the UI thread it's done and use the UI thread to update the UI with the new data. 让该线程等待响应,然后让它通知UI线程已完成,并使用UI线程用新数据更新UI。 Why can't you let that background thread update the UI? 为什么不能让该后台线程更新UI? Because that would violate Android's threading rule which says ONLY the UI thread can update the UI. 因为这会违反Android的线程规则,即只有UI线程才能更新UI。

AsyncTask allows you to run something OFF the UI thread (doInBackground()), and post the return value onto the UI Thread (onPostExecute()) so it can update the UI safely without violating Android's rule. AsyncTask允许您在UI线程(doInBackground())之外运行某些东西,并将返回值发布到UI线程(onPostExecute())上,以便它可以安全地更新UI,而不会违反Android的规则。 You don't call doInBackground() or onPostExecute() directly instead you call AsyncTask.execute() and the code in AsyncTask will call doInBackground() on a background Thread, and when it finishes it will call onPostExecute() on the UI Thread. 您不直接调用doInBackground()或onPostExecute(),而是调用AsyncTask.execute(),AsyncTask中的代码将在后台线程上调用doInBackground(),完成后它将在UI线程上调用onPostExecute()。 。 That way you don't get tangled up with doing all of the Threading yourself. 这样一来,您就不会为自己做所有的Thread所困扰。

Now your idea of having multiple requests run through a single subclass isn't going to work because AsyncTask ties together two parts of making service calls: the details of executing a service call to get a response, and what to do with that response to update the UI. 现在,让多个请求通过一个子类运行的想法将行不通,因为AsyncTask将进行服务调用的两个部分联系在一起:执行服务调用以获取响应的细节以及如何处理该响应以进行更新用户界面。 If called postFirstName() method what you do after it returns that response is probably different that if you called postLastName(). 如果调用postFirstName()方法,则返回该响应后所执行的操作可能与调用postLastName()时不同。 And because it's different means you can't just define ONE AsyncTask for all these different calls. 并且因为它是不同的,所以您不能只为所有这些不同的调用定义一个AsyncTask。 You could share code either through composition or subclassing a base class, but you'll have to create a subclass for each unique operation you want to perform on the server. 您可以通过组合或子类化基类来共享代码,但是您必须为要在服务器上执行的每个唯一操作创建一个子类。 So think Class instead of Method for each call. 因此,对于每个调用,请考虑使用Class而不是Method。

You don't have to use the parameters passed to the doInBackground(). 您不必使用传递给doInBackground()的参数。 If you have multiple types of parameters to pass to the AsyncTask. 如果您有多种类型的参数要传递给AsyncTask。 Pass them in the constructor. 将它们传递给构造函数。 Since you can't reuse AsyncTask instances (ie can't call AsyncTask.execute() more than once per instance). 由于您不能重用AsyncTask实例(即,每个实例不能多次调用AsyncTask.execute())。 Their lifecycle MUST be instantiate, execute(), and toss. 它们的生命周期必须实例化,execute()和折腾。 That means passing input in the constructor won't hurt your ability to use the AsyncTask. 这意味着在构造函数中传递输入不会损害您使用AsyncTask的能力。

I've written my own version of AsyncTask that separates out three callback methods: success() for handling the result returned from doInBackground(); 我编写了自己的AsyncTask版本,该版本分离出三种回调方法:success()用于处理从doInBackground()返回的结果; handleException() called when any exception is thrown from the doInBackground(); 从doInBackground()引发任何异常时调用handleException()。 and doFinally() called regardless of doInBackground() returns something or throws an exception. 和doFinally()调用而与doInBackground()无关,则返回某些内容或引发异常。 In default AsyncTask onPostExecute() is called regardless of error or success. 默认情况下,无论错误或成功如何,都将调用AsyncTask onPostExecute()。 It would look something like this: 它看起来像这样:

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

Often I'll do this as an anonymous inner class so passing references to the UI isn't needed, but again if you don't do Anon classes you can just pass those to the constructor. 通常,我会以匿名内部类的形式进行操作,因此不需要传递对UI的引用,但是同样,如果您不执行Anon类,则可以将其传递给构造函数。 You just have to be careful to not touch the UI should the Activity be destroyed (like the user flips the screen). 您只需要注意不要破坏活动(如用户翻转屏幕),不要触摸用户界面。

Prior to design thing, there are somethings that looks like you are doing wrong. 在设计之前,有些事情看起来像您做错了。

  • You should not be calling doInBackground directly as you are doing here 您不应该像在这里doInBackground直接调用doInBackground

    new ServerProxy().doInBackground("setSelectedCell" , postParameters); 新的ServerProxy()。doInBackground(“ setSelectedCell”,postParameters);

    instead it should be calling any version of execute 相反,它应该调用任何版本的execute

As for the design, you can try this 至于设计,你可以试试这个

  • You can create a ServerProxy class and dont extend it from AsyncTask . 您可以创建ServerProxy类,并且不要从AsyncTask对其进行扩展。 Just use it to have your logic encapsulated. 只需使用它就可以封装您的逻辑。
  • And now you can create a ServerProxyAsync which will take ServerProxy as composition. 现在,您可以创建一个ServerProxyAsync ,它将ServerProxy作为组成。
  • Now one option you can do is 现在您可以做的一个选择是

    public class ServerProxy extends AsyncTask 公共类ServerProxy扩展了AsyncTask

    Here String could be the method name you want to call on ServerProxy instance and you can use reflection for that. 在这里, String可以是您要在ServerProxy实例上调用的方法名称,并且可以为此使用反射。

NOTE: There could be other soln to this prob, this is just the first attempt to the problem 注意:此问题可能还有其他解决方法,这只是解决问题的第一次尝试

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM