【译】使用 Dart & Henson 改进 Andro

作者: 汤涛 | 来源:发表于2016-06-08 18:45 被阅读1347次

    声明:本文也在我的微信公众号 Android程序员(AndroidTrending) 发布。

    原文链接:Better Android Intents with Dart & Henson
    原文作者:Daniel Molinero Reguera
    译文出自:汤涛的简书
    译者:汤涛
    状态:完成

    最近看到这篇文章,感觉不错,就翻译了一下。文中提到的 Android Intent 的种种问题,有些也是我之前遇到的一些痛点,项目规模稍大一些后,有些问题会慢慢暴露出来,虽不是非常严重,但正是对代码的精益求精,才是我们不断进步的源泉,也是我推荐文章的重要标准。作者来自著名的团购鼻祖Groupon公司,相信这篇分享值得大家一看。

    Buster Keaton — The Battling Butler.jpg

    Intent 是 Android 生态系统的重要组成部分。他们用来表达一个执行动作,可分为隐式和显式 Intent。在应用程序内部,所有的 Intent 以一种抽象的方式,一起定义了一个信息传递层。在本文中,我们将解释为什么 Android 创建显式 Intent 的方式容易出错,也给大家展示一些有问题的应对方案。最后,我们将介绍一个生成这种信息传递层的库:Dart & Henson,它使用简单,能方便、快捷与健壮地在你的 Activity 和 Service 之间传递信息。

    显示 Intent 需要明确指定组件,常用于在应用内的 Activity 或 Intent 之间传递信息,额外的信息通过 extras 提供给目标组件,与 Intent 一起传递。比如下面的代码,创建了一个显示 Intent 来启动 Activity:

    Intent intent = new Intent(context, DetailActivity.class);
    intent.putExtra(EXTRA_ITEM_ID, selectedItem.id);
    intent.putExtra(EXTRA_SHOW_MAP, true);
    startActivity(intent);
    

    被启动的 Activity 代码可能是这样的:

    public class DetailActivity extends Activity {
      public static final String EXTRA_ITEM_ID = "extra.item_id";
      public static final String EXTRA_SHOW_MAP = "extra.show_map";
    
      private String itemId;
      private boolean shouldShowMap;
    
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        itemId = getIntent().getStringExtra(EXTRA_ITEM_ID);
        shouldShowMap = getIntent().getBooleanExtra(EXTRA_SHOW_MAP, false);
    
        if (itemId == null) {
          throw new IllegalArgumentException("Item Id is required");
        }
        ...
      }
      ...
    }
    

    这种机制很好地处理了组件的创建与通信,但仍然有一些问题需要注意:

    • 目标组件作为一个实体,对输入没有任何控制权。在我们的例子里,itemId 是必需的,但如果没有传递它,DetailActivity 最好的处理方式也只能是抛出异常。
    • Intent 的创建(完全)不够健壮,并没有对 extra 中的 key 或 value 做任何检查。

    一分预防胜过十分治疗

    有问题的解决方案

    解决这些问题的一个可能的方案是 Intent 工厂模式。它主要由一些工厂方法组成,包含了应用程序里用到的各种 Intent。比如像下面这样的 Intent 工厂:

    public class IntentFactory {
      public Intent newDetailActivityIntent(Context context, String itemId, boolean showMap) {
        Intent intent = new Intent(context, DetailActivity.class);
        intent.putExtra(EXTRA_ITEM_ID, itemId);
        intent.putExtra(EXTRA_SHOW_MAP, showMap);
        return intent;
      }
      ...
    }
    

    然而,这种解决方案有一些局限,并不是一个很好的办法。

    • Intent 工厂是一个集中类,这个类可能会变得很大且复杂。
    • 它违背了开放/闭合原则。对修改并没有关闭,我们将总是需要给每个新的 Activity 添加一个新方法。
    • 目标组件应该是唯一知晓参数细节与逻辑的地方。
    • 可选参数处理。同一个组件有不同的需求,是否应该写不同的方法?还是写一个方法并使用默认值?
    • 它会诱使后续的开发人员模仿,进而产生其他的 Intent 工厂,最终演变成大泥球模式,使得代码越来越糟。

    有一个类似的策略可以分散这些工厂方法到各自的目标组件。也就是指,每个组件可以包含一个(或多个)静态方法,用于生成这些启动它自身的 Intent。这个办法可以解决开放/闭合原则的问题,分解 Intent 工厂,也许还可以避免大泥球模式。尽管如此,关于可选参数的问题依然存在。有人说 builder 模式可以?我们自己实现它?...

    我选择用懒惰的人做困难的工作,因为一个懒惰的人会找到简单的方法完成它。比尔盖茨

    Dart 2 & Henson

    Dart 是一个 Android 开源库。它绑定 Activity 字段到 Intent extra,Butter Knife 也是用类似的方案,关联 Activity 与 XML 布局中的View。在我们的例子里,它看起来是这样:

    
    public class DetailActivity extends Activity {
      @InjectExtra String itemId;
      @Nullable @InjectExtra boolean shouldShowMap;
    
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Dart.inject(this);
        ...
      }
      ...
    }
    

    @InjectExtra 注解声明了一个同名的 extra key,默认情况下,所有的注解字段都是必需的,如果 extra 没有提供,会抛出异常。如果想使其可选,需要加上 @Nullable 注解。接下来,只需要调用 Dart.inject 即可自动生成相关代码。

    Groupon,我们意识到注解里的那些信息,已经足够创建我们一直想要的builder模式。因此,我们决定在 Dart 基础上再进一步:我们做了一个注解处理器,用于生成 Intent builders,这个新模块叫做 Henson,它集成在 Dart 2 中。

    DetailActivity 这个例子里,Henson 生成了一个小型的领域特定语言 (DSL),来使得跳转到 DetailActivity 变得非常容易:

    Intent intent = Henson.with(context)
        .gotoDetailActivity()
        .itemId(selectedItem.id)
        .shouldShowMap(true)
        .build();
    
    startActivity(intent);
    

    首先是通过 Henson.with(context).gotoXXX() 获取目标 Activity 或 Service 的 builder。然后,使用自动生成的方法设置必需的 extras, 比如 itemId 是使用 itemId(String str)。之后,用同样的方式设置可选参数。最后调用 build,你就可以得到一个有效的 Intent,用于启动你的组件。

    这段领域特定语言(DSL)会为所有@InjectExtra 注解标记的字段生成相关类。这相当于一个信息传递层,解决了我们创建 Intent 时碰到的那些问题:

    • 通过注解,目标组件对 extras 拥有完全的控制权
    • DSL 定义在组件内的一处,如果它有修改,产生的问题都可以在编译时被发现。
    • 没有违反开放/闭合原则,实际上,我们什么都不需要写,一切都是自动生成
    • 因为使用了 builder 模式,可选参数很容易实现。
    • 还可以自动补全代码!

    完整的示例代码在这里

    总结

    Henson 创建了一个小型的领域特定语言(DSL),可以更加健壮地构建启动 Activity 与 Service 的 Intent,它允许缺失必需的extra,支持灵活的可选参数,最棒的是,使用 Dart 2 与 Henson,你一行代码也不必写了。😊

    还不赶紧试试?
    f2prateek/dart---Extras "injection" Library for Android

    Groupon 正在寻找优秀的移动开发工程师,加入我们,一起构建像这样的优秀项目吧。

    相关文章

      网友评论

      • xiaobinZh:用这个Jet 功能类似,比较简单,我用了挺久的。
      • 捡淑:阴吹思婷
      • 合肥黑:Dart 2 & Henson网上的资料不多啊,是不是用的人很少?楼主你自己有在用么
      • soaringEveryday:够新颖

      本文标题:【译】使用 Dart & Henson 改进 Andro

      本文链接:https://www.haomeiwen.com/subject/wcdjdttx.html