【问题标题】:DRY: a case with AsyncTasksDRY:AsyncTasks 的一个案例
【发布时间】:2014-02-05 22:43:01
【问题描述】:

我正在开发一个 Android 应用,它对网络服务有很多不同的请求。 每个请求都以这种方式在AsyncTask 的子类中完成:

(new AsyncTask<String, Void, Object1>() {
    @Override
    protected Object1 doInBackground(String... params) {
        // network request and parsing to Object1
        Object1 obj = new Object1();
        obj1 = Parser.parseObject1(httpClient.execute(...));
        return obj1;
    }

    @Override
    protected Object1 onPostExecute(Object1... ret) {
        return ret[0];
    }
}).execute();

Object1 是不同对象的占位符(CarBicycleTruck...),每个对象都位于不同的AsyncTask

除了在String 中返回 httpClient 的输出并在主线程(UI 线程)中解析之外,我还有什么选择?如果要解析大量数据,避免在 UI 线程中解析听起来很合理,对吗?

-= 更新 =-

让我重新表述这个问题:我正在寻求一种更智能的方式来开发我的应用程序,避免重复(AsyncTask 有很多样板代码)。我的做法是创建 20 多个 AsyncTask 的子类,这显然不是 DRY(不要重复自己)。

在 iOS 中,我们有 lambda 表达式,因此在 web 请求中完成回调非常简单和简洁。

【问题讨论】:

  • 我不确定你的问题到底是什么——但最后一点,是的,如果可能的话,你应该避免在 UI 线程上解析,特别是如果你要启动一个线程对于 HTTP 调用,在这个线程上进行解析是有意义的。
  • 您不必每次都编写完整的代码。 AsyncTask 是一个可以扩展或以其他方式使用的类。
  • @JosephEarl 我想知道一种更简单的方法来避免 AsyncTask 需要的那些重复代码。我为 AsyncTask 的每个子类类型创建一个类,但在我的项目中,我有 20 多个 AsyncTask 类。正常吗?
  • @zapl 我重新表述了我的问题。请再看一遍。我很感激
  • 如果您的用例是 HTTP 调用,我建议您使用一个库来完成复杂的类/API 设计,以减少重复。您可以围绕 asynctask 编写自己的包装器(例如,CursorLoader 是基于 AsyncTask 并且您使用它的方式完全不同)但它需要做很多工作。您可以尝试创建一个仅使用 Object1 作为通用参数的抽象类,并以这种方式获得更少的样板文件。或者传入一个策略模式,比如以通用方式处理这些事情的对象。

标签: java android android-asynctask dry


【解决方案1】:

您可以创建包含大部分样板代码的类。例如

public class SpecialAsyncTask<T> extends AsyncTask<String, Void, T> {

    public interface ResultProvider<T> {
        T generateResultInBackground(String... params);
    }

    public interface ResultConsumer<T> {
        void handleResultInForeground(T result);
    }

    private final ResultProvider<T> mProvider;
    private final ResultConsumer<T> mConsumer;
    private SpecialAsyncTask(ResultProvider<T> provider, ResultConsumer<T> consumer) {
        mProvider = provider;
        mConsumer = consumer;
    }

    @Override
    protected T doInBackground(String... params) {
        return mProvider.generateResultInBackground(params);
    }

    @Override
    protected void onPostExecute(T result) {
        mConsumer.handleResultInForeground(result);
    }

    public static <T> void execute(ResultProvider<T> provider, ResultConsumer<T> consumer, String... params) {
        new SpecialAsyncTask<T>(provider, consumer).execute(params);
    }
}

是一个示例,您可以如何将Object1 保留为通用参数,同时能够指定一个对象,该对象只需要实现一个接口来处理否则必须位于新的AsyncTask 实例中的代码。

使用这样的架构,您可以将一些通用代码定义为静态内容:

class Providers {
    public static final ResultProvider<String> HTTP_GETTER = new ResultProvider<String>() {

        @Override
        public String generateResultInBackground(String... params) {
            return MagicHttpLibrary.getContentAsString(params[0]);
        }

    };
}

您可以只使用Providers.HTTP_GETTER 作为参数,而不是实现doInBackground。或者创建一个新的类层次结构,实现这些接口之一,使用不同的方法来访问它们(例如工厂)

使用上面的例子看起来像下面的例子

class User extends Activity implements ResultConsumer<String> {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        SpecialAsyncTask.execute(Providers.HTTP_GETTER, this , "http://google.com");
        SpecialAsyncTask.execute(Providers.HTTP_GETTER, this , "http://yahoo.com");
    }

    @Override
    public void handleResultInForeground(String result) {
        Toast.makeText(this, result, Toast.LENGTH_LONG).show();
    }
}

除了不同的方法调用之外,或多或少没有重复的代码。这取决于您想如何使用一个类以及代码中实际发生的变化以了解如何设计类似的东西。确定需要参数化的部分并将重复的代码移动到重复使用的位置(继承/组合)。

【讨论】:

    【解决方案2】:

    Google 的 Volley HTTP 请求库在同一个工作线程中执行请求和解析。所以,这是一个很好的编码示例。

    【讨论】:

    • 很抱歉,我认为没有必要使用 Google 的 Volley。我只是在问一种优化和 DRY 的方式来创建具有不同回调的不同 Web 请求,而无需创建很多 AsyncTasks 的子类
    猜你喜欢
    • 2016-10-09
    • 1970-01-01
    • 2021-05-21
    • 1970-01-01
    • 2020-08-29
    • 1970-01-01
    • 2020-01-13
    • 1970-01-01
    相关资源
    最近更新 更多