End

【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全局只有一个实例

基本步骤:
  1. 在Application中实例化AppComponent,保证全局AppComponent只有一个实例
  2. 通过AppComponent管理AppModule,使用@Singleton标注AppComponent以及AppModule中的方法
  3. 在AppComponent中定义需要注入全局类实例的方法
  4. AppModule中定义创建全局类实例的方法
  5. 在需要注入全局类实例的类中,通过全局的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




posted @ 2017-09-18 21:25  白乾涛  阅读(159)  评论(0编辑  收藏  举报