内容简介:在这篇文章中,我会介绍 什么是依赖注入,Dagger2是什么,解决什么问题以及基础注解的使用举个例子
在这篇文章中,我会介绍 什么是依赖注入,Dagger2是什么,解决什么问题以及基础注解的使用
依赖注入
什么是 依赖。
举个例子
有一个 A 类 它里面定了一个 B 类型的 属性 b; 这里 A 就依赖了 B;
public class A{ public A(){ b = new B(); b.print(); } private B b; }
这就意味着 A 离开 B 不能单独运行,也就是说 A 在哪里工作,B就会跟到哪里,A 无法离开 B 被复用。
这种情况下 A 就是 依赖者,B就是依赖。依赖者依赖于它的依赖。
两个相互使用的类称为耦合;耦合有强有弱。耦合总是有方向性的。可能 A 依赖 B,但 B 不一定依赖 A。
依赖类型
- 类 / 接口 依赖
- 属性 / 方法 依赖
- 间接 / 直接 依赖
硬编码依赖的不好
在依赖者内部构建或者由依赖者寻找依赖这种就称为 硬编码依赖
- 降低复用性
- 不好测试
- 强耦合
- 增加维护成本
关于 什么是依赖,更详细的硬编码依赖的缺点这部分,更详细的可以参考这篇文章,我就是从篇文章学习来的。
https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-introduction-be6580cb3edb
什么是依赖注入
依赖注入:一个对象提供另一个对象的依赖的技术;
依赖是个能被使用的对象(一个服务);注入是将依赖传递给要使用它的对象(客户端 / 依赖者)。
服务作为客户端的一部分。将服务传递给客户端而不是客户端构建或者寻找服务,这是模式(依赖注入)的基本要求。
换句话说:
依赖作为依赖者的一部分。将依赖传递给依赖者而不是由依赖者构建或者寻找依赖,这是依赖注入的基本要求。
也就是说 依赖从来原来的由依赖者构建,改为现在由外部注入,也可以称为 控制反转。
这样的好处是很明显的,提高可测试性,解偶,降低维护成本等等。
更详细的解释 可以看一下这篇文章,解释的超级棒,如果你看过权力的游戏,就更棒了。
https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-di-part-i-f5cc4e5ad878
Dagger2 就是 Android 平台的一个依赖注入框架,它现在由 Google 维护开发。
Dagger2 是编译时框架,会在编译时根据你的注解配置生成需要的代码。
下面是我对 Dagger2 中的常用注解的理解。理解了这些注解的意思和作用,基本就学会了 Dagger2 的基本用法了。
常用注解
@Inject
这个注解有两个作用:
- 修饰需要注入的属性,Dagger2 会自动注入
- 修饰被注入的类的构造方法上;Dagger2 会在需要的时候通过这个注解找到构造函数自动构造对象注入
public class MainActivity extends AppCompatActivity { @Inject DBManager dbManager; } public class DBManager { @Inject public DBManager(){} }
@Component
这个注解的作用 是连接提供依赖和注入依赖的。相当与一个注射器的角色,将依赖注入到需要的地方。
刚刚通过上面的 @Inject
注解 了 提供依赖的构造方法 和 需要注入的属性,而这样还是不够的,需要使用 @Comnponent
连接起来。
创建一个接口,并定义一个方法,定义要往哪里注入;在编译时期 Dagger2 就会自动生成这个接口的实现类 并以 Dagger 开头。
还可以定义 向外提供实例的方法;Dagger2 都会在编译时期生成相应的代码。
下面是 示例
@Component() public interface MainComponent { void inject(MainActivity mainActivity); DBManager getDBManager(); } // 在需要被注入的类中注入 例如: public class MainActivity extends AppCompatActivity { @Inject DBManager dbManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 注入 DaggerMainComponent.create().inject(this); } }
@Component
有两个属性 modules
and dependencies
;
-
modules
的作用是引用Module
的,下面@Module
会继续说 -
dependencies
的作用是 引用其他Component
使用的,相当于 把其他的Component
当作组件一样引用过来;
@SubComponent
顾名思义 就是 Comnponent 的儿子,它也表示一个注射器的角色,不过它可以继承 Component的全部 属性。
Dagger2 不会生成 Dagger开头的 DaggerSubComponent 这种类,所以,SubComponent 需要在 Component 注册和维护。这样的也好统一管理维护,Dagger2 会在生成 Component的时候自动实现生成在内定义的方法。
举个例子 我的 ApplicationComponent 是个全局单例的,有 NetModule, APPModule,等等很多全局性依赖,如果我的 Activity 的注射器 使用 @SubComnponent
,那么就可以使用Application的全部依赖。
@ActivityScoped @Subcomponent(modules = MainModule.class) public interface MainComponent { void inject(MainActivity mainActivity); } @APPScoped @Component(modules = {APPModule.class, APIModule.class} ) public interface APPComponent { MainComponent plus(MainModule module); SecondComponent plus(SecondModule module); } //注入 DaggerAPPComponent.builder() .aPPModule(new APPModule(getApplication())) .build() .plus(new SecondModule()) .inject(this);
当然还有另外一种方法不用 @SubComponent
,使用 Component
并使用 denpendencies
引用上 ApplicationComponent
这样就相当于将 ApplicationComponent
组合进来。
@Module && @Provides
@Module
这个注解用来标注提供依赖的工厂。对的,工厂,我是这么理解的。
@Provides
这个注解用在提供定义提供依赖的方法上,表示向外提供依赖。方法的返回类型就是提供的依赖类型。
前面提到的 @Inject
可以在注解在构造函数以用来提供依赖;而在 @Inject
不能满足需要的时候这个就派上用场了。
例如 我注入一个 字符串,数字或一个 第三方依赖的对象 例如 Retrofit , @Inject
已经满足不了啦。
这个时候可以创建一个类 专门用来提供这些依赖,并使用 @Module
注解,然后在 Component
的属性 modules
引用上就可以使用了。
// 需要注入的 Activity public class ThirdActivity extends AppCompatActivity { @Inject String name; @Inject int age; @Inject OkHttpClient client; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_third); DaggerThirdComponent.create().inject(this); Log.e(ThirdActivity.class.getSimpleName(), "onCreate: name-"+name+";age-"+age+";client-"+client); } } // 提供依赖的 工厂 @Module public class ThirdModule { @Provides public String provideName(){ return "skymxc"; } @Provides public int provideAge(){ return 24; } @Provides public OkHttpClient provideOkHttpClient(){ OkHttpClient client = new OkHttpClient.Builder().build(); return client; } } // 连接 依赖和注入方 ,在这里引用 依赖提供方。 @Component(modules = ThirdModule.class) public interface ThirdComponent { void inject(ThirdActivity activity); }
@Named
在依赖迷失时给出方向。
解释一下 依赖迷失 :
依旧是上面那个例子,现在 都是根据返回值类型来注入的,现在都是不同的类型所以还没有出现迷失的情况;
现在我如果要加上 地址 属性;如下
// activity内 @Inject String name; @Inject int age; @Inject OkHttpClient client; @Inject String address; // module 中 @Provides public String provideName(){ return "skymxc"; } @Provides public int provideAge(){ return 24; } public String provideAddress(){ return "北京"; }
这个时候 在 module 中 有两个返回 String 类型的 方法,Dagger2 这个时候就不知道注入哪一个了,所以就会出现 依赖迷失 的情况;
错误: [Dagger/DuplicateBindings] java.lang.String is bound multiple times: @Provides String com.skymxc.example.dagger2.di.module.ThirdModule.provideAddress() @Provides String com.skymxc.example.dagger2.di.module.ThirdModule.provideName()
简单的解决方法就是在 属性和提供依赖上 加上 @Named
注解
@Named("name") @Provides public String provideName(){ return "skymxc"; } @Provides @Named("address") public String provideAddress(){ return "北京"; } // 在 属性上也加上 @Named("name") @Inject String name; @Named("address") @Inject String address;
这样就可以解决了 依赖迷失。
@Qualifier
@Named
的元注解,解决依赖迷失的大 Boss;看一下 @Named
的源码, @Named
就是被 @Qualifier
注解的。
@Qualifier @Documented @Retention(RUNTIME) public @interface Named { /** The name. */ String value() default ""; }
如果怕通过 @Named
写字符串的方式容易出错就可以通过 @Qualifier
自定义注解来实现。
下面举个例子,再加一个 身高属性。定义两个注解来区分 @Age
and @Height
.
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Height { } @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Age { } //在 module 和 属性上使用 @Age @Provides public int provideAge(){ return 24; } @Provides @Height public int provideHeight(){ return 175; } @Age @Inject int age; @Height @Inject int height;
@Singleton
配合 @Component
实现 范围内单例
@Singleton
必须和 @Component
配合才能实现单例,而且只能保证在 @Component
范围内单例,如果要实现全局单例,就必须要保证 @Component
的实例在全局范围内只有一个,类似 Application 。
举个例子,我要 DBManager
在全局单例,需要以下几个步骤
@Singleton
// 1.DBManager 标注 @Singleton @Singleton public class DBManager { @Inject public DBManager(){} } // 2. @Singleton @Component(modules = {APPModule.class, APIModule.class}) public interface APPComponent { MainComponent plus(MainModule module); SecondComponent plus(SecondModule module); //可有可无 为了测试 DBManager getDBManager(); } //3. 在 Application 中获取 实例,并保证唯一实例 public class MApplication extends Application { private APPComponent appComponent; @Override public void onCreate() { super.onCreate(); appComponent = DaggerAPPComponent.builder() .aPPModule(new APPModule(this)) .build(); } public APPComponent getAppComponent() { return appComponent; } } // 测试,在 MainActivity 注入两个。 public class MainActivity extends AppCompatActivity { @Inject DBManager dbManager; @Inject DBManager dbManager1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //使用 Application 获取 AppComponent ((MApplication)getApplication()).getAppComponent() .plus(new MainModule()) .inject(this); Log.e(MainActivity.class.getSimpleName(), "onCreate: appdb-->"+((MApplication)getApplication()).getAppComponent().getDBManager().hashCode()); //是否是全局范围内单例 if (dbManager==dbManager1) { Log.e(MainActivity.class.getSimpleName(), "onCreate: dbmanager-sintleton->"+dbManager.hashCode()); }else{ Log.e(MainActivity.class.getSimpleName(), "onCreate: dbmanager:"+dbManager.hashCode()); Log.e(MainActivity.class.getSimpleName(), "onCreate: dbmanager1:"+dbManager1.hashCode()); } } } // 在 SecondActivity 注入两个看看是否和 Main 中的是一个实例 public class SecondActivity extends AppCompatActivity { @Inject DBManager dbManager; @Inject DBManager dbManager1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); ((MApplication)getApplication()).getAppComponent() .plus(new SecondModule()) .inject(this); if (dbManager==dbManager1) { Log.e(SecondActivity.class.getSimpleName(), "onCreate: dbmanager-singleton->"+dbManager.hashCode()); }else{ Log.e(SecondActivity.class.getSimpleName(), "onCreate: dbmanager:"+dbManager.hashCode()); Log.e(SecondActivity.class.getSimpleName(), "onCreate: dbmanager1:"+dbManager1.hashCode()); } } }
测试结果必须是全局唯一单例,看一下 log
E/MainActivity: onCreate: appdb-->192114699 onCreate: dbmanager-sintleton->192114699 E/SecondActivity: onCreate: dbmanager-singleton->192114699
@Singleton
的作用域 始终是跟随所在的 Component
的实例的,如果超出它的范围就无法保证单例。
就拿上个例子举例,如果每次 在 Activity 注入的时候 不从 Application 获取实例而是每次都是使用 DaggerAppComponent 创建一个新的 实例 ,那么就无法保证两个 Activity 内的 DBManager 都是一个实例了,因为每个 Activity 都是获取新的 AppComponent 的实例,它的作用范围只能在单个实例内。
下面我实现一个 只在 Activity 范围实现单例的 例子,就是把上面的代码改改,在Activity注入的时候 创建新的 Component 实例。
public class SecondActivity extends AppCompatActivity { @Inject DBManager dbManager; @Inject DBManager dbManager1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); // ((MApplication)getApplication()).getAppComponent() // .plus(new SecondModule()) // .inject(this); // 获取新实例 DaggerAPPComponent.builder().aPPModule(new APPModule(getApplication())).build().plus(new MainModule()).inject(this); if (dbManager==dbManager1) { Log.e(SecondActivity.class.getSimpleName(), "onCreate: dbmanager-singleton->"+dbManager.hashCode()); }else{ Log.e(SecondActivity.class.getSimpleName(), "onCreate: dbmanager:"+dbManager.hashCode()); Log.e(SecondActivity.class.getSimpleName(), "onCreate: dbmanager1:"+dbManager1.hashCode()); } } } public class MainActivity extends AppCompatActivity { @Inject DBManager dbManager; @Inject DBManager dbManager1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 获取新实例 DaggerAPPComponent.builder().aPPModule(new APPModule(getApplication())).build().plus(new MainModule()).inject(this); if (dbManager==dbManager1) { Log.e(MainActivity.class.getSimpleName(), "onCreate: dbmanager-sintleton->"+dbManager.hashCode()); }else{ Log.e(MainActivity.class.getSimpleName(), "onCreate: dbmanager:"+dbManager.hashCode()); Log.e(MainActivity.class.getSimpleName(), "onCreate: dbmanager1:"+dbManager1.hashCode()); } } } // log 09-23 00:02:52.937 E/DBHelper: DBHelper: 09-23 00:02:52.937 E/MainActivity: onCreate: dbmanager-sintleton->115289709 09-23 00:02:57.097 E/DBHelper: DBHelper: 09-23 00:02:57.097 E/SecondActivity: onCreate: dbmanager-singleton->64826129
总结 : Dagger2 实现单例要 @Singleton
和 @Component
|| @SubComponent
配合使用,只能实现范围内(实例内)单例,所以范围要控制好。只要范围控制好,随意 Activity 或者 Application 范围。
@Scope
作用域 上面说到的 @Singleton
就是它的默认实现,也是唯一一个默认实现。
看一下 @Singleton
的源码
/** * Identifies a type that the injector only instantiates once. Not inherited. * * @see javax.inject.Scope @Scope */ @Scope @Documented @Retention(RUNTIME) public @interface Singleton {}
@Singleton
能够实现范围内单例 主要是 @Scope
在起作用。默认实现叫 Singleton
也是为了更好的理解。
我们可以根据自己的情况,自定义我们自己的依赖作用域,就像我们上面说的 跟随 Application 生命周期的,跟随 Activity 生命周期的,或者 User 生命周期的等等。
举个例子 我们定义俩个 AppScoped, ActivityScoped. 分别让我们的依赖实现 全局单例和Activity内单例
/** * APP全局单例 * 此注解使用的 Component 要全局范围内唯一 ,不然无法实现全局单例 */ @Scope @Retention(RetentionPolicy.RUNTIME) public @interface APPScoped { } /** * activity 内单例 * 使用 此注解的Component 生命周期要跟随 Activity 的生命周期。 */ @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScoped { }
- 创建一个类 SingletonObj 让其在 Activity范围内 单例, 让 DBManager 全局单例
@ActivityScoped public class SingletonObj { @Inject public SingletonObj(){} } /** * */ @APPScoped public class DBManager { @Inject DBHelper helper; @Inject public DBManager(){} }
- 定义 Component ,注意 AppScoped , ActivityScoped 的位置
@APPScoped @Component(modules = { APIModule.class,APPModule.class}) public interface APPComponent { MainComponent plus(MainModule module); SecondComponent plus(SecondModule module); DBManager getDBManager(); }
@ActivityScoped @Subcomponent(modules = MainModule.class) public interface MainComponent { void inject(MainActivity mainActivity); }
@ActivityScoped @Subcomponent(modules = SecondModule.class) public interface SecondComponent { void inject(SecondActivity activity); }
- 获取 Component 并开始注入
在 Application 获取 AppComponent 的实例 ,并保持唯一。
public class MApplication extends Application { private APPComponent appComponent; @Override public void onCreate() { super.onCreate(); appComponent = DaggerAPPComponent.builder() .aPPModule(new APPModule(this)) .build(); } public APPComponent getAppComponent() { return appComponent; } }
在 MainActivity 获取到 MainComponent 的实例 并注入
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ @Inject DBManager dbManager; @Inject DBManager dbManager1; @Inject SingletonObj mainSingleton; @Inject SingletonObj mainSingleton1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.bt_to_second).setOnClickListener(this); findViewById(R.id.bt_to_third).setOnClickListener(this); ((MApplication)getApplication()).getAppComponent() .plus(new MainModule()) .inject(this); Log.e(MainActivity.class.getSimpleName(), "onCreate: appdb-->"+((MApplication)getApplication()).getAppComponent().getDBManager().hashCode()); //查看 是否和 second的一致,是否是全局范围内单例 if (dbManager==dbManager1) { Log.e(MainActivity.class.getSimpleName(), "onCreate: dbmanager-sintleton->"+dbManager.hashCode()); }else{ Log.e(MainActivity.class.getSimpleName(), "onCreate: dbmanager:"+dbManager.hashCode()); Log.e(MainActivity.class.getSimpleName(), "onCreate: dbmanager1:"+dbManager1.hashCode()); } //主要看 这个 和 second的是否一致,是否是activity范围内单例。 if (mainSingleton==mainSingleton1){ Log.e(MainActivity.class.getSimpleName(), "onCreate: main-singleton->"+mainSingleton.hashCode()); }else{ Log.e(MainActivity.class.getSimpleName(), "onCreate: main:"+mainSingleton.hashCode()); Log.e(MainActivity.class.getSimpleName(), "onCreate: main1:"+mainSingleton1.hashCode()); } } }
在 SecondActivity 获取到 SecondComponent 的实例 并注入 ,这里就可以看出来 是否是 范围内单例。
public class SecondActivity extends AppCompatActivity { @Inject DBManager dbManager; @Inject DBManager dbManager1; @Inject SingletonObj mainSingleton; @Inject SingletonObj mainSingleton1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); ((MApplication)getApplication()).getAppComponent() .plus(new SecondModule()) .inject(this); if (dbManager==dbManager1) { Log.e(SecondActivity.class.getSimpleName(), "onCreate: dbmanager-singleton->"+dbManager.hashCode()); }else{ Log.e(SecondActivity.class.getSimpleName(), "onCreate: dbmanager:"+dbManager.hashCode()); Log.e(SecondActivity.class.getSimpleName(), "onCreate: dbmanager1:"+dbManager1.hashCode()); } if (mainSingleton==mainSingleton1){ Log.e(SecondActivity.class.getSimpleName(), "onCreate: main-singleton>"+mainSingleton.hashCode()); }else{ Log.e(SecondActivity.class.getSimpleName(), "onCreate: main:"+mainSingleton.hashCode()); Log.e(SecondActivity.class.getSimpleName(), "onCreate: main1:"+mainSingleton1.hashCode()); } } }
log 可以看出 范围内单例
E/MainActivity: onCreate: appdb-->229426894 onCreate: dbmanager-sintleton->229426894 onCreate: main-singleton->142055919 E/SecondActivity: onCreate: dbmanager-singleton->229426894 onCreate: main-singleton>241744847
总结 :我们可以通过 @Scope
随意自定义我们自己的作用域,当然不是说我们定义了 ActivityScoped 他就能保证 Activity内单例了,要配合 Component 范围并用对位置。
这些Demo 的代码 我放在了 Github
基础部分就先介绍这些吧,接下来我会继续 Dagger2-Android 的分享。
参考资料
- https://google.github.io/dagger/
- https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-introduction-be6580cb3edb
- https://proandroiddev.com/how-to-dagger-2-with-android-part-1-18b5b941453f
- https://blog.csdn.net/briblue/article/details/75578459
- https://juejin.im/entry/593cee56ac502e006b3dc9c2
- https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-dagger-2-part-i-f2de5564ab25
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入理解SPARK
耿嘉安 / 机械工业出版社 / 2016-1-1 / 99
《深入理解SPARK:核心思想与源码分析》结合大量图和示例,对Spark的架构、部署模式和工作模块的设计理念、实现源码与使用技巧进行了深入的剖析与解读。 《深入理解SPARK:核心思想与源码分析》一书对Spark1.2.0版本的源代码进行了全面而深入的分析,旨在为Spark的优化、定制和扩展提供原理性的指导。阿里巴巴集团专家鼎力推荐、阿里巴巴资深Java开发和大数据专家撰写。 本书分为......一起来看看 《深入理解SPARK》 这本书的介绍吧!