一看就会,Dagger原理简单分析

Dagger2是一款依赖注入框架,相比于黄油刀,Dagger2的入门难度系数更大。如果你还没有使用过Dagger2,赶紧去尝试一下吧。

首先说一下Dagger2,可以帮我们干哪些活。使用过MVP开发的老铁们都知道,Activity持有Presenter层的实例,也就是说 ,我们需要去实例化这个Presenter,而Presenter持有了View层接口的实例。这样程序之间耦合性非常严重,我们希望,把某个类的实例化过程放到一个容器中,当前的Activity不需要去管理他的实例化过程,说的直白点,不用我们手动的去new 这个对象了。我们仔细想想,这样做有什么好处,假设程序中有很多的地方用到了这个Presenter,有朝一日,我们发现,Presenter设计的不够完美,构造方法需要改动,这样一来可就麻烦了,所有用到的Presenter的地方,都需要去进行改动。这个时候,Dagger派上用场了

常规写法:

public  class MainActivity extends AppCompatActivity implements IView{
    public MainPresenter mainPresenter;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.tv_1);
        // 实例化Presenter
        mainPresenter = new MainPresenter(this);
        mainPresenter.getData();
    }

    @Override
    public void getString(String str) {
        // Iview 接口的回调
        textView.setText(str);
    }
}

public interface IView {
    void  getString(String str);
}

public class MainPresenter {
    IView  iView;
    MainPresenter(IView iView) {
        this.iView = iView;
    }
    // 提供给 Activity层调用
    public void getData() {
       //  dosomething:假设通过Model层获取到数据之后,回调给IView。
        iView.getString("hello java");
    }
}

以上就是最简单的MVP的写法(没有写Model层)。

使用Dagger怎么写呢?

public  class MainActivity extends AppCompatActivity implements IView{

    @Inject
    public MainPresenter mainPresenter;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.tv_1);
        DaggerMainComponent.builder()
                .mainViewModule(new MainViewModule(this))
                .build()
                .inject(this);
        mainPresenter.getData();
    }

    @Override
    public void getString(String str) {
         // TODO
        textView.setText(str);
    }
}

public interface IView {
    void  getString(String str);
}

public class MainPresenter {
    IView  iView;
    @Inject
    MainPresenter(IView iView) {
        this.iView = iView;
    }

    public void getData() {
       //  dosomething:假设通过Model层获取到数据之后,回调给IView。
        iView.getString("hello java");
    }
}

相比于常规写法,我们没有在MainActivity里进行new MainPresenter(this)操作,而是使用了 @Inject 来标注了我们想要实例化的类以及标注它的构造函数。使用@Inject相当于做了个"记号",告诉Dagger,我想要这个类的实例,麻烦你去帮我做一下实例化操作。

这里再贴一下,Component和Module的代码:

### MainComponent

@Component (modules = {MainViewModule.class})
public interface MainComponent {
    void inject(MainActivity activity);
}
////////////////////////////////////////////
### MainViewModule
@Module
public class MainViewModule {
    IView iView;
    public MainViewModule(IView iView) {
        this.iView = iView;
    }
    @Provides
     IView create() {
        return iView;
    }
}

以上是Dagger2很简单的使用,当然这也不是我们今天所要关心的,今天的主题是,Dagger2是怎么帮我们实现了 new MainPresenter(this) 这个操作的,内部的逻辑是怎样的呢?

简单分析原理

Dagger2自动生成代码的原理是 通过apt插件在编译时期生成相应的注入代码,接下来我们就一个一个的分析。

1.MainPresenter与MainPresenter_Factory

我们用 @Inject 标注了MainPresenter 以及他的 构造函数,然后我们Rebuild Project一下,会在app/build/generated/source/apt/debug/包名/ 下生成一个 类 MainPresenter_Factory,这里贴一下 MainPresenter与MainPresenter_Factory 的代码:

### MainPresenter_Factory类
public final class MainPresenter_Factory implements Factory<MainPresenter> {
  private final Provider<IView> iViewProvider;

  public MainPresenter_Factory(Provider<IView> iViewProvider) {  
    assert iViewProvider != null;
    this.iViewProvider = iViewProvider;
  }

  @Override
  public MainPresenter get() {  
    return new MainPresenter(iViewProvider.get());
  }

  public static Factory<MainPresenter> create(Provider<IView> iViewProvider) {  
    return new MainPresenter_Factory(iViewProvider);
  }
}

### MainPresenter类
public class MainPresenter {
    IView  iView;

    @Inject
    MainPresenter(IView iView) {
        this.iView = iView;
    }

    public void getData() {
       //  dosomething:假设通过Model层获取到数据之后,回调给IView。
        iView.getString("hello java");
    }
}

我们重点看MainPresenter_Factory 的get()方法,返回类型是 MainPresenter,get()方法内部实际上是去new了一个MainPresenter,传入的参数是iViewProvider.get()。我们不禁会想,Dagger2 是不是在这里帮我们实例化MainPresenter的?答案是肯定的

iViewProvider是一个Provider类型,泛型参数是我们 new MainPresenter()所需要的IView。iViewProvider是在MainPresenter_Factory 的构造函数进行初始化的。

public interface Provider<T> {
    T get();
}

create()返回返回一个 MainPresenter_Factory实例,好像没什么好说的,暂时先放一边。

MainPresenter_Factory类看完了,还是一头雾水,我们知道get()方法会返回一个我们所需要的MainPresenter实例,但是我们不清楚 get()是被谁调用的,以及iViewProvider.get() 是谁提供的呢??

思考: MainPresenter 的构造函数中传入的参数是IView,而IView是接口类型,Dagger明确规定,不能使用@Inject来标注接口和第三方库,碰到这种情况,需要使用到Module,所以我们有理由相信,iViewProvider.get() 是由MainViewModule提供的

带着这两个疑问看下文!!!

2.MainViewModule与MainViewModule_CreateFactory

public final class MainViewModule_CreateFactory implements Factory<IView> {
  private final MainViewModule module;

  public MainViewModule_CreateFactory(MainViewModule module) {  
    assert module != null;
    this.module = module;
  }

  @Override
  public IView get() {  
    IView provided = module.create();
    if (provided == null) {
      throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
    }
    return provided;
  }

  public static Factory<IView> create(MainViewModule module) {  
    return new MainViewModule_CreateFactory(module);
  }
}


////////////////////////////////////////////

@Module
public class MainViewModule {
    IView iView;

    public MainViewModule(IView iView) {
        this.iView = iView;
    }
    @Provides
     IView create() {
        return iView;
    }

}

重点看看 MainViewModule_CreateFactory的get()方法,返回IView,这不就是我们new MainPresenter()所需要的参数吗!但是到这里我们还是不能确定,iViewProvider.get() 是由MainViewModule提供的!

我们虽然找到了MainViewModule_CreateFactory,也看到了它的get()方法的返回值就是我们 new MainPresenter()所需要的那个参数,但是,我们没有找到证据,iViewProvider.get() 就是由MainViewModule提供的。

正当我一筹莫展的时候,想起了之前网上看博客大牛们说,Component的作用像一个桥梁,负责把@Inject和Module的提供的实例一起注入到目标类中.. 莫非,我要找的答案在MainComponent以及它生成的类中吗?

答案也是肯定的!!!

3.MainComponent与DaggerMainComponent

public final class DaggerMainComponent implements MainComponent {
  private Provider<IView> createProvider;
  private Provider<MainPresenter> mainPresenterProvider;
  private MembersInjector<MainActivity> mainActivityMembersInjector;

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

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

  private void initialize(final Builder builder) {  
    this.createProvider = MainViewModule_CreateFactory.create(builder.mainViewModule);
    this.mainPresenterProvider = MainPresenter_Factory.create(createProvider);
    this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), mainPresenterProvider);
  }

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

  public static final class Builder {
    private MainViewModule mainViewModule;
  
    private Builder() {  
    }
  
    public MainComponent build() {  
      if (mainViewModule == null) {
        throw new IllegalStateException("mainViewModule must be set");
      }
      return new DaggerMainComponent(this);
    }
  
    public Builder mainViewModule(MainViewModule mainViewModule) {  
      if (mainViewModule == null) {
        throw new NullPointerException("mainViewModule");
      }
      this.mainViewModule = mainViewModule;
      return this;
    }
  }
}

//////////////////////////////////
@Component (modules = {MainViewModule.class})
public interface MainComponent {
    void inject(MainActivity activity);
}

DaggerMainComponent实现了MainComponent接口。在构造函数里调用了initialize(),initialize()内部做了初始化操作。首先创造了MainViewModule_CreateFactory实例,接着把这个实例作为参数去生成MainPresenter_Factory实例,这不就是我们在第二步一直困惑的地方吗,终于找到证据了,哈哈。 MainPresent实例所需要的依赖由MainViewModule_CreateFactory提供。最后一步是生成MainActivity_MembersInjector实例,我猜测是MainActivity的注入类。

内部类Builder主要是做了创建Module以及自身的实例,不是重点。

我们大概捋一捋,我们所需要的MainPresenter实例由MainPresenter_Factory##get()创建,而创建MainPresent实例所需要的依赖由MainViewModule_CreateFactory提供。但是,在步骤1里,我们还是有个疑问没解决,MainPresenter_Factory##get()是被谁调用的呢?

重点看看DaggerMainComponent###inject()方法,方法内部调用的是 mainActivityMembersInjector.injectMembers(activity),将MainActivity注入到MainActivity_MembersInjector类中。

public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final MembersInjector<AppCompatActivity> supertypeInjector;
  private final Provider<MainPresenter> mainPresenterProvider;

  public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<MainPresenter> mainPresenterProvider) {  
    assert supertypeInjector != null;
    this.supertypeInjector = supertypeInjector;
    assert mainPresenterProvider != null;
    this.mainPresenterProvider = mainPresenterProvider;
  }

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

  public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<MainPresenter> mainPresenterProvider) {  
      return new MainActivity_MembersInjector(supertypeInjector, mainPresenterProvider);
  }
}

MainActivity_MembersInjector构造函数里接收一个Provider<MainPresenter>参数,这个在injectMembers()里会用到。

我们单独把injectMembers()拎出来:

public void injectMembers(MainActivity instance) {  
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    supertypeInjector.injectMembers(instance);
    instance.mainPresenter = mainPresenterProvider.get();
  }

instance.mainPresenter = mainPresenterProvider.get();
看到这行代码的时候,所有的疑问都消除了,也就是说 上面的那个疑问:MainPresenter_Factory##get()是被谁调用的呢?? 它其实是在这里被调用的。

看到这里是不是有种 恍然大悟的感觉!mainPresenterProvider.get()也就是 Dagger2帮助我们生成的MainPresenter实例,刚开始我们还保持怀疑,mainPresenterProvider.get()得到到实例是不是指向了我们MainActivity的那个实例呢? 看到这里,才真正的明白了,将MainPresenter_Factory中创建好的MainPresenter实例赋值给instance(MainActivity)的成员mainPresenter,这样我们用@Inject标注的mainPresenter就得到了实例化。

做个小结吧,Module的作用是 提供依赖,像我们的例子中的:

MainPresenter(IView iView) {
       this.iView = iView;
}

这个IView 实例就是由Module去提供的,而Component的作用像一个桥梁,负责把@Inject和Module的提供的实例一起注入到目标类中,像这个例子,目标类就是MainActivity了。

Android的技术讨论Q群:947460837

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342