写在前面

现在越来越多的使用到了开源项目,但是仅限于使用,却不了解,更谈不上深入。也是因为越来越多的开源项目,平时工作中遇到问题也是第一时间寻找对应的开源项目,少了许多独立的思考。现在虽然能很轻松的完整一些有很多功能的App,但自己只是完成了一下界面,核心的通信、图片加载等功能都是使用的开源项目。从今天开始,我希望搭建一个Android项目的起步工程,一点点了解这些开源项目,不求多么的精通,至少不再是只会调用API。代码会在github上开源,至于能不能一直写下去,要看看我的拖延症还有没有救。

新建工程

使用Android Studio,新建一个空白工程,最小支持Android 4.0

添加Dagger2 依赖

修改app目录下的build.gradle

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

...

apply plugin: 'com.neenbedankt.android-apt'

...

dependencies {
...
    compile 'com.google.dagger:dagger:2.4'
    compile 'org.glassfish:javax.annotation:10.0-b28'
}

在空白工程上测试一下Dagger2

依赖注入的可以通过https://github.com/android-cn/blog/tree/master/java/dependency-injection来了解一下。
通过之前的了解,实现一个简单的例子

HttpEngine.java
@Singleton
public class HttpEngine {

    @Inject
    public HttpEngine() {
    }
}

DataComponent.java
@Singleton
@Component
public interface DataComponent {
    void inject(MainActivity mainActivity);
}

MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Inject HttpEngine mHttpEngine;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerDataComponent.builder().build().inject(this);
    }
}

Dagger2自动生成的代码

上面的代码可以为MainActivity注入一个HttpEngine对象,现在有两个问题,怎么注入的,注入的是单例的对象吗。
查看Dagger2生成的代码
首先查看MainActivity_MembersInjector.java

public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final Provider<HttpEngine> mHttpEngineProvider;

  public MainActivity_MembersInjector(Provider<HttpEngine> mHttpEngineProvider) {
    assert mHttpEngineProvider != null;
    this.mHttpEngineProvider = mHttpEngineProvider;
  }

  public static MembersInjector<MainActivity> create(Provider<HttpEngine> mHttpEngineProvider) {
    return new MainActivity_MembersInjector(mHttpEngineProvider);
  }

  @Override
  public void injectMembers(MainActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.mHttpEngine = mHttpEngineProvider.get();
  }

  public static void injectMHttpEngine(
      MainActivity instance, Provider<HttpEngine> mHttpEngineProvider) {
    instance.mHttpEngine = mHttpEngineProvider.get();
  }
}

MainActivity_MembersInjector从名字就能看出,为MainActivity注入Members。在构造的时候传入一个Provider对象,在injectMembers函数中通过Provider的get获取一个HttpEngine对象,注入到MainActivity中,在MainActivity中,被赋值的成员函数用Inject标识。到这里,明白了怎么向MainActivity注入Members的,但是Provider怎么提供要注入的对象的,还是不知道。

DaggerDataComponent.java

public final class DaggerDataComponent implements DataComponent {
  private MembersInjector<MainActivity> mainActivityMembersInjector;

  private DaggerDataComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static DataComponent create() {
    return builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.mainActivityMembersInjector =
        MainActivity_MembersInjector.create(HttpEngine_Factory.create());
  }

  @Override
  public void inject(MainActivity mainActivity) {
    mainActivityMembersInjector.injectMembers(mainActivity);
  }

  public static final class Builder {
    private Builder() {}

    public DataComponent build() {
      return new DaggerDataComponent(this);
    }
  }
}

在DaggerDataComponent的initialize()函数中,创建了一个 MainActivity_MembersInjector 的实例,传入的是一个 HttpEngine_Factory 的实例。在inject()函数中会调用MainActivity_MembersInjector的injectMembers()函数进行注入。

HttpEngine_Factory.java

public enum HttpEngine_Factory implements Factory<HttpEngine> {
  INSTANCE;

  @Override
  public HttpEngine get() {
    return new HttpEngine();
  }

  public static Factory<HttpEngine> create() {
    return INSTANCE;
  }

HttpEngine_Factory提供了HttpEngine的构造方法,使用enum的方式保证单例模式。

Provider,Injector,Component

至此,一个最简单的注入就完成了,通过上面的例子,可以将Dagger2的注入分为三个部分,Provider,Injector,Component。Provider提供对象或者说构造方法,Injector负责将Provider提供的对象进行注入,至于Injector要使用哪个Provider,由Component来进行指定。

上面的例子,有两个疑问:

  1. 我们没有在任何地方手动的指定HttpEngine和DataComponent的关联,DaggerDataComponent是怎么找到HttpEngine的构造方法的呢?
  2. 怎么使用Dagger2创建一个不是单例的Provider

第一个问题,和Android apt有关,以后慢慢研究。
第二个问题,可以使用 @Provides

@Module
public class DataModule {

    @Singleton
    @Provides
    HttpEngine provideHttpEngine() {
        return new HttpEngine();
    }
}

修改component,指定Module

@Singleton
@Component(modules = DataModule.class)
public interface DataComponent {
    void inject(MainActivity mainActivity);

}

会生成一个新的class

public final class DataModule_ProvideHttpEngineFactory implements Factory<HttpEngine> {
  private final DataModule module;

  public DataModule_ProvideHttpEngineFactory(DataModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public HttpEngine get() {
    return Preconditions.checkNotNull(
        module.provideHttpEngine(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<HttpEngine> create(DataModule module) {
    return new DataModule_ProvideHttpEngineFactory(module);
  }
}

get方法获取的不再是单例的对象。
Module就像是一个Provider的仓库,我们可以定义多个仓库,可以在compont中指定使用那个仓库。

Module

关于Module和@Provides,还有一些疑问:

  1. 一个Compnot指定多个Module会是什么情况。
  2. Module可以继承吗
  3. 如何让@Provides作为一个单例

我们修改一下
HttpEngine.java

public interface HttpEngine {
    String getTag();
}

添加

public class NormalHttpEngine implements HttpEngine {

    @Override
    public String getTag() {
        return "NormalHttpEngine";
    }
}
public class OkHttpEngine implements HttpEngine {

    @Override
    public String getTag() {
        return "OkHttpEngine";
    }
}
@Module
public class DataModule2 {

    @Singleton
    @Provides
    HttpEngine provideHttpEngine() {
        return new NormalHttpEngine();
    }
}

DataComponent修改为

@Singleton
@Component(modules = {DataModule.class, DataModule2.class})
public interface DataComponent {
    void inject(MainActivity mainActivity);
}

build,会出现error

Error:(15, 10) error: com.sw926.xyz.com.sw926.xyz.data.HttpEngine is bound multiple times:
@Provides com.sw926.xyz.com.sw926.xyz.data.HttpEngine com.sw926.xyz.com.sw926.xyz.data.DataModule.provideHttpEngine()
@Provides com.sw926.xyz.com.sw926.xyz.data.HttpEngine com.sw926.xyz.com.sw926.xyz.data.DataModule2.provideHttpEngine()

得出结论,可以指定多个Module,但Provider不能冲突。

修改DataModule2和DataComponent

@Module
public class DataModule2 extends DataModule {

    @Provides
    HttpEngine provideHttpEngine() {
        return new NormalHttpEngine();
    }
}
@Singleton
@Component(modules = DataModule2.class)
public interface DataComponent {
    void inject(MainActivity mainActivity);
}

build,仍然出现error

Error:(13, 16) error: @Provides methods may not override another method. Overrides: @Provides com.sw926.xyz.com.sw926.xyz.data.HttpEngine com.sw926.xyz.com.sw926.xyz.data.DataModule.provideHttpEngine()

**@Provides 的函数不能被override,但是可以被继承**

第三个问题,@Provides 前面加上 @Singleton并不会变成单例的模式,如果想使用单例,可以在Application中创建component,在Activity中进行注入,代码如下:

public class App extends Application {

    private DataComponent mDataComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mDataComponent = DaggerDataComponent.builder().build();
    }

    public DataComponent getDataComponent() {
        return mDataComponent;
    }
}
public class MainActivity extends AppCompatActivity {

    @Inject HttpEngine mHttpEngine;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((App)getApplication()).getDataComponent().inject(this);
    }
}

项目地址: https://github.com/sw926/xyz/tree/dagger2

搭建一个完整的Android工程(一)Dagger2的更多相关文章

  1. 搭建一个完整的Java开发环境

    搭建一个完整的Java开发环境 作为一个Java程序员,配置一个java开发环境是必备的技能,今天给广大菜鸟初学者补上一课.环境的配置,大概就分三个1,JDK 2,Tomcat(或者其他的)3,ecl ...

  2. asp.netmvc 三层搭建一个完整的项目

    接下来用 asp.net mvc 三层搭建一个完整的项目: 架构图: 使用的数据库: 一张公司的员工信息表,测试数据 解决方案项目设计: 1.新建一个空白解决方案名称为Company 2.在该解决方案 ...

  3. 手把手搭建一个完整的javaweb项目

    手把手搭建一个完整的javaweb项目 本案例使用Servlet+jsp制作,用MyEclipse和Mysql数据库进行搭建,详细介绍了搭建过程及知识点. 下载地址:http://download.c ...

  4. 利用vue-cli配合vue-router搭建一个完整的spa流程

    好文章备忘录: 转自:https://segmentfault.com/a/1190000009160934?_ea=1849098 demo源码:https://github.com/1590123 ...

  5. 11. 搭建一个完整的K8S集群

    11. 搭建一个完整的Kubernetes集群 1. kubectl的命令遵循分类的原则(重点) 语法1: kubectl 动作 类 具体的对象 例如: """ kube ...

  6. react全家桶从0搭建一个完整的react项目(react-router4、redux、redux-saga)

    react全家桶从0到1(最新) 本文从零开始,逐步讲解如何用react全家桶搭建一个完整的react项目.文中针对react.webpack.babel.react-route.redux.redu ...

  7. Django1.8教程——从零开始搭建一个完整django博客(一)

    第一个Django项目将是一个完整的博客网站.它和我们博客园使用的博客别无二致,一样有分类.标签.归档.查询等功能.如果你对Django感兴趣的话,这是一个绝好的机会.该教程将和你一起,从零开始,搭建 ...

  8. 转载自 BotVS 「 珍藏版 」如何搭建一个完整的交易框架

    [img]http://dn-filebox.qbox.me/8c218c119046b2a25df2d9c7b00c1e0fa6899bdd.png[/img]NO:01 交易策略 ≠ 交易系统. ...

  9. Kubernetes — 从0到1:搭建一个完整的Kubernetes集群

    准备工作 首先,准备机器.最直接的办法,自然是到公有云上申请几个虚拟机.当然,如果条件允许的话,拿几台本地的物理服务器来组集群是最好不过了.这些机器只要满足如下几个条件即可: 满足安装 Docker ...

随机推荐

  1. RichTextBoxEx

    using System; using System.Collections.Specialized; using System.Drawing; using System.Drawing.Imagi ...

  2. VS自动生成的packages.config配置文件有什么用?

    通过nuget管理安装了程序包之后,Visual Studio会自动生成一个关于这些程序包版本的配置文件,删除或者保留它对程序不会造成什么影响.

  3. jquery的promise实践--连续加载图片

    在javascript设计模式实践之代理模式--图片预加载中用代理模式实现了图片预加载功能. 现在就更进一步,完成一个能够一张一张的连续图片加载的功能. 功能: 1.一张一张加载图片. 2.加载错误, ...

  4. 使用nodejs+express(4.x+)实现文件上传

    最简单的做法是通过“connect-multiparty”中间件实现上传. 通过在项目中npm install connect-multiparty进行安装. 用法: var multipart = ...

  5. GitHub for Windows 2.0使用教程

    Git是目前最先进的分布式版本控制系统,作为一个程序员,我们需要掌握其用法. 一:下载GitHub for Windows 2.0 二:安装GitHub  下载之后点击进行安装过程,安装之后桌面上会有 ...

  6. 查看SQL Server多实例的版本

    通过 select @@version 查看当前的 SQL Server 安装的版本: 结果返回的是 SQL Server 2008 R2 (SP1),可安装的明明是 SQL Server 2012  ...

  7. 修复 XE8 FMX Windows 列印旋转文字问题

    问题:XE8 Firemonkey Windows 无法列印旋转文字(与显示在视窗里的代码相同时) 适用:XE8 Windows 平台(其它平台测试没问题) 修复前效果: 修复后效果: 修复方法: 请 ...

  8. DokuWiki用storage的模式在sae上部署后速度太慢

    利用sae做的io wrapper接口,对dokuwiki做了基于sae的分布式storage存储改造,能正常安装和运行,但速度太慢,基本没什么用. 定义一个常量: define('SAESTORE_ ...

  9. 自行实现PHP代码注解特性

    PHP 注解 到目前为止,PHP的反射特性中是不支持注解Annotation的,但是可以支持基本的文档注释内容的获取 ReflectionMethod::getDocComment() - 从5.1. ...

  10. java-spring-mvc_上傳下載文件配置及controller方法

    下载: 1.在spring-mvc中配置(用于100M以下的文件下载) <bean class="org.springframework.web.servlet.mvc.annotat ...