关于Java:异步编程最佳实践

Asynchronous programming best practices

我最近写了我的第一个Android应用程序,大约有8000-10000行代码。有一件事一直阻碍我使用正常的设计模式,那就是Android大量使用异步调用(打开对话框、活动等)。由于这个原因,我的代码很快开始看起来像"意大利面",我最终开始不喜欢看某些课程。

是否有特定的设计模式或编程方法适用于任何人都会推荐的系统?对于编写可管理的异步代码有什么建议吗?


  • 使用全局变量

如果您不想用简单的Intent.putExtra()调用来混乱代码,并为每个唯一的Activity管理这些东西,那么您必须在应用程序中使用全局变量。扩展Application并存储应用程序处于活动状态时所需的数据。要实际实现它,请使用这个很好的答案。这将使活动之间的依赖关系消失。例如,假设在应用程序的生命周期中,您的应用程序需要一个"用户名"——这是一个很好的工具。不需要脏的Intent.putExtra()呼叫。

  • 使用风格

在制作第一个Android应用程序时,一个常见的错误是通常只需开始编写XML视图。XML文件将(毫无问题且速度非常快)扩展到非常多的代码行。在这里,您可以有一个解决方案,您只需使用style属性来实现特定的行为。例如,考虑这段代码:

值/样式.xml:

1
2
3
4
5
6
7
<style name="TitleText">
    <item name="android:layout_height">wrap_content</item>
    <item name="android:layout_width">wrap_content</item>
    <item name="android:textSize">18sp</item>
    <item name="android:textColor">#000</item>
    <item name="android:textStyle">bold</item>  
</style>

布局/main.xml:

现在,如果你有两个TextView和两个TextView应该具有相同的行为,那么就让它们使用TitleText样式。样例代码:

1
2
3
4
5
6
7
8
9
10
11
<!--- ... -->
<TextView
   android:id="@+id/textview_one"
   style="@style/TitleText"
/>

<TextView
   android:id="@+id/textview_two"
   style="@style/TitleText"
/>
<!--- ... -->

简单,不需要重复代码。如果您真的想进一步了解这个特定主题,请看一下布局技巧:创建可重用的UI组件。

  • 使用字符串

这一点很简短,但我认为提及这一点很重要。开发人员可能会犯的另一个错误是跳过strings.xml,只在代码中编写UI消息(和属性名)(在他需要的地方)。为了使应用程序更易于维护,只需在strings.xml文件中定义消息和属性。

  • 创建和使用全局工具类

当我编写第一个应用程序时,我只是在需要的地方编写(和复制)方法。结果如何?许多方法在不同的活动之间具有相同的行为。我学到的是做一个工具班。例如,假设您必须在所有活动中发出Web请求。在这种情况下,跳过在实际的Activity中定义它们,并为其创建静态方法。样例代码:

1
2
3
4
5
6
7
8
9
10
11
public final class Tools {

    private Tools() {
    }

    public static final void sendData(String url,
              String user, String pass) {
        // URLConnections, HttpClients, etc...
    }

}

现在,您只需在需要向服务器发送数据的Activity中使用下面的代码:

1
Tools.sendData("www.www.www","user","pass");

不过,你明白了。在你需要的地方使用这个"模式",它可以防止你弄乱你的代码。

  • 让自定义类定义用户需要与应用程序交互的行为

这可能是最有用的一点。为了定义"用户需要在哪里与您的应用程序交互",假设您有一个Menu,从行的角度来看,哪个行为非常长,为什么我们要将Menu的计算保持在同一类中?每一个小项目都会使你的Activity类成为一段痛苦的代码,时间更长——你的代码看起来像"意大利面"。例如,不要这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    MenuItem item;
    item = menu.findItem(R.id.menu_id_one);
    if (aBooleanVariable) {
        item.setEnabled(true);
    } else {
        item.setEnabled(false);
    }
    // More code...
    return super.onPrepareOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem i) {
    // Code, calculations...
    // ...
    // ...
    return super.onOptionsItemSelected(i);
}

重新设计成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private MyCustomMenuInstance mMenuInstance;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);        
    setContentView(R.layout.main);

    mMenuInstance = new MyCustomMenuInstance();
}  

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    mMenuInstance.onPrepareOptionsMenu(menu);
    return super.onPrepareOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem i) {
    mMenuInstance.onOptionsItemSelected(i);
    return super.onOptionsItemSelected(i);
}

例如,MyCustomMenuInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MyCustomMenuInstance {

    // Member fields..

    public MyCustomMenuInstance() {
        // Init stuff.
    }

    public void onPrepareOptionsMenu(Menu menu) {
        // Do things..
        // Maybe you want to modify a variable in the Activity
        // class? Well, pass an instance as an argument and create
        // a method for it in your Activity class.
    }

    public void onOptionsItemSelected(MenuItem i) {
        // Do things..
        // Maybe you want to modify a variable in the Activity
        // class? Well, pass an instance as an argument and create
        // a method for it in your Activity class.
    }

}

你看这是怎么回事。你可以把这个应用到很多事情上,比如onClickonClickListeneronCreateOptionsMenu,列表很长。要了解更多"最佳实践",您可以在这里看到一些来自Google的示例应用程序。看看他们是如何以一种好的和正确的方式实现事情的。

最后一句话:保持代码的整洁,以逻辑的方式命名变量和方法,尤其是以正确的方式。始终,始终理解代码中的位置-这非常重要。


从一个业余的角度来看,我不希望我的第一次尝试是一个干净的,生产就绪的应用程序。有时我会吃意大利面,意大利肉饼甚至意大利饺子。在这一点上,我试图重新思考我最不喜欢的代码是什么,并寻找更好的替代方案:

  • 重新考虑类以更好地描述对象,
  • 将每个方法中的代码保持在最低限度,
  • 尽可能避免依赖于静态变量,
  • 将线程用于昂贵的任务,不要将其用于快速过程,
  • 将UI与应用程序逻辑分离(将其保留在类中)。
  • 尽可能保留私有字段:当您想更改类时,这将很有帮助,
  • 重复这些代码,直到您喜欢代码为止

我在异步方法中看到的最常见的错误之一是在创建一个或多个线程的循环中使用静态变量,而不考虑值可能在另一个线程中更改。避免静力学!

正如OceanBlue指出的那样,从这一点上可能不清楚final static变量不会产生任何危险,但会改变公共静态变量。这不是静态本身的问题,而是它们将拥有一个值的概念,然后发现该值发生了变化。可能很难找出问题所在。典型的例子是单击计数器或计时器值,当单击了多个视图或多个计时器时。

希望你能从比我经验丰富的人那里得到建议。祝你好运!


如果处理UI是您最大的问题,那么您将需要掌握事件驱动的编码。事件驱动编码背后的思想是所有现代用户界面系统的背后,并且在各种各样的事情中都很有用(不仅仅是用户界面)。

当我学习的时候,最简单的方法就是把每一个组件和事件当作是独立的。您只需要担心传递到事件方法中的事件对象。如果你习惯于编写基本上从头到尾都在运行的应用程序,这是一个有点思想上的转变,但是实践会很快让你达到目的。


使用模型视图控制器模式怎么样?

至少你必须在"模型"中隔离(对象或对象集)所有的状态及其逻辑管理一个单独的班级(可能是活动班)所有与视图、侦听器、回调等相关的内容)