Android依赖注入应用

Stella981
• 阅读 567
  • 依赖注入(DI)是一种设计模式,允许在运行时或编译时移除或改变硬编码的依赖性。使用依赖注入库可以减少编码量、把精力专注在更有价值的地方、降低维护成本。

  • Android程序通常使用注解(Annotation,例如@Click)实现“声明式编程”和依赖注入。

  • 注:“声明式编程”告诉机器在什么地方做什么事(Where to do What),让机器去决定如何干。而传统的“命令式编程”则需要告诉机器如何干(How to do What)。 声明式编程可以让程序员从具体的编码实现中解脱,减少编码量,把精力专注在更有价值的地方。

常见的依赖注入库

  • Dagger:实现了类构造注入(POJO注入),不支持函数注入。功能太简单,不建议使用。

  • ButterKnife:实现了视图注入、资源注入、事件绑定,使用自动生成的内部类实现注入。

    • 可在 Activity、Fragment 或 Adapter 类中注入视图:使用@Bind注解将每个视图字段与其资源ID关联,不用写findViewById和类型转换代码。 例如:@Bind(R.id.title) TextView title;

      支持视图数组绑定,例如:

      @Bind({ R.id.first_name, R.id.middle_name, R.id.last_name })
      List<EditText> nameViews;
      
    • 自动绑定事件处理函数:在事件处理函数上添加注解(@OnClick@OnItemSelected等),不用写事件处理的匿名侦听类和事件绑定代码。例如:

      @OnClick(R.id.submit)
      public void submit(View view) {
          // TODO submit data to server...
      }
      
    • 资源注入:使用@BindColor@BindDimen@BindDrawable@BindString等注解将资源ID与对象绑定。 例如:@BindDrawable(R.drawable.graphic) Drawable graphic;

    • 需要调用 ButterKnife.inject(activity) 在运行时动态绑定。 具体绑定关系和事件处理匿名侦听类是在预编译阶段自动生成的匿名类中实现的。

      所要绑定的视图字段的可访问性为package或public级别。

    • Eclipse中只需要一个jar包,用于自动生成依赖注入类。运行期不需要额外的jar包。

    • 在 Android Studio 开发环境中有自动添加注解的插件(Zelezny),可自动插入所有视图注解和视图字段。

      Android依赖注入应用

  • RoboGuice:实现了视图注入、资源注入、系统服务注入、POJO注入、测试用例支持,基于 Google Guice 库实现。

    • 视图注入、资源注入、系统服务注入类似于 ButterKnife。例如: @InjectView(R.id.textView1) TextView textView1; @InjectResource(R.string.app_name) String name; @Inject LayoutInflater inflater;

    • POJO注入(类构造注入):省略构造函数,自动创建成员对象。例如 @Inject Foo foo;

    • 没有事件绑定功能。如需要可使用 ButterKnife 或 AndroidAnnotations。

    • 继承限制:你得在 RoboActivity 和 RoboFragment 中继承其中一个才能在Activity事件或Fragment中使用 RoboGuice 。

    • 安装:需要 roboguice、guice、jsr 等三个jar包。

  • AndroidAnnotations:实现了依赖注入(视图注入、事件绑定、资源注入、系统服务注入、值传递注入)、简化的线程模型、REST客户端。

    • 依赖注入和事件绑定:与 ButterKnife 类似,但需要在 Activity 等相关类前使用 @EActivitie 等注解以便使用依赖注入功能, 不需要像 ButterKnife 那样写额外的 inject() 绑定语句。

      下面的例子使用了 @EActivity@ViewById@Click@AfterViews等注解,简化了以往 onCreate 的初始化代码。

      @EActivity(R.layout.activity_main)              // 代替setContentView,@EActivity生成子类
      public class MainActivity extends Activity {
          private IViewHelper mHelper = ViewFactory.createHelper();
          @ViewById(R.id.lineWidthBar) SeekBar mLineWidthBar;     // 视图注入
      
          @Click void line_btn() {                    // 事件绑定,不指定ID就用名称查找资源
              mHelper.setCommand("line");
          }
      
          @Click(R.id.rect_btn)
          void onRectClick() {                        // 指定了ID就可以用别的函数名
              mHelper.setCommand("rect");
          }
      
          @SeekBarProgressChange(R.id.lineWidthBar)
          void onProgressChanged(SeekBar seekBar, int progress) {
              mHelper.setStrokeWidth(progress);
          }
      
          @SeekBarTouchStart(R.id.lineWidthBar)
          void onStartTrackingTouch(SeekBar seekBar) {
              mHelper.setContextEditing(true);
          }
      
          @SeekBarTouchStop(R.id.lineWidthBar)
          void onStopTrackingTouch(SeekBar seekBar) {
              mHelper.setContextEditing(false);
          }
      
          @AfterViews                                 // 视图已注入后
          void init() {
              // Do more after views injected
          }
      }
      
    • 简化的线程模型:用简单函数调用的方式使用UI线程和后台线程。例如:

      @Background             // 开启新线程后台运行,注意不要引用UI控件,而且返回值类型一定是void
      void someBackgroundWork(String name, long timeToDoSomeLongComputation) {
          try {
              TimeUnit.SECONDS.sleep(timeToDoSomeLongComputation);
          } catch (InterruptedException e) {
          }
      
          updateUi(String.format(helloFormat, name), androidColor);   // 直接调用就切到UI线程了
          showNotificationsDelayed();
      }
      
      @UiThread               // UI线程,可以指定多个参数
      void updateUi(String message, int color) {
          setProgressBarIndeterminateVisibility(false);
          textView.setText(message);  // UI线程中可访问UI控件
          textView.setTextColor(color);
      }
      
      @UiThread(delay=2000)   // 可以设置延时毫秒
      void showNotificationsDelayed() {
      }
      
    • 使用自动生成的子类实现注入,子类名为原类名加上一个下划线。例如在 AndroidManifest.xml 中使用的Activity改为android:name=".MainActivity_"

    • 在Eclipse中需要一个编译期jar包(androidannotations-X.Y.Z.jar),用于预编译阶段自动生成依赖注入的子类。 运行期需要另一个jar包(androidannotations-api-X.Y.Z.jar),约120KB。

    因为 AndroidAnnotations (简称AA)功能较多,可大幅减少代码量,易于理解和维护,所以本文推荐使用AA。

    本文接下来介绍在 ADT Bundle(Eclipse)开发环境下使用 AA 的方法。

插件安装和工程配置

  • 安装 Eclipse Java Development Tools 插件,以便启用 Annotation Processing

  • 下载并解压 AA的jar包

    • androidannotations-X.Y.Z.jar 用于预编译自动生成依赖注入代码文件,需要复制到项目中的某个目录(例如新建的 compile-libs 目录中,外部目录也行),不能是 libs 目录。

    • androidannotations-api-X.Y.Z.jar 用于注解和程序运行,需要放到项目的 libs 目录。

      为了查看源码,建议也放到 compile-libs 目录并 Add to Build Path,然后设置其 Java Source Attachment 为 androidannotations-api-X.Y.Z-sources.jar。 该jar文件根据自己的需要放在项目的 compile-libs 目录或外部目录,对于多人团队建议放在项目内。

  • 设置程序工程的属性,启用 Annotation Processing

    Android依赖注入应用

    选中程序工程,打开 Project | Properties。

    查看 Java Compiler ,确定 Compiler compliance level 必须是 1.6 。

    查看 Java Compiler \ Annontation Processing ,选中 Enabled annontation processing 。 在Mac电脑上建议将自动生成的代码目录建议改为 gen,以避免 Eclipse 找不到代码目录(以点号开头的目录自动隐藏)。

    查看 Java Compiler \ Annontation Process \ Factory Path ,点击 Add JARs,把 androidannotations-X.Y.Z.jar 加进来。

    点击确定后将弹出个对话框,提示 annotation 设置变更,要求 rebuild project ,确定即可重建项目。

  • 在工程中使用 AA 的注解

    上面介绍AA时已经举例,这里不再重复,进一步阅读可参考官方Wiki文档

    Cookbook 页面中的“Enhanced components”(例如@EActivity)实质上会在预编译时生成对应的子类,在子类中实现注解注入功能。

    注意:子类的名称就是在原来的类之后加了一个下划线,例如MyActivity_, 需要在AndroidManifest.xml中改变原来的Activity类名,使用相关类时也要做相应替换。

    如果不方便在线浏览官方Wiki文档,可以下载Wiki文档: git clone https://github.com/excilys/androidannotations.wiki.git

注:本文首发于新博客(rhcad.com)

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
添砖java的啾 添砖java的啾
3年前
distinct效率更高还是group by效率更高?
目录00结论01distinct的使用02groupby的使用03distinct和groupby原理04推荐groupby的原因00结论先说大致的结论(完整结论在文末):在语义相同,有索引的情况下groupby和distinct都能使用索引,效率相同。在语义相同,无索引的情况下:distinct效率高于groupby。原因是di
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这