Dagger 2 基本用法
1. Dagger2是什么?
Dagger2是一个依赖注入(Dependency Injection)框架。
什么又是依赖注入呢?
借别人的话来说,就是“目标类中所依赖的其他类的初始化过程,不是通过在目标类中编码的方式完成,而是通过其他手段把已经初始化好的实例自动注入到目标类中”。
再换种方式来说,就是把类实例的初始化过程都挪至一个地方统一管理,具体需要哪个实例时,就从这个地方取出(注入到目标类中)。
他的主要作用,就是对象的管理,其目的是为了降低程序耦合。
2.基本的概念
我们反向推到,当们使用
@Inject
A a
想要获取a对象的示例的时候,Dagger2 会先去找,当前Activity或者Fragment所连接的桥梁,例如上图中,连接的只有一个桥梁,实际上可以有多个,这个桥梁,会去寻找他所依赖的模块,如图中,依赖了模块A,和模块B,然后在模块中,会去寻找@Providers注解,去寻找A的实例化对象。
角色介绍
在讲用法前,先对几个重要角色进行了解。
实例需求端:
一个类中,它包含了实例成员,在使用这些实例前,需要对它们进行初始化,但前面已经说了初始化的过程挪至其他地方。所以这个类的需求是,已经完成初始化的实例对象。
暂且把这样的类称为“实例需求端”。
实例供应端:
进行实例初始化的地方,并对外供应所需的实例。
Dagger2中,供应端有两种方式可以供应实例:(后面会介绍)
第一种:使用Module
第二种:使用@Inject标注构造函数
桥梁:
实例供应端并不知道它要供应的实例交给谁。
这个时候就需要通过桥梁,将实例需求端与实例供应端联系在一起。
Dagger2中,Component就负责扮演桥梁的角色。
3.如何使用Dagger2
(1)引入依赖库
Dagger2官网
(https://github.com/google/dagger)
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 注解 实例化对象@ProvidesA providerA() {return new A();}
}
(4)创建一个Component
//第一步 添加@Component
//第二步 添加module
@Component(modules = {MainModule.class})
public interface MainComponent {//第三步 写一个方法 绑定Activity /Fragmentvoid inject(MainActivity activity);
}
(5)Rebuild Project
这一步是必须的
然后AS 会自动帮我们生成一个
开头都是以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 对象的实例*/@InjectA a;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);/**** 第一步 添加依赖关系*///第一种方式DaggerMainConponent.create().inject(this);//第二种方式DaggerMainConponent.builder().build().inject(this);/**** 第三步 调用A 对象的方法*/a.eat();}
}
4. 其他用法:
1)构造方法需要其他参数时候
@Module
public class MainModule {
/***
* 构造方法需要其他参数时候
*
* @return
*/
@Provides
B providerB() {
return new B();
}
@Provides
A providerA(B b) {
return new A(b);
}
}
(2) 模块之间的依赖关系
模块与模块之间的联系,
@Module (includes = {BModule.class})// includes 引入)
public class AModule {@ProvidesA 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")@ProvidesMainApi provideMainApiDev(MainChildApi mainChildApi, String url) {return new MainApi(mainChildApi, activity,"dev");}@Named("release")@ProvidesMainApi provideMainApiRelease(MainChildApi mainChildApi, String url) {return new MainApi(mainChildApi, activity,"release");}}
在Activity/Fragment中使用
public class MainActivity extends AppCompatActivity {@Named("dev")@InjectMainApi apiDev;@Named("release")@InjectMainApi apiRelease;@Overrideprotected 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);}}
(4)@Singleton注解
单利模式,是不是超级方便,你想然哪个对象单利化,直接在他的Provider上添加@Singleton 就行了
例如
@Singleton@ProvidesA providerA(B b) {return new A(b);}
注意: 第一个坑!!!
如果 moudule所依赖的Comonent 中有被单利的对象,那么Conponnent也必须是单利的
@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
}
然后 在Activity中使用,直接打印a1 a2 的地址,
@InjectA a2;@InjectA 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)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
假如你并不希望在调用inject()方法后,就对@Inject标注的实例进行初始化注入,而是希望在用到该实例的时候再进行初始化,那么我们就可以使用Lazy<T>和Provider<T>来实现。
public class UploadActivity extends AppCompatActivity{@InjectLazy<UploadPresenter> mPresenter1;@InjectProvider<UploadPresenter> mPresenter2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//注入实例DaggerUploadActivityComponent.builder().build().inject(this);}public void xxx(){//调用Lazy的get()方法后才开始初始化Presenter并得到该实例//并且后面每次调用get()方法得到的实例都将是同一个。mPresenter1.get().xxx();//调用Provider的get()方法后才开始初始化Presenter并得到该实例//并且后面每次调用get()方法,都会重新调用供应端的供应方法来获取新实例。mPresenter2.get().xxx();}
}
注意: 如果使用了前面介绍的作用域注解@Scope控制了实例的唯一性,那么即使多次调用Provider的get()方法,得到的依然是同一个实例
其中Lazy(懒加载)的作用好比component初始化了一个present对象,然后放到一个池子里,需要的时候就get它,所以你每次get的时候拿到的对象都是同一个;并且当你第一次去get时,它才会去初始化这个实例.
provider(强制加载)的作用:
1:同上当你第一次去get时,它才会去初始化这个实例
2:后面当你去get这个实例时,是否为同一个,取决于他Module里实现的方式
另外可以使用Provider实现强制加载,每次调用get都会调用Module的Provides方法一次,和懒加载模式正好相反。
8. 限定符注解 @Qualifier
@Qualifier主要是用于解决,因实例供应端存在多个返回类型相同的供应方法而引起歧义的问题。
下面举个例子,现在页面需有两个Dialog,一个用于登录,一个用于注册。
8.1 使用@Named注解区分
Dagger2默认提供了一个@Named注解,从代码可以看出属于@Qualifier的一种实现。
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {String value() default "";
}
在供应端对两个Dialog进行构造。
@Module
public class TestActivityModule {private Context mContext;public TestActivityModule(Context context){mContext = context;}@ProvidesContext context(){return mContext;}@Named("login")@ProvidesDialog loginDialog(Context context){Dialog dialog = new Dialog(context);dialog.setTitle("登录提示");....return dialog;}@Named("register")@ProvidesDialog registerDialog(Context context){Dialog dialog = new Dialog(context);dialog.setTitle("注册提示");....return dialog;}
}
可以看到,在实例供应端中,需在提供Dialog实例的方法上面加@Named注解以区分。如果不加则会报错,因为Dagger2不知道要使用哪个方法来获取Dialog实例。
@Component(modules = TestActivityModule.class)
public interface TestActivityComponent {void inject(TestActivity testActivity);
}
public class TestActivity extends AppCompatActivity{@Named("login")@InjectDialog mDialogLogin;@Named("register")@InjectDialog mDialogRegister; @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//注入实例DaggerTestActivityComponent.builder().testActivityModule(new TestActivityModule(this)).build().inject(this);}
}
在实例需求端中,同样要使用@Named注解标注要注入的实例。让Dagger2知道,
@Named("login")标注的mDialogLogin,需要通过@Named("login")标注的供应方法来获取。
@Named("register")标注的mDialogRegister,需要通过@Named("register")标注的供应方法来获取。
8.2 使用自定义@Qualifier区分
使用@Named注解的话,需要加入字符串来区分,这样比较麻烦也容易出错。所以我们可以使用自定义的限定符注解。
@Qualifier
public @interface DialogLogin {
}
@Qualifier
public @interface DialogRegister {
}
然后把前面涉及的@Named("login")换成@DialogLogin,@Named("register")换成@DialogRegister即可~
9. 作用域注解 @Scope
@Scope的作用是使同一个Component中供应的实例保持唯一。
9.1 使用作用域注解实现局部单例
举例说明:
public class UploadActivity extends AppCompatActivity{@InjectUploadPresenter mPresenter1;@InjectUploadPresenter mPresenter2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//注入实例DaggerUploadActivityComponent.builder().build().inject(this);}
}
- 如果不使用作用域注解,则代码中的mPresenter1,mPresenter2将会是两个不一样的实例,可通过打印内存地址查看。
- 而如下使用作用域注解后,则两者将会是同一个实例,可通过打印内存地址查看。
步骤1 自定义@Scope注解
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {}
步骤2 桥梁Component添加作用域注解
@ActivityScope
@Component(modules = UploadActivityModule.class)
public interface UploadActivityComponent {void inject(UploadActivity uploadActivity);
}
步骤3 供应端中提供实例的方法添加作用域注解
@Module
public class UploadActivityModule {@ActivityScope@ProvidesUploadPresenter uploadPresenter() {return new UploadPresenter();}
}
如果是使用1.3.2方式提供实例,则在类上方添加作用域注解
@ActivityScope
public class UploadPresenter{@Injectpublic UploadPresenter() {}
}
经过@Scope处理后,UploadActivity中的UploadPresenter实例将保持唯一。
9.2 使用作用域注解实现全局单例
全局单例,相信大家就很熟悉了,就是平时用饿汉懒汉等等写的那种单例模式。
前面已经说过@Scope作用是使同一个Component中供应的实例保持唯一。
也就是说,如果我在另一个Activity中再创建了一个新的Component,那么它所提供的UploadPresenter实例也将是新的。这和我们理解的全局单例并不一样。
所以,要想实现全局单例,那就要确保获取实例的Component一直都是同一个。
如何实现呢?
答案是创建一个Component用于提供全局单例的实例(创建过程和4.1基本一样),然后在Application中对该Component进行初始化,以后要获取单例时,都统一通过它来获取。
全局性的实例供应端
@Module
public class AppModule {private Application mApplication;public AppModule (Application application){mApplication = application;}@Singleton@ProvidesApplication application(){return mApplication;}@Singleton@ProvidesActivityStackManager activityStackManager() {return new ActivityStackManager();}
}
全局性的桥梁
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {ActivityStackManager activityStackManager();Application application();
}
Application中初始化
public class MyApplication extends Application {public static AppComponent mAppComponent;@Overridepublic void onCreate() {super.onCreate();mAppComponent= DaggerAppComponent.builder().appModule(new AppModule(this)).build();}
}
每次都通过Application中的AppComponent获取某实例,即可保证全局单例
public class UploadActivity extends AppCompatActivity{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);MyApplication.mAppComponent.activityStackManager().pushOneActivity(this); }@Overrideprotected void onDestroy() {super.onDestroy();MyApplication.mAppComponent.activityStackManager().popOneActivity(this); }
}
- 使用了Dagger2默认提供的作用域注解@Singleton,通过源码可以发现它的实现其实和前面的@ActivityScope是一样的。
- 所以真正实现全局单例的不是并@Singleton,而是使用每次获取实例都通过同一个Component。
- 但由于它的字面意思为单例,所以我们通常把它应用在全局性的桥梁和实例供应端中。
- 全局性的Component中没有加入inject方法来自动注入(当然你也可以这么做,但全局性的比较少这么做),而是加入了activityStackManager()方法,供外部调用来获取实例。
10.@bind
@Binds与@Provides
相信大家经常会使用@Provides来在Module里面提供需要注入对象的构造, 但从来没有用过@Binds.
如果我们需要注入一个接口的实现,我们常常会这么做:
@Provides
public XXInterface providesXX(XXImp imp) {
return imp;
}
其实这样的代码可以通过@Binds简化为
@Binds
public abstract XXInterface bindXX(XXImp imp)
在Dagger2中,一般都是使用@provide方法注入接口。
在我们使用MVP模式搭建Android app的时候,一般我们会这样做,创建一个接口presenter命名为HomePresenter。
public interface HomePresenter {Observable<List<User>> loadUsers()
}
然后创建一个这个接口的实例,叫做HomePresenterImp,
public interface HomePresenter {Observable<List<User>> loadUsers();
}
public class HomePresenterImp implements HomePresenter {public HomePresenterImp(){} @Overridepublic Observable<List<User>> loadUsers(){//Return user list observable}
}
但是请注意,在HomePresenterImp这个类中,我并没有使用@Inject在构造方法上。所以,我们要在module中我们经常会使用一个叫provide的注解提供这个类的对象,就像这样。
@Module
public class HomeModule {@Providespublic HomePresenter providesHomePresenter(){return new HomePresenterImp();}
}
但是,如果我们需要添加一个依赖到presenter叫UserService,那就意味着,我们也要在module中添加一个provide方法提供这个UserService,然后在HomePresenterImp类中加入一个UserService参数的构造方法;
或者
我们可以使用@Binds这个注解就想这样:
@Module
public abstract class HomeModule {@Bindspublic abstract HomePresenter bindHomePresenter(HomePresenterImp homePresenterImp);
}
这个就是告诉dagger,这个HomePresenter是用的HomePresenterImp类实现的。当然,你会注意到这个class是抽象类。这就意味着我们可以添加抽象方法。
那么现在。只要在HomePresenterImp类中的构造方法加上@Inject注解,这样,我们就不需要添加依赖参数来提供provide方法了。
我们只要简单的在构造方法中使用@Inject;
@PerActivity
public class HomePresenterImp implements HomePresenter {private UserService userService;@Injectpublic HomePresenterImp(UserService userService){this.userService = userService;}@Overridepublic Observable<List<User>> loadUsers(){return userService.getUsers();}
}
如果你有提供实例类的方法只调用构造函数注入接口。在dagger中使用@Binds注解可以代替原有的样板模式。
英文好的就看英文吧,英文翻译过来,有些地方还是有点不能很好的解释清楚。
附上链接(需要翻墙):android.jlelse.eu/inject-interfaces-without-providing-in-dagger-2-618cce9b1e29
11. binds 相关
// from https://blog.csdn.net/weixin_33924312/article/details/91874838
@BindsOptionalOf
使用dagger2的时候,会有个问题,假如我们在需要被注入的类中请求注入对象A和对象B,如果注入的时候Component没有提供对象B,那么就会编译不通过。
Dagger2中使用了BindsOptionalOf来解决这个问题。
@Modulepublic abstract class TestModule1 {@BindsOptionalOfabstract TestClass1 optionalTestClass1();}
定义如上一个module,然后在原来的module中使用include包含这个module,或者原来component中modules增加这个module。
然后在被注入对象中增加注入
@Inject
Optional<TestClass1> mTestClass1;
注入的类型需要时Optional(一个容器,java 1.8提供,或者guava提供都行)。注意,这个时候我们原来的module并没有 @Provides 方法用来提供TestClass1对象,但是编译能通过。这个时候,注入后,通过optional的get等方法将会返回空值。
我们也可以在module中提供@Provides方法来提供对象,这个时候Optional中就能取到非空的对象。
注意无论是否提供@Provides,请求注入的Optinal对象本身一定不为空,可能为空的是Optional容器内部的内容。
PS 可以注入的类型有
Optional<CoffeeCozy>
Optional<Provider<CoffeeCozy>>
Optional<Lazy<CoffeeCozy>>
Optional<Provider<Lazy<CoffeeCozy>>>
@BindsInstance
存在一种情况,我们需要注入一个对象,这个对象的构造函数需要提供参数。在Dagger2 使用(一)的注入变异3 和 注入变异4中已经有两种解决方案了。分别是通过module提供或者参数的构造函数添加@Inject。
现在,还有一种方法使用@BindsInstance
public class GameMode {@Injectpublic GameMode(String gameName){}}
存在一个GameMode类,构造函数需要一个gameName对象。
@Componentpublic interface GameComponent {GameMode getGameMode();@Component.Builderinterface Builder{@BindsInstance Builder gameModeName(String str);GameComponent build();}}
在GameComponent中,使用@Component.Builder定义一个接口,接口首先需要一个返回Component的方法。然后使用@BindsInstance 注解一个参数为String的方法。
GameComponent component = DaggerGameComponent.builder().gameModeName("hard").build();
component.getGameMode();
通过如上方式使用。这里我们在构建GameComponent的时候手动注入了字符串"hard"。相当于在Module中@Provides了这个字符串。
注意在调用build()
创建 Component 之前,所有@BindsInstance
方法必须先调用。如果@BindsInstance
方法的参数可能为 null,需要再用@Nullable
标记,同时标注 Inject 的地方也需要用@Nullable
标记。这时 Builder 也可以不调用@BindsInstance
方法,这样 Component 会默认设置 instance 为 null。
使用@Nullable的方法
public class GameMode {@Injectpublic GameMode(@Nullable String gameName) {}}@BindsInstanceBuilder gameModeName(@Nullable String str);
进阶进阶!!
加入GameMode的构造函数需要两个String怎么办?使用@BindInstance提供两个参数为String的方法,但是dagger如何选择用哪个方法注入到构造函数中?我们想到了@Qualifier
之前介绍的Qualifier是使用不同的参数名来区分的。其实直接使用不同的@Qualifier也能够做到同样的事情。
比如我定了两个Qualifier,然后给构造函数做上标记!
@BindsInstanceBuilder gameModeName(@Nullable String str);public class GameMode {@Injectpublic GameMode(@GameModeName String gameName, @PlayerName String playerName) {}}
在component中同样使用标记
@Componentpublic interface GameComponent {GameMode getGameMode();@Component.Builderinterface Builder {@BindsInstanceBuilder gameModeName(@GameModeName String str);@BindsInstanceBuilder playerName(@PlayerName String str);GameComponent build();}}
这样就能区分到底调用哪个参数了。
注:Subcomponent同样提供了 Subcomponet.Builder来完成同样的事情。
进阶进阶!!
关于 在上一篇@Scope中我们讲到了一个关于作用域的例子,我们复用它。
@ActivityScope
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {ClassApplication getClassApplication();ClassActivity getClassActivity();ClassUser getClassUser();@Subcomponent.Builderinterface ActivityBuilder {@BindsInstanceActivityBuilder activity(Activity activity);ActivityComponent build();}
}
我们修改ActivityComponent,要求手动注入一个activity变量(在android中很正常,context还是需要的)。
这个时候如果我们在UserComponent中还是
ActivityComponent getActivityComponent(ActivityModule activityModule);
会出现错误,因为这样我们没有注入activity! 怎么办?
@UserScope@Subcomponent(modules = UserModule.class)public interface UserComponent {//ActivityComponent getActivityComponent(ActivityModule activityModule);ActivityComponent.ActivityBuilder newActivityComponentBuilder();........}
改成返回Buidler,然后开发者自己在Builder上调用activity注入activity,然后调用builder构建component这就没问题了。
// ActivityComponent activityComponent = mUserComponent.
// newActivityComponentBuilder().activity(XXX).build();
// activityComponent.getClassApplication();
// activityComponent.getClassUser();
// activityComponent.getClassActivity();
像这样生成。
Set注入
之前介绍的内容都是单个对象的注入,那么我们是否能将多个对象注入到容器中呢?首先是Set
@Modulepublic class GameModule {@Provides@IntoSetpublic Player getPlayer1() {return new Player("1");}@Provides@IntoSetpublic Player getPlayer2() {return new Player("1");}}
我在module中使用@Provides提供了Player,不同的是,添加了@IntoSet的注解,表示我会把这个注入到Set中。
然后在被注入的单位中请求注入,比如
@Inject
Set<Player> mPlayers;
不需要在component中做其他修改,就能将player1和player2注入到mPlayers中。
注:如果存在多个Set,类型相同,就需要使用@Qualifier。
进阶!我们可以同时向Set注入多个对象
@Provides@ElementsIntoSetpublic Set<Player> getPlayers() {HashSet set = new HashSet<>();set.add(new Player("3"));set.add(new Player("4"));return set;}
使用@ElementsIntoSet 并且返回Set<Player>对象。
Map注入
map的注入和Set有些区别,因为他需要提供key
@Modulepublic class GameModule {@Provides@IntoMap@StringKey(value = "1")public Player getPlayer1() {return new Player("1");}@Provides@IntoMap@StringKey(value = "2")public Player getPlayer2() {return new Player("2");}}
@IntoSet 变成了@IntoMap,并且使用@StringKey注解提供了key值。
PS:这里的key值是String类型,所以能够注入到 Map<String,Player> 对象中。
dagger还提供了一些内置的key类型,包裹classKey,IntKey等,android辅助包中也提供了ActivityKey等。
@MapKey
StringKey的源码,StringKey的value类型为String,应该是指定了Key的数据类型为String。而StringKey又被@MapKey注解,是不是表明该注解是Map的Key的注解呢?不妨试一下,我们自定义一个AppleKey:
@Documented@Target(METHOD)@Retention(RUNTIME)@MapKeypublic @interface AppleKey {AppleBean value();}
但是编译会报错哦!!
注释类型中声明的方法的返回类型,如果不满足指定的返回类型,那么编译时会报错:
- 基本数据类型
- String
- Class
- 枚举类型
- 注解类型
- 以上数据类型的数组
但是还可以指定Enum类型,或者特定的类的。
enum MyEnum {ABC, DEF;}@MapKey@interface MyEnumKey {MyEnum value();}@MapKey@interface MyNumberClassKey {Class<? extends Number> value();}@Moduleclass MyModule {@Provides@IntoMap@MyEnumKey(MyEnum.ABC)static String provideABCValue() {return "value for ABC";}@Provides@IntoMap@MyNumberClassKey(BigDecimal.class)static String provideBigDecimalValue() {return "value for BigDecimal";}}
进阶进阶!!!!
使用复合键值,这个厉害了,因为map的key又不能多个,如何复合键值?
@MapKey(unwrapValue = false)@Retention(value=RUNTIME)public @interface GameInfo {String name();float price();}@Modulepublic class GameModule {@Provides@IntoMap@GameInfo(name = "game",price = 100f)public String getGameInfo(){return "gameinfo";}}
然后被注入对象请求注入
@Inject
Map<GameInfo,String> mGameInfoStringMap;
看一下键类型,这个很关键,竟然是一个GameInfo类型的。
如果你这么做了,并且编译失败了,请不要惊讶,因为你还缺少一些依赖库:
compile 'com.google.auto.value:auto-value:1.5.1'
provided 'javax.annotation:jsr250-api:1.0'
具体原因会在研习篇分析源码的时候分析。
@Binds
假设存在如下场景
public interface HomePresenter {}public class HomePresenterImpl implements HomePresenter{public HomePresenterImpl(UserService userService){}}
非常常见,我们在使用的时候一般会使用接口类型,而不是使用实现类型。
@Inject
HomePresenter mHomePresenter;
所以我们需要在Module中provides
@Providespublic HomePresenter getHomePresenter(UserService userService){return new HomePresenterImpl(userService);}@Provides UserService getUserService(){return new UserService();}
实际上注入的是一个HomePresenterImpl没有问题。
但是,关于这类注入子类的问题,还有一种解决方案,就是使用@binds
@Modulepublic abstract class BindsDataModule {@Bindspublic abstract HomePresenter bindHomePresenter(HomePresenterImpl homePresenterImpl);}
module类改为abstract类型的,使用@Binds注解一个返回为HomePresenter的方法,并且需要想要注入的实现类的类型参数。
然后需要在HomePresenterImple和UserService中使用@Inject来注解构造函数。
@Binds的使用就是如此简单,那么它到底有什么意义呢?相比原来的@Provides有什么优势呢?
仔细想想我们在最初介绍dagger的几个变种中,有一种情况是不提供provides方法,而是在构造函数上添加@Inject的方法。其实这种方法有利于书写,方便快捷的优势,但是当遇上如上这种情况,我们需要注入的是一个接口的时候,我们无法使用@Inject注解构造函数的方法来提供一个接口(我们可以把@Inject添加到实现类中,但是在注入时,dagger并不知道你想要注入的是哪个实现。)
所以@Binds就这样诞生了,可以想象的是,@Binds的作用其实就是告诉dagger,当请求注入一个接口时,我们使用某个实现类何其绑定,用实现类来进行注入。
Dagger 2 基本用法相关推荐
- 单元测试以及dagger的使用
最近对测试比较敏感,因为涉及了代码重构,虽然我是很有自信的一个人,但是过度严谨的强迫症很多时候让我很尴尬. 所以还是希望面对源代码有一定的测试代码,以方便验证代码重构之后的正确与否. 所以,游戏开始了 ...
- Android 依赖注入可以更简单 —— 新版本 Dagger 2 使用教学
今年 3 月 21 号 Dagger 2 在 2.10 版本之后针对 Android 方面做了很大的优化,使用方法也随之有了不少变化.本次改动除了让 Dagger 2 的使用更加符合控制反转原则,还针 ...
- 依赖注入利器 - Dagger ‡
转载请标明出处:http://blog.csdn.net/shensky711/article/details/53715960 本文出自: [HansChen的博客] 概述 声明需要注入的对象 如何 ...
- dagger2 注入_如何使用Dagger 2在您的应用程序中实现依赖注入
dagger2 注入 Kriptofolio应用程序系列-第4部分 (Kriptofolio app series - Part 4) Dependency injection will signif ...
- android注解Dagger2,Dagger 2 在 Android 上的使用(二)
本文介绍了Dagger 2 中@Inject.@Component.@Module和@Provides以及@Module和@Binds的使用. <七步诗> 煮豆燃豆萁,豆在釜中泣. 本是同 ...
- Dagger学习 -- 基础概念
目标 了解 Dagger 基本概念 官方定义 Google 对 Dagger 的定义如下: Dagger is a fully static, compile-time dependency inje ...
- dagger android,在Android开发中使用Dagger2的方法
在Android开发中使用Dagger2的方法 发布时间:2020-12-08 17:12:38 来源:亿速云 阅读:236 作者:Leah 在Android开发中使用Dagger2的方法?相信很多没 ...
- Dagger的使用一
文章目录 一.前言 二.添加依赖 三.简单示例 四.作用域 五.自定义作用域 六.@Binds 和 @Provides 七.关于Dagger为什么在Android中要比Hilt复杂 八.inject( ...
- Koin--适用于Kotlin的超好用依赖注入框架,Dagger替代者,Koin史上最详细解说,一篇就够了,妈妈再也不用担心我不会依赖注入了
今年呆在家中实在无聊,外面太危险了,还是在家学习比较安全可持续. 过年期间,我又复习了几遍依赖注入框架Dagger. 诶,什么是依赖注入? 说白了就是降低跟类对象之间的耦合,当需要修改类对象的时候,能 ...
最新文章
- Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy
- 前端开发需要了解的JS插件
- Python基础:第一个Python程序(2)
- 如何在友好的情景下向用户索取手机权限?
- ad09机械层说明_悉数PCB上的各种层
- 河马php一句话木马,一句话木马的套路
- wps如何自己制作流程图_自己如何制作APP软件
- extmail从数据库导出通讯录
- python定时下载FTP指定文件
- 虚拟机不能上网以及无法ping通百度的解决方案
- 说明使用tc编程的一般步骤 c语言,TC编程手册详解-完整版.doc
- 微信小程序-如何解决onShareAppMessage转发gif格式图片不展示?【亲测有效】
- egret 使用EUI开发UI界面
- Prometheus资源监控工具配置小结
- 5.深度解密五:网站被百度搜索引擎降权的原因及百度算法汇总
- 开发一个渐进式Web应用程序(PWA)前都需要了解什么?
- 【自用】Vue项目中使用自定义字体样式
- 【操作系统的目标和作用】
- php简易留言板功能,php简单的留言板与回复功能具体实现
- 全国计算机等级考试二级cpp试题,2017年全国计算机二级C++考试试题附答案