目录
一: Dagger2 是什么?
二: 为什么要有 Dagger2
三: Dagger2 如何使用
基本的概念
如何使用 Dagger2
高级用法
(1) 构造方法需要其他参数时候
(2) 模块之间的依赖关系
(3) @Named 注解使用
(4) @Singleton 注解
(5) 自定义 Scoped
(6)Subcomponent
(7)lazy 和 Provider
四: MVP + Dagger2
Ps: 文末有架构师进阶资料和面试题资料
一: Dagger2 是什么?
是一个依赖注入框架, butterknife 也是一个依赖注入框架. 不过 butterknife, 最多叫奶油刀, Dagger2 被叫做利器啊, 他的主要作用, 就是对象的管理, 其目的是为了降低程序耦合.
二: 为什么要有 Dagger2
8 年 Android 开发程序员: 架构进阶, Dagger2 的原理及使用详解
下面我就手写了
- public class A {
- public void eat() {
- System.out.print("吃饭了");
- }
- }
使用的时候我们就要
- A a = new A();
- a.eat();
如果现在改了, 早 A 的构造方法中必须传入 B 对象
- public class A {
- private B b;
- public A(B b) {
- this.b = b;
- }
- public void eat() {
- System.out.print("吃饭了");
- }
- }
那么使用的时候
- A a = new A(new B());
- a.eat();
可能就有人说了, 不就加一个对象么, 这里只是我举的一个很简单的例子, 看的感觉很简单, 但是在实际开发中, 如果现在改了一个这个构造方法. 是不是意味着, 整个项目中的都的改, 一不小心, 就是 BUG 啊
三: Dagger2 如何使用
1. 基本的概念
上来给你说, 怎么玩, 肯定懵逼, 这里我简单说一下几个概念, 想有个认知, 在往下看, 会好很多, Dagger 是通过 @Inject 使用具体的某个对象, 这个对象呢, 是由 @Provides 注解提供, 但是呢, 这个 @Provides 只能在固定的模块中, 也就是 @Module 注解, 我们查找的时候, 不是直接去找模块, 而是去找 @Component
8 年 Android 开发程序员: 架构进阶, Dagger2 的原理及使用详解
我们反向推导, 当我们使用
@Inject A a
想要获取 a 对象的示例的时候, Dagger2 会先去找, 当前 Activity 或者 Fragment 所连接的桥梁, 例如上图中, 连接的只有一个桥梁, 实际上可以有多个, 这个桥梁, 会去寻找他所依赖的模块, 如图中, 依赖了模块 A, 和模块 B, 然后在模块中, 会去寻找 @Providers 注解, 去寻找 A 的实例化对象.
2. 如何使用 Dagger2
(1) 引入依赖库
Dagger2 官网
- compile 'com.google.dagger:dagger:2.11'
- annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
(2) 创建 Moudule
- // 第一步 添加 @Module 注解
- @Module
- public class MainModule {
- }
(3) 创建具体的示例
- // 第一步 添加 @Module 注解
- @Module
- public class MainModule {
- // 第二步 使用 Provider 注解 实例化对象
- @Provides
- A providerA() {
- return new A();
- }
- }
(4) 创建一个 Component
- // 第一步 添加 @Component
- // 第二步 添加 module
- @Component(modules = {MainModule.class})
- public interface MainComponent {
- // 第三步 写一个方法 绑定 Activity /Fragment
- void inject(MainActivity activity);
- }
- (5)Rebuild Project
8 年 Android 开发程序员: 架构进阶, Dagger2 的原理及使用详解
然后 AS 会自动帮我们生成一个
8 年 Android 开发程序员: 架构进阶, Dagger2 的原理及使用详解
开头都是以 Dagger 开始的
(6) 将 Component 与 Activity/Fragment 绑定关系
- package com.allens.daggerdemo;
- import Android.support.v7.App.AppCompatActivity;
- import Android.os.Bundle;
- import com.allens.daggerdemo.Bean.A;
- import com.allens.daggerdemo.component.DaggerMainConponent;
- import javax.inject.Inject;
- public class MainActivity extends AppCompatActivity {
- /***
- * 第二步 使用 Inject 注解, 获取到 A 对象的实例
- */
- @Inject
- A a;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- /***
- * 第一步 添加依赖关系
- */
- // 第一种方式
- DaggerMainConponent.create().inject(this);
- // 第二种方式
- DaggerMainConponent.builder().build().inject(this);
- /***
- * 第三步 调用 A 对象的方法
- */
- a.eat();
- }
- }
肯定有小伙伴说了, 为了拿到一个对象, 这么大个弯, 太麻烦了. 别急慢慢看, 路要一步一步走嘛
3. 高级用法
(1) 构造方法需要其他参数时候
怎么说呢, 就和最来时的意思一样,
- A a = new A(new B());
- a.eat();
这种情况, 如何使用 Dagger2 呢
肯定有小伙伴这么想
- @Provides
- A providerA() {
- return new A(new B());
- }
直接 new 一个 B , 这样的使用方法, 是不对的!!!!!!, 不对的!!!!!!!, 不对的!!!!!!!!!
正确的打开方式
这时候, 我们什么都不用该, 只需要在 moudule 中添加一个依赖就可以了
- @Module
- public class MainModule {
- /***
- * 构造方法需要其他参数时候
- *
- * @return
- */
- @Provides
- B providerB() {
- return new B();
- }
- @Provides
- A providerA(B b) {
- return new A(b);
- }
- }
(2) 模块之间的依赖关系
image
模块与模块之间的联系,
- @Module (includes = {BModule.class})// includes 引入)
- public class AModule {
- @Provides
- A providerA() {
- return new A();
- }
- }
这样的话, Dagger 会现在 A moudule 中寻找对象, 如果没找到, 会去找 module B 中是否有被 Inject 注解的对象, 如果还是没有, 那么 GG, 抛出异常
一个 Component 应用多个 module
- @Component(modules = {AModule.class,BModule.class})
- public interface MainComponent {
- void inject(MainActivity activity);
- }
dependencies 依赖其他 Component
- @Component(modules = {MainModule.class}, dependencies = AppConponent.class)
- public interface MainConponent {
- void inject(MainActivity activity);
- }
注意 这里有坑. 一下会讲解
(3) @Named 注解使用
相当于有个表示, 虽然大家都是同一个对象, 但是实例化对象不同就不如
- A a1 = new A();
- A a2 = new A();
- // a1 a2 能一样嘛
Module 中 使用 @Named 注解
- @Module
- public class MainModule {
- private MainActivity activity;
- public MainModule(MainActivity activity) {
- this.activity = activity;
- }
- @Named("dev")
- @Provides
- MainApi provideMainApiDev(MainChildApi mainChildApi, String url) {
- return new MainApi(mainChildApi, activity,"dev");
- }
- @Named("release")
- @Provides
- MainApi provideMainApiRelease(MainChildApi mainChildApi, String url) {
- return new MainApi(mainChildApi, activity,"release");
- }
- }
在 Activity/Fragment 中使用
- public class MainActivity extends AppCompatActivity {
- @Named("dev")
- @Inject
- MainApi apiDev;
- @Named("release")
- @Inject
- MainApi apiRelease;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- DaggerMainComponent.builder()
- .mainModule(new MainModule(this))
- .mainChildModule(new MainChildModule())
- .build()
- .inject(this);
- apiDev.eat();
- apiRelease.eat();
- Log.i("TAG","apiDev--->" + apiDev);
- Log.i("TAG","apiRelease--->" + apiRelease);
- }
- }
打印 Log
- 07-14 01:46:01.170 2006-2006/? I/TAG: apiDev--->com.allen.rxjava.MainApi@477928f
- 07-14 01:46:01.170 2006-2006/? I/TAG: apiRelease--->com.allen.rxjava.MainApi@f2b291c
(4) @Singleton 注解
单利模式, 是不是超级方便, 你想然哪个对象单利化, 直接在他的 Provider 上添加 @Singleton 就行了
例如
- @Singleton
- @Provides
- A providerA(B b) {
- return new A(b);
- }
注意: 第一个坑!!!
如果 moudule 所依赖的 Comonent 中有被单利的对象, 那么 Conponnent 也必须是单利的
- @Singleton
- @Component(modules = {
- MainModule.class
- })
- public interface MainConponent {
- }
然后 在 Activity 中使用, 直接打印 a1 a2 的地址,
- @Inject
- A a2;
- @Inject
- A a1;
可以看到 Log
- 12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
- 12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
不相信的小伙伴可以吧 @Singleton 去掉试试
现在我们完成了单利, 然后做了一个事情, 就是点击某个按钮, 跳转到一个新的 Activiry, 两边都引用同样一个 A 对象, 打印 A 的地址,
说一下, 一个 Conponent 可以被对个 Activity/Fragment 引用, 如
- @Singleton
- @Component(modules = {MainModule.class})
- public interface MainConponent {
- void inject(MainActivity activity);
- void inject(TestAct activity);
- }
上面与两个 Activity, MainActivity 和 TestAct , 都引用相同的 对象, 答应地址看看
- 12-30 00:48:17.477 2788-2788/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
- 12-30 00:48:17.517 2788-2788/com.allens.daggerdemo E/TAG: A2---->com.allens.daggerdemo.Bean.A@4f81861
竟然不同, 说好的单利呢
注意: 第二个坑, 单利对象只能在同一个 Activity 中有效. 不同的 Activity 持有的对象不同
那有人就要问了, 没什么办法么, 我就想全局只要一个实例化对象啊? 办法肯定是有的,
(5) 自定义 Scoped
- /**
- * @作者 : Android 架构
- * @创建日期 :2017/7/14 下午 3:04
- * @方法作用:
- * 参考 Singleton 的写法
- * Scope 标注是 Scope
- * Documented 标记在文档
- * @Retention(RUNTIME) 运行时级别
- */
- @Scope
- @Documented
- @Retention(RUNTIME)
- public @interface ActivityScoped {
- }
首先想一下, 什么样的对象, 能够做到全局单例, 生命周期肯定和 App 绑定嘛, 这里我做演示, 一个 AppAip 我们要对这个对象, 全局单利, 所以二话不说, 先给 Application 来个全家桶,
- Module
- @Module
- public class AppModule {
- @Singleton
- @Provides
- AppApi providerAppApi() {
- return new AppApi();
- }
- }
- Component
- @Singleton
- @Component(modules = AppModule.class)
- public interface AppComponent {
- AppApi getAppApi();
- }
- Application
- public class MyApp extends Application {
- private AppConponent appComponent;
- @Override
- public void onCreate() {
- super.onCreate();
- appComponent = DaggerAppConpoment.create();
- }
- public AppConponent getAppComponent() {
- return appConponent;
- }
- }
最后是如何使用
首先, 这个是个桥梁, 依赖方式, 上文已经说过了
- @ActivityScoped
- @Component(modules = {MainModule.class}, dependencies = AppConponent.class)
- public interface MainComponent {
- void inject(MainActivity activity);
- void inject(TestAct activity);
- }
细心的小伙伴可能已经发现了, 只有在上面一个 MainComponent 添加了一个 @ActivityScoped, 这里说明一下,@Singleton 是 Application 的单利
注意, 第三个坑, 子类 component 依赖父类的 component , 子类 component 的 Scoped 要小于父类的 Scoped,Singleton 的级别是 Application
所以, 我们这里的 @Singleton 级别大于我们自定义的 @ActivityScoped, 同时, 对应 module 所依赖的 component , 也要放上相应的 Scope
好吧, 上面的例子, 打印 Log.
- 12-30 02:16:30.899 4717-4717/? E/TAG: A1---->com.allens.daggerdemo.Bean.AppApi@70bfc2
- 12-30 02:16:31.009 4717-4717/? E/TAG: A2---->com.allens.daggerdemo.Bean.AppApi@70bfc2
一样啦
爬坑指南 (极度重要)
Provide 如果是单例模式 对应的 Compnent 也要是单例模式
inject(Activity act) 不能放父类
即使使用了单利模式, 在不同的 Activity 对象还是不一样的
依赖 component, component 之间的 Scoped 不能相同
子类 component 依赖父类的 component , 子类 component 的 Scoped 要小于父类的 Scoped,Singleton 的级别是 Application
多个 Moudle 之间不能提供相同的对象实例
Moudle 中使用了自定义的 Scoped 那么对应的 Compnent 使用同样的 Scoped
(6)Subcomponent
这个是系统提供的一个 Component, 当使用 Subcomponent, 那么默认会依赖 Component
例如
- @Subcomponent(modules = TestSubModule.class)
- public interface TestSubComponent {
- void inject(MainActivity activity);
- }
- @Component(modules = {MainModule.class})
- public interface MainConponent {
- TestSubComponent add(TestSubModule module);
- }
在 TestSubComponent 中 我 void inject(MainActivity activity);, 便是这个桥梁, 我是要注入到 MainActivity, 但是 dagger 并不会给我生成一个 Dagger 开头的 DaggerTestSubComponent 这个类, 如果我想使用 TestSubModule.class 里面提供的对象, 依然还是使用 DaggerMainConponent 例如
- DaggerMainConponent
- .builder()
- .mainModule(new MainModule())
- .build()
- .add(new TestSubModule())
- .inject(this);
可以看到这里有一个 add 的方法, 真是我在 MainConponent 添加的 TestSubComponent add(TestSubModule module);
(7)lazy 和 Provider
- public class Main3Activity extends AppCompatActivity {
- @PresentForContext
- @Inject
- Lazy<Present> lazy;
- @PresentForName
- @Inject
- Provider<Present> provider;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main3);
- AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
- ActivityComponent activityComponent = DaggerActivityComponent.builder()
- .appComponent(appComponent)
- .activityModule(new ActivityModule())
- .build();
- activityComponent.injectActivity(this);
- Present present = lazy.get();
- Present present1 = provider.get();
- }
- }
其中 Lazy(懒加载) 的作用好比 component 初始化了一个 present 对象, 然后放到一个池子里, 需要的时候就 get 它, 所以你每次 get 的时候拿到的对象都是同一个; 并且当你第一次去 get 时, 它才会去初始化这个实例.
procider(强制加载) 的作用:
1: 同上当你第一次去 get 时, 它才会去初始化这个实例
2: 后面当你去 get 这个实例时, 是否为同一个, 取决于他 Module 里实现的方式
四: MVP + Dagger2
这是现在主流的设计架构
MVP, 这个我会在后面的文章介绍, 这里不做太多解释
当你了解 MVP 的时候, 你就知道, 所有的业务逻辑全在 Presenter,
换句话, presenter 持有的对象, 控制着你程序的全部逻辑, 这在 dagger 中, 讲白了 我们只要将所有的 presetner 对象控制就可以了
下面附上目录结构, 当然仅仅作为参考. dagger 强大的用法还是需要各位自己去体会, 下面的项目 是我刚刚学会 dagger 时候 写的一个项目
8 年 Android 开发程序员: 架构进阶, Dagger2 的原理及使用详解
8 年 Android 开发程序员: 架构进阶, Dagger2 的原理及使用详解
可以看到 , 我是 将所有的 activity 或者 fragment 全部添加在同一个 Component 中, 当然现在的话 不推荐, 比如 Utils 你可以专门做一个 Component,
首先放上我的 Module, 公司项目, 很多东西没敢放上来, 体谅, 可以看到 我这里提供了一个 SplashPresenter, 也就是启动页的 Presneter, 业务逻辑
- @Module
- public class ApiModule {
- public ApiModule() {
- }
- @Provides
- @Singleton
- Handler provideHandler() {
- return new Handler();
- }
- @Provides
- @Singleton
- SQLiteDatabase provideSQLiteDatabase() {
- return new DataBaseHelper(MyApp.context, Config.SqlName, null, Config.SqlVersion).getWritableDatabase();
- }
- /**
- * @ User : Android 架构
- * @ 创建日期 : 2017/7/13 下午 3:24
- * @模块作用 :
- * <p>
- * ====================================================================================================================================
- * ====================================================================================================================================
- */
- private SplashPresenter splashPresenter;
- public ApiModule(SplashAct splashAct) {
- splashPresenter = new SplashPresenter(splashAct, new SplashModel());
- }
- @Provides
- @Singleton
- SplashPresenter provideSplashPresenter() {
- return splashPresenter;
- }
- .....
- }
当我使用的时候, 只需要注入即可, 如下代码
- public class SplashAct extends BaseActivity implements SplashContract.View {
- @Inject
- SplashPresenter presenter;
- @Inject
- Handler handler;
- @Inject
- ApiService apiService;
- @Override
- protected void onCreate() {
- setContentView(R.layout.activity_splash);
- }
- @Override
- protected void initInject() {
- DaggerApiComponent.builder()
- .apiModule(new ApiModule(this))
- .build()
- .inject(this);
- }
- @Override
- protected void initListener() {
- presenter.getWordsInfo(true, apiService);
- }
- @Override
- public void gotoLogInAct() {
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- startActivity(new Intent(SplashAct.this, LogInAct.class));
- finish();
- }
- }, 1500);
- }
- }
最后
给大家分享一份移动架构大纲, 包含了移动架构师需要掌握的所有的技术体系, 大家可以对比一下自己不足或者欠缺的地方有方向的去学习提升;
来源: http://www.jianshu.com/p/c206c351e1be