5

我创建了以下类:

public class AsyncHttpsClientHelper {

public static final int REMOVE_CREDIT_CARD = 1;
public static final int ENABLE_AUTORENEW = 2;
// +10 final ints...

private static AsyncHttpsClientHelper instance = null;
private static Activity activity;
// Some other variables

    private AsyncHttpsClientHelper(Context context) {
        // Initiate variables
    }

    public static AsyncHttpsClientHelper getInstance(Context context) {
        // Guarantees the same instance for this class (Singleton)
    }

    public void performNetworkTask(int networkTaskType, String value)
    {
        switch (networkTaskType)
        {
            case REMOVE_CREDIT_CARD:
            {
                CupsLog.d(TAG, "path: " + Consts.ACCOUNT_REMOVE_CREDIT_CARD_PATH);
                client.post(Consts.ACCOUNT_REMOVE_CREDIT_CARD_PATH , new JsonHttpResponseHandler() {

                 @Override
                 public void onSuccess(JSONObject result) {
                try {
                    CupsLog.d(TAG, Consts.ACCOUNT_REMOVE_CREDIT_CARD_PATH + " -> onSuccess, result: " + result.toString(3));
                    AccountService.getInstance(activity).pullAccountDetailsFromServer();
                    Toast.makeText(activity, "Your credit card was removed", Toast.LENGTH_SHORT).show();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }

                 @Override
                 public void onFailure(Throwable arg0) {
                    CupsLog.d(TAG, "commitPaymentToServer -> onFailure");
                     BusProvider.getInstance().post(new DialogChangeEvent(DialogChangeEvent.REMOVE_CREDIT_CARD, "failed"));
                }
        });
        break;
            }
            case ENABLE_AUTORENEW:
            {
                // Do ENABLE_AUTORENEW logic
            }
            // +10 cases goes here...
        }
    }
}

这门课还没有完成,我还需要在这里添加另外 10 个我在应用程序周围执行的其他网络调用。

要运行其中一个网络任务,我运行这一行,例如:

AsyncHttpsClientHelper.getInstance(this).performNetworkTask(AsyncHttpsClientHelper.COMMIT_COUPON_TO_SERVER, event.getValue());

Event当任务完成时,我使用Square事件工具触发Bus向活动/片段布局提交所需的视觉更改。

问题:我的老板声称这是一种不好的做法,当我完成这门课时会变得一团糟。此外,他声称这个类应该是一个愚蠢的类,他所知道的只是配置AsyncHttpClient对象并返回它,这样我就可以使用它并在相关的Activity. 基本上他说 https 调用本身应该位于 Activity 类中。我更喜欢这种方式,并认为它使我的活动更清晰,更易于阅读。另一方面,他说这种方式更难调试,并且这个类结合了控制器和视图功能的一部分,这是它不应该做的。

那么谁是对的呢?创建这样的课程真的是一种不好的做法吗?

4

8 回答 8

4

看看 Google IO 2010 上关于 Android REST 客户端应用程序的演示文稿 - 我认为它对您的问题的完整答案。

https://www.youtube.com/watch?v=xHXn3Kg2IQE

干杯:)

于 2014-05-22T14:06:54.820 回答
3

您保持对 Activity 的静态引用 - 这是不好的做法!如果在长时间运行的网络操作期间手机旋转怎么办。

Google I/O 演示中描述的最强大和最干净的解决方案。

您的解决方案有一些缺点:

  • 单例不是线程安全的
  • 它使用 Activity 上下文 - 您可以轻松地获得内存泄漏或调用死活动 UI
  • UI 和网络调用紧密相连
  • 它只支持一个活动

根据您的问题 - 是的,就地调用 AsyncHttpClient 会产生您建议的更清晰的代码。

于 2014-05-25T14:49:48.290 回答
1

考虑一下 Dimitry 关于轮换的说法,因为它是真的。永远不要对与 UI 有任何联系的对象使用静态引用。它会导致内存泄漏,并且很难调试。

其他问题:

  • 忘记 getInstance(Context c)。(实际上,由于静态上下文引用,您必须编写此代码)
  • 您的 performNetworkTask 方法具有仅用于其中一项任务的“值”属性。令人困惑的是,只有编写此代码的人才会知道“价值”是什么。如果您需要其他参数来处理其他请求怎么办?将它们作为“值”之类的参数附加?这将是一团糟。相反,您应该创建一个抽象请求类,并为每个请求类型派生一个新类。这样就很容易理解发生了什么,也很容易扩展功能。
  • 我猜你的老板是想说你不应该将回调连接到你的助手类中。网络可以由单独的类处理(最好由服务或异步 http 客户端处理),但回调肯定应该在 Activity 中,因为这是发生反应的地方。对于来自不同活动的请求,您将如何以不同的方式做出反应?使用你的实现你不能,因为所有的回调都是有线的。看看图书馆的网站,它有一个关于如何实现这一点的比较好的例子:http: //loopj.com/android-async-http/#recommended-usage-make-a-static-http-client

并回答您的问题:

我的老板声称这是一种不好的做法,当我完成这门课时会变得一团糟。

是的,它会变得一团糟(当你开始添加/修改功能时会变得更糟)。

此外,他声称这个类应该是一个愚蠢的类,他所知道的只是配置 AsyncHttpClient 对象并返回它,以便我可以使用它并在相关 Activity 中执行 https 任务。

这应该是愚蠢的。在我看来,你可以保留 performNetworkTask 方法,但是你需要有回调参数让调用者做出不同的反应。

public void performNetworkTask(Request request, JsonHttpResponseHandler handler);

你也应该放弃int networkTaskType。我的建议是使用抽象的 Request 类。但是,如果您更喜欢这种方式,那么切换到 Enum 是最低限度的。

基本上他说 https 调用本身应该位于 Activity 类中。

“Https 调用”可以意味着任何东西。我认为他的意思是回调应该由调用者传递。

我更喜欢这种方式,并认为它使我的活动更清晰,更易于阅读。

这样一来,您的 Activity 中的代码肯定会减少,但您只能支持一个 Activity!基本上,您只是将缺少的代码从 Activity 移到了这个帮助器类中。更不用说以任何方式修改帮助器都会很痛苦。

另一方面,他说这种方式更难调试,并且这个类结合了控制器和视图功能的一部分,这是它不应该做的。那么谁是对的呢?创建这样的课程真的是一种不好的做法吗?

  • 我同意您尝试将 View 部分连接到帮助程序类。
  • 我不知道他所说的“难以调试”是什么意思,所以最好
    问问他。
  • 是的,这是一种不好的做法。你应该重构。
于 2014-05-25T16:25:29.463 回答
0

使代码更具可读性和易于维护始终是一种好习惯。封装责任是做到这两点的最佳方式之一。

对你的老板来说,一个很好的论点是:你的视图和服务器之间的连接从视图开始并在服务器中结束,但是完成它的人(并且对此负责)是这个类。与服务器或服务器无关,Activity它是如何完成的,只有结果。


我建议根据您共享的代码进行一些修改:

  • 删除构造函数和getInstance()
  • 创建一个initiator()方法和启动AsyncHttpClientGson以及其他参数
  • 创建其他类来扩展您的帮助程序并实现它们自己的特定performNetworkTask()(它们也可以并且应该具有它们特定的方法名称)
  • performNetworkTask()静态使用
  • 开始处理回调(视图对此负责)

会是这样的:

public class HttpsClientAsyncHelper {
   Acitivity activity;
   AsyncHttpClient client;
   Gson gson;

   // Other params

   public static void initiator(Context context)
   {
       activity = (Activity) context;
       client = new AsyncHttpClient().setCookieStore(CookieUtil.getInstance(activity)
                                .getPersistentCookieStore());
       gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
                               .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                               .create();
   }

   // Other methods
}

并创建另外 10 个这样的类:

public class RemoveCreditCardAsyncTask extends HttpsClientAsyncHelper {

   public static void performNetworkTask(Context context)
   {
       initiator();
       // Do what you did in this specific case
   }
}

用法:

 RemoveCreditCardAsyncTask.performNetworkTask(this);
 // specific method names
 AutoRenewAsyncTask.enableAutoRenew(this);
 AutoRenewAsyncTask.disableAutoRenew(this);
 // even more specific
 AutoRenewAsyncTask.enable(this);
 AutoRenewAsyncTask.disable(this);

我自己在一个更通用的类中练习这个,Network它处理与我的所有通信WebService

serverPostRequest(String controller, String action, JSONObject jsonParameters) throws Exception {}

fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {} 

奖励:对您来说可能有用的方法:

public static boolean isAvailable(Context context)
{
    ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();

    return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
于 2014-05-25T23:37:40.137 回答
0

也许来自 Misko Hevery 的这个链接:

http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/

及其后续行动:

http://misko.hevery.com/2008/08/21/where-have-all-the-singletons-gone/

会有所帮助。他专门处理信用卡应用程序中的单例,它具有隐式 MVC。MVC 无处不在。你通常需要一个很好的理由来离开它。

您的逻辑中还有一个大的 switch() 。通常这由 OO 中的多态性处理。方法擅长以对单元测试友好的方式封装功能。

于 2014-05-26T04:36:53.627 回答
0

我完全同意你老板的说法,他说这应该是一个愚蠢的班级。您很可能需要一个ExecutorWorker类,而不是 Helper类。毕竟,单身人士大部分时间都是邪恶的,你最终会泄漏你的上下文并最终导致内存泄漏。

有一种更好的方法,使用 self reservedFragments

现在考虑这一点,将您的帮助程序类变成一个自保留Fragment的,它应该是您系统的连接持有者,并且应该能够通过该连接执行一些任务。也失去了这一点,switch...case并将您的任务变成某种RunnableCallable类似的课程。

现在你有一个基于片段的执行器,比如类和一堆可以执行的任务。这种方法将为您带来以下优势:

  • 由于我们的片段是自保留的,它会像一个单例一样,
  • 你可以在活动之间使用这个片段,这是你在使用单例时无法做到的,
  • 由于 Fragment 本身是一个 android 组件,因此 android 会在您需要时为您提供上下文,因此您不必担心泄漏上下文或内存。
  • 即使您在某些任务运行时丢失了当前上下文(假设用户使用了主页按钮),您也可以简单地等待另一个上下文可用(片段重新附加到某些活动)

顺便说一句,您也可以Service为此使用 a 而不是 Fragments,原理相同,但可能更难实现,因为 Service-Activity 通信增加了另一层复杂性。

于 2014-05-28T00:55:41.577 回答
0

为您的网络相关任务尝试这种方法:

1.创建HttpHelper初始化http对象的类,编写辅助方法来执行http请求。

2.为您的所有 http 请求创建单独AsyncTask的,并从此类初始化 HttpHelper 并执行网络操作,这有两个好处:

a)您的代码保持可读性和模块化,更易于维护和调试。

b)由于网络请求是独立的,因此可以自由地在应用程序中的任何位置使用此类。

3.AsyncTask 接口返回,可以使用Handler或者你可以直接传递活动对象,但是这样你可以泄漏上下文,这可能导致内存泄漏。

于 2014-05-31T06:18:45.353 回答
0

回答下列问题:

  1. 您是否需要在类的每个方法中重复一些逻辑?如果是 - 那么很可能您需要将其拆分为更多类,以防止代码重复。
  2. 您的课程是否同时具有面向客户的输出(视图)、内部业务逻辑(控制器)和数据访问/持久性逻辑(模型) - 如果它需要同时拥有这两者,那么您可能希望按顺序拆分它将表示与业务逻辑和数据访问 (MVC) 分开。
  3. 您的类是否包含可能在其他用例/集成/等中可能有用的逻辑?如果是,您可能希望将此逻辑放入一个单独的类中(作为属性继承或包含在您的实际类中。
于 2014-06-01T12:58:38.227 回答