• 【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




  • 相关阅读:
    树形结构菜单,递归实现
    基于Vue的日历组件,可以标注重要日子
    关于element-ui级联菜单(城市三级联动菜单)和回显问题
    继承(面试问到)
    vue监听浏览器刷新
    Popover 弹出框,里面的表格点击后关闭弹窗
    el-table表格合并单元格
    对角线
    ElementUI中el-radio再次点击取消选中
    保留文本框换行和空格
  • 原文地址:https://www.cnblogs.com/baiqiantao/p/7545334.html
Copyright © 2020-2023  润新知