【Dagger2】 案例大全
只有Inject是不可以的,必须有Component
public class Test {
@Inject Person person;
private void test() {
System.out.println(person.name);
}
public static void main(String[] args) {
new Test().test();//NullPointerException
}
}
class Person {
public String name;
@Inject
public Person() {
name = "默认的名字";
}
}
可以只有Inject和Component,没有Module
public class Test {
@Inject Person person;
private void test() {
DaggerMainComponent.builder().build().inject(this);//必须有注入的代码,否则根本无法将实例注入到目标类中
System.out.println(person.name);
}
public static void main(String[] args) {
new Test().test();//默认的名字
System.out.println(new Test().person.name);//NullPointerException。必须有注入的代码
}
}
@Component//可以不指定Module。也可以指定Module但不提供相应的方法
interface MainComponent {
void inject(Test obj);
}
class Person {
public String name;
@Inject
public Person() {
name = "默认的名字";
}
}
同时有Inject和Module时,优先使用Module
Component会首先从Module维度中查找类实例,若找到就用Module维度创建类实例,并停止查找Inject维度,否则才是从Inject维度查找类实例。
所以创建类实例级别:Module维度要高于Inject维度。
public class Test {
@Inject Person person;
private void test() {
DaggerMainComponent.builder().mainModule(new MainModule("白乾涛")).build().inject(this);
System.out.println(person.name);
}
public static void main(String[] args) {
new Test().test();//白乾涛
}
}
@Component(modules = MainModule.class)//指定Module
interface MainComponent {
void inject(Test obj);
}
@Module
class MainModule {
private String name;
public MainModule(String name) {
this.name = name;
}
@Provides
Person providerPerson() {
return new Person(name);//调用的是这里
}
}
class Person {
public String name;
@Inject
public Person() {
name = "默认的名字";//优先使用Module,所以不会调用这里
}
public Person(String name) {
this.name = name;
}
}
递归注解_1_在Module中构造对象时依赖另一个参数
public class Test {
@Inject Person person;//8
private void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);//1
System.out.println(person.name);//9
}
public static void main(String[] args) {
new Test().test();//包青天
}
}
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test obj);
void injectName(MainModule obj);
}
@Module
class MainModule {
@Inject String name;//5
@Provides
Person providerPerson() {//2
DaggerMainComponent.builder().mainModule(new MainModule()).build().injectName(this);//3
return new Person(name);//6
}
@Provides
String providerName() {//4
return "包青天";
}
}
//********************************************等价于下面这种形式,更简洁**********************************************
@Module
class MainModule {
@Provides
Person providerPerson(String name) {//2,5
return new Person(name);//6
}
@Provides
String providerName() {//3
System.out.println("即使后面用不到providerPerson方法中的形参name,也会走到这个方法里");
return "包青天";//4
}
}
class Person {
public String name;
@Inject
public Person() {
name = "默认的名字";//优先使用Module,所以不会调用这里
}
public Person(String name) {//7
this.name = name;
}
}
递归注解_2_在构造方法中构造对象时依赖另一个参数
这个例子不要看,简直不能再乱了!
public class Test {
@Inject Person person;//6
private void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);//1
System.out.println(person.name);//7
}
public static void main(String[] args) {
new Test().test();//包青天
}
}
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test obj);
void injectName(Person obj);
}
@Module
class MainModule {
@Provides
String providerName() {//4
return "包青天";
}
}
class Person {
@Inject public String name;//5
@Inject
public Person() {//2
DaggerMainComponent.builder().mainModule(new MainModule()).build().injectName(this);//3
}
}
递归注解_3_用@Inject注解标注有参构造方法
public class Test {
@Inject Person person;//6
private void test() {
System.out.println("C");
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);//1
System.out.println("F");
System.out.println(person.name);//7
}
public static void main(String[] args) {
System.out.println("A");
Test test = new Test();
System.out.println("B");
test.test();//包青天
}
}
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test obj);
}
@Module
class MainModule {
@Provides
String providerName() {//3
System.out.println("D");//先走这里*************************************
return "包青天";
}
}
class Person {
public String name;//5
@Inject
public Person(String name) {//2
System.out.println("E");//再走这里*************************************
this.name = name;//4
}
}
Qualifier和Named注解,区分用哪个方法创建对象
若一个类的实例有多种方法可以创建出来,那Component应该选择哪种方法来创建该类的实例呢?
@Qualifier和Named注解就是解决依赖注入迷失问题的。dagger2在发现依赖注入迷失时,在编译代码时会报错。
注意:我发现不可以用Qualifier和Named标注构造方法,会报如下错误。
Error:(41, 1) Gradle: 错误: @Qualifier annotations are not allowed on @Inject constructors.
public class Test {
@Inject Person person;
@Inject @MyType(1) Person person1;//将所有@MyType(1)改为@Named("1")也是相同的效果
@Inject @MyType(2) Person person2;//必须能在Module中找到相应的提供对象的方法,否则报错。
private void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);
System.out.println(person.name + " " + person1.name + " " + person2.name);//默认的名字 普通人 中国人
}
public static void main(String[] args) {
new Test().test();
}
}
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test obj);
}
@Module
class MainModule {
@Provides
Person providerNormalPerson() {
return new Person();
}
@MyType(1)
@Provides
Person providerPerson() {
return new Person("普通人");
}
@MyType(2)
@Provides
Person providerChinesePerson() {
return new Person("中国人");
}
}
class Person {
public String name;
public Person() {
name = "默认的名字";
}
public Person(String name) {
this.name = name;
}
}
@Qualifier
@interface MyType {//自定义一个限定符
int value();
}
Component匹配多个Module,Named注解的使用
public class Test {
@Inject Person person;//会去Component指定的所有Module中查找提供Person的方法,但是会忽略Named等限定不一样的方法
@Named("2") @Inject Person person2;
private void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);
System.out.println(person.name+" "+person2.name);
}
public static void main(String[] args) {
new Test().test();//来自Module 来自Module2
}
}
@Component(modules = {MainModule.class, MainModule2.class})//Component匹配多个Module
interface MainComponent {
void inject(Test obj);
}
@Module
class MainModule {
@Provides
Person providerPerson() {
return new Person("来自Module");
}
}
@Module
class MainModule2 {
@Named("2")//如果Component指定的多个Module中具有方法声明完全相同的两个方法,会编译失败。此时可以通过使用Named限定来解决。
@Provides
Person providerPerson() {//由于MainModule2中的此方法的声明和MainModule中的完全一样,所以不能放在同一个Module中
return new Person("来自Module2");
}
}
class Person {
public String name;
public Person(String name) {
this.name = name;
}
}
Module之间相互包含,演示include
@Component(modules = {MainModule.class})//依赖一个Module
interface MainComponent {
void inject(Test obj);
}
@Module(includes = {APPModule.class})//依赖其他Module
class MainModule {
@Provides
Person providerPerson(String name) {//这里的name由其includes的APPModule提供
return new Person(name);
}
}
//**********************************************和下面的效果完全相同********************************************
@Component(modules = {MainModule.class, APPModule.class})//直接依赖多个Module
interface MainComponent {
void inject(Test obj);
}
@Module
class MainModule {
@Provides
Person providerPerson(String name) {//这里的name由其includes的APPModule提供
return new Person(name);
}
}
@Module
class APPModule {
@Provides
String providerName() {
return "包青天";
}
}
依赖另一个Component,演示dependencies
经测试发现,使用继承关系,如【interface MainComponent extends AppComponent】除了原始的继承外(扩展了方法),没有任何额外的意义。也即MainComponent不会额外dependencies AppComponent,MainComponent的modules也不会额外添加AppComponent的modules。
public class Test {
@Inject Person person;
private void test() {
AppComponent appComponent = DaggerAppComponent.builder().build();
DaggerMainComponent.builder()
.appComponent(appComponent)//关键步骤一,不添加运行时报错
.build().inject(this);
System.out.println(person.name);
}
public static void main(String[] args) {
new Test().test();//包青天
}
}
@Component(dependencies = {AppComponent.class})//依赖另一个Component
interface MainComponent {
void inject(Test obj);
}
@Component(modules = {APPModule.class})
interface AppComponent {
Person getAPerson();//关键步骤二
//【被依赖的Component】必须提供【依赖方Component】需要的对象(因为依赖方缺少相应的Provides方法),如果不提供编译失败。方法名随意
}
@Module
class APPModule {
@Provides
Person providerPerson() {
return new Person("包青天");
}
}
Scope和Singleton:基于Component实例的单例模式
注意:以下演示效果中,使用"用Scope标注的"自定义注解MyScope,和使用Singleton注解的效果完全一致。事实上,Singleton和网上流传的PerActivity等之类的玩意的效果完全一样,其唯一的区别就是名字不一样。
注意:如果不使用Scope标注自定义注解MyScope,则所有返回的对象都是不同的对象。
结论:使用Scope或Singleton注解后,基于同一Component的实例可以具有单例效果;但是,要想保持为全局单例,就必须保证Component实例为全局单例。
public class Test {
public static void main(String[] args) {
new Test1().test();
new Test2().test();
}
}
class Test1 {
@Inject Person person, person1;
public void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);
System.out.println((person == person1) + " " + person.toString() + " " + person1.toString());
//true com.bqt.dagger.Person@29453f44 com.bqt.dagger.Person@29453f44
}
}
class Test2 {
@Inject Person person, person1;
public void test() {
DaggerMainComponent2.builder().mainModule(new MainModule()).build().inject(this);
System.out.println((person == person1) + " " + person.toString() + " " + person1.toString());
//true com.bqt.dagger.Person@12a3a380 com.bqt.dagger.Person@12a3a380
}
}
@Module
class MainModule {
@MyScope//如果只在这里添加@Singleton(任何用Scope标注的注解)注解,编译失败!
@Provides
Person providerPerson() {
return new Person();
}
}
@MyScope//如果只在这里添加@Singleton注解任何用Scope标注的注解),没有任何效果。
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test1 obj);
}
@MyScope//如果只在这里添加@Singleton注解任何用Scope标注的注解),没有任何效果。
@Component(modules = MainModule.class)
interface MainComponent2 {
void inject(Test2 obj);
}
//************************************也可以使用同一个Component,效果和上面使用两个时完全一样**************************************
@MyScope
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test1 obj);
void inject(Test2 obj);
}
@Scope
@interface MyScope {
}
class Person {
public String name;
public Person() {
name = "默认的名字";
}
}
全局单例模式:保证Component全局只有一个实例
基本步骤:
- 在Application中实例化AppComponent,保证全局AppComponent只有一个实例
- 通过AppComponent管理AppModule,使用@Singleton标注AppComponent以及AppModule中的方法
- 在AppComponent中定义需要注入全局类实例的方法
- 在AppModule中定义创建全局类实例的方法
- 在需要注入全局类实例的类中,通过全局的AppComponent实例将全局类实例注入到此类中
public class App extends Application {
public static App mApp;
private AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
mApp = this;
//1、在Application中实例化AppComponent,保证全局AppComponent只有一个实例
mAppComponent = DaggerAppComponent.builder().appModule(new AppModule()).build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
}
@Singleton//2、使用@Singleton标注AppComponent
@Component(modules = AppModule.class)//2、通过AppComponent管理AppModule
interface AppComponent {
void injectPerson(GZ obj);//3、在AppComponent中定义需要注入全局类实例的方法
void injectPerson(SZ obj);
//void injectPerson(Object obj);//不能使用Object来代替GZ或SZ
}
@Module
class AppModule {
@Provides
@Singleton//2、使用@Singleton标注AppModule中的方法
public Person providePerson() {
return new Person("包青天");//4、在AppModule中定义创建全局类实例的方法
}
}
class Person {
public String name;
@Singleton//这里的Singleton是没有意义的,但是加上去可以方便理解这个类的用途
public Person(String name) {
this.name = name;
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("bqt", "【是否为同一对象】" + (new GZ().person == new SZ().person));//true
}
}
class GZ {
@Inject public Person person;
public GZ() {
//5、在需要注入全局类实例的类中,通过全局的AppComponent实例将全局类实例注入到此类中
App.mApp.getAppComponent().injectPerson(this);
Log.i("bqt", "【GZ】" + person.toString());//【GZ】com.bqt.dagger.Person@e70c3b5
}
}
class SZ {
@Inject public Person person;
public SZ() {
App.mApp.getAppComponent().injectPerson(this);
Log.i("bqt", "【SZ】" + person.toString());//【SZ】com.bqt.dagger.Person@e70c3b5
}
}
一个MVP架构下完整的Dagger2案例
PS:以下案例在项目中可以优化,比如一个界面应该用一个Component,比如如果不需要Model,Component可以不依赖任何Model。
在MVP架构中,最常见的依赖关系,就是Activity持有presenter的引用,并在Activity中实例化这个presenter,即Activity依赖presenter;而同时,presenter又需要依赖View接口,从而更新UI;同样,presenter和Model之间也需要相互依赖。这样一来,虽然,Activity和Model之间完全解耦了,但Activity与presenter、presenter和Model之间却紧紧耦合在了一起。
V:Activity接口及Activity
public interface IMainView {
void showToast(String src);
}
public class MainActivity extends AppCompatActivity implements IMainView {
@Inject IMainPresenter mainPresenter;//注意:如果是通过Module中@Provides注解标注的方法来生成对象,这里可以声明为IMainPresenter
// 否则,必须声明为MainPresenter,因为此时框架是去查MainPresenter中使用@Inject标注的构造方法,而不是接口中的***
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
.mainModule(new MainModule(this, "白乾涛"))
.build()
.inject(this);
mainPresenter.login("123");
}
@Override
public void showToast(String src) {
Toast.makeText(this, src, Toast.LENGTH_SHORT).show();
}
}
P:Presenter接口及Presenter
public interface IMainPresenter {
void login(String password);
}
public class MainPresenter implements IMainPresenter {
private IMainView mainView;
private String name;
@Inject MainModel mainModel;//注意:如果是通过Module中@Provides注解标注的方法来生成对象,这里可以声明为IMainModel
// 否则,必须声明为MainModel,因为此时框架是去查MainModel中使用@Inject标注的构造方法,而不是接口中的***
public MainPresenter(IMainView mainView, String name) {
this.mainView = mainView;
this.name = name;
Log.i("bqt", "【构造MainPresenter】");
DaggerMainModelComponent.builder()
.mainModelModule(new MainModelModule())
.build()
.inject(this);
}
@Override
public void login(String password) {
String info = mainModel.login(name, password);
if (mainView != null) mainView.showToast(info);
Log.i("bqt", info);
}
}
MainModule 和 MainComponent
@Module
public class MainModule {
private IMainView mainView;
private String name;
public MainModule(IMainView mainView, String name) {
this.mainView = mainView;
this.name = name;
Log.i("bqt", "【构造MainModule】");
}
@Provides
IMainPresenter provideMainPresenter() {
return new MainPresenter(mainView, name);
}
}
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);//这里必须指定要注入到哪个类里面,参数声明必须是MainActivity而不能是IMainView
}
M:Model相关的4个类
我把这些东西全部放在了MainPresenter类里面,不然文件膨胀太严重了!
//*******************************************以下是MVP中M相关的类***********************************************
interface IMainModel {//在这个案例中,抽象出的M接口完全没有存在的价值了
String login(String name, String password);
}
class MainModel implements IMainModel {
@Override
public String login(String name, String password) {
return (password == null || password.equals("")) ? "请登录" : "登录成功,你的名字为:" + name;
}
@Inject
public MainModel() {
Log.i("bqt", "【构造MainModel】");
}
}
@Component(modules = MainModelModule.class)
interface MainModelComponent {
void inject(MainPresenter mainPresenter);
}
@Module
class MainModelModule {
}
额,本来一个类可以搞定的事,现在一下子膨胀到10个类了 ^_^ O(∩_∩)O \(^o^)/~
2017-9-18
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/7545334.html