• Dagger2 (二) 进阶篇


    一.作用域Scope

    之前了解RoboGuice的时候,我们知道它默认给我们提供了几个注解,ContextSingleton和Singleton,但是Dagger2更为灵活,只有javax包中提供的Singleton注解。更为强大的是,我们可以自定义作用域。

    首先我们定义一个运行时的注解。

    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PerActivity {
    
    }
    

    我们新增加一个Module,专门为Activity中提供依赖。

    @Module
    public class ActivityUtilModule {
        private Activity activity;
    
        public ActivityUtilModule(Activity activity) {
            this.activity = activity;
        }
    
        @Provides
        public Resources provideResource() {
            return activity.getResources();
        }
    
        @Provides
        @PerActivity
        public Toaster provideToaster() {
            return new Toaster(activity);
        }
    }
    

    然后我们新增一个Component来承载这个Module,注意,这里我用了dependencies,这样Component就可以传递依赖到子Component。

    @PerActivity
    @Component(dependencies = ApplicationComponent.class, modules = ActivityUtilModule.class)
    public interface ActivityComponent {
        void inject(MainActivity activity);
    }
    

    我们修改ApplicationComponent,让其不再直接注入到Activity中,而使用ActivityComponent来注入。

    在MainActivity中我们这样写:

    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
    
        @Inject
        Toaster toaster;
    
        @Inject
        @Named("app")
        Context context;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ActivityComponent activityComponent = DaggerActivityComponent.
                    builder().
                    activityUtilModule(new ActivityUtilModule(this)).
                    applicationComponent(component()).
                    build();
            activityComponent.inject(this);
    
            toaster.longToast(context.getPackageName());
    
        }
    
        protected ApplicationComponent component() {
            return ((DaggerApplication) getApplication()).Component();
        }
    }
    

    运行成功后,会弹出包名。相信到这里,大家还是一头雾水。

    当我们去掉ActivityComponent中的PerActivity注解时,我们就会发现,编译出错了:这里告诉我们没依赖这个注解,就使用了这个模块,这是编译时期就会不通过的。这里也暴露了作用域的作用,约束一个模块的注入器组件。且在这个组件中使用这个Module时,提供的这个Provider是单例。即组件模块内单例。

    Error:(10, 1) 错误: github.pedroneer.dagger2.dagger.ActivityComponent (unscoped) may not reference scoped bindings:
    @Provides @github.pedroneer.dagger2.dagger.PerActivity github.pedroneer.dagger2.Toaster github.pedroneer.dagger2.dagger.ActivityUtilModule.provideToaster()
    

    好,也许到这里还是不懂,先看下这个例子。

    我们将BaseActivity中注入了依赖。

    public class BaseActivity extends FragmentActivity {
    
        @Inject
        Toaster toaster;
    
        @Inject
        @Named("app")
        Context context;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ActivityComponent activityComponent = DaggerActivityComponent.
                    builder().
                    activityUtilModule(new ActivityUtilModule(this)).
                    applicationComponent(component()).
                    build();
            activityComponent.inject(this);
    
            toaster.longToast(context.getPackageName());
        }
    
        private ApplicationComponent component() {
            return ((DaggerApplication) getApplication()).Component();
        }
    }
    

    此外,我们MainActivity和SecondActivity都继承自BaseActivity,这也符合我们平时的做法。

    public class MainActivity extends BaseActivity {
        private static final String TAG = "toaster";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            toaster.longToast(context.getPackageName());
            Log.d(TAG, "onCreate: " + toaster.hashCode());
            Intent intent = new Intent(this, SecondActivity.class);
            startActivity(intent);
        }
    }
    
    public class SecondActivity extends BaseActivity {
        private static final String TAG = "toaster";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            toaster.longToast(context.getPackageName());
            Log.d(TAG, "onCreate: " + toaster.hashCode());
        }
    }
    

    那接下来我们打印的两个hashCode会是同一个么?

    当然不是。

    03-21 00:32:57.706 12716-12716/github.pedroneer.dagger2 D/toaster: onCreate: 531688530
    03-21 00:32:57.866 12716-12716/github.pedroneer.dagger2 D/toaster: onCreate: 204017154
    

    因为我们在BaseActivity中就指定了每次创建时都会创建一个新的ActivityComponent,但ActivityComponent中依赖的ApplicationComponent确是一个,因为我们每次都是从DaggerApplication中拿这个对象注入的。

    也就是说,注解了PerActivity的对象,只在一个模块的组件生命周期内单例。

    再看下面的例子:

    我们提供custom和the两种toaster。

    @Module
    public class ActivityUtilModule {
        private Activity activity;
    
        public ActivityUtilModule(Activity activity) {
            this.activity = activity;
        }
    
        @Provides
        public Resources provideResource() {
            return activity.getResources();
        }
    
        @Provides
        @PerActivity
        @Named("custom")
        public Toaster provideToaster() {
            return new Toaster(activity);
        }
    
        @Provides
        @Named("the")
        public Toaster provideTheToaster() {
            return new Toaster(activity);
        }
    }
    

    并在ActivityComponent中提供依赖。

    @PerActivity
    @Component(dependencies = ApplicationComponent.class, modules = ActivityUtilModule.class)
    public interface ActivityComponent {
        void inject(BaseActivity activity);
    
        @Named("custom")
        Toaster theToaster();
    
        @Named("the")
        Toaster toaster();
    }
    

    最后我们在MainActivity中查看下多次获取这个两个依赖的情况。

    public class MainActivity extends BaseActivity {
        private static final String TAG = "toaster";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Log.d(TAG, "onCreate: " + activityComponent.theToaster().hashCode());
            Log.d(TAG, "onCreate: " + activityComponent.theToaster().hashCode());
            Log.d(TAG, "onCreate: " + activityComponent.toaster().hashCode());
            Log.d(TAG, "onCreate: " + activityComponent.toaster().hashCode());
        }
    }
    
    03-21 00:46:34.836 24014-24014/github.pedroneer.dagger2 D/toaster: onCreate: 1028556617
    03-21 00:46:34.836 24014-24014/github.pedroneer.dagger2 D/toaster: onCreate: 1028556617
    03-21 00:46:34.836 24014-24014/github.pedroneer.dagger2 D/toaster: onCreate: 857606222
    03-21 00:46:34.836 24014-24014/github.pedroneer.dagger2 D/toaster: onCreate: 614825327
    

    答案很明显,我们定义了PerActivity中的custom修饰的Toaster每次都是唯一的,而the修饰的则每次创建一个对象。这就是作用域及无作用域的区别。

    总结一下,当我们为一个组件打上标签作用域,那么这个组件的生命周期内,模块内的依赖也就随着组件的生命周期消亡而消亡,如果模块内存在提供作用域的Provider,而使用这个模块的组件不标注该作用域,则编译报错。Provider中提供的方法在同一模块内每次调用都会初始化,而标明了作用域的则不会。

    二.绑定类型

    这个上面的例子已经用过了,使用方法和RoboGuice相同,Dagger2也增加了Qualifier的概念,即可以自定义注解标示组件提供需要的注入类型。这里不做赘述。

    三.懒加载

    Dagger2支持Lazy Load,即在我们真正使用对象的时候才回去初始化。

    public class MainActivity extends BaseActivity {
    
        @Inject
        @Named("the")
        Lazy<Toaster> toasterLazy;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (something()) {
                toasterLazy.get().longToast("hello");
            }
        }
    
        private boolean something() {
            return new Random().nextBoolean();
        }
    }
    

    这样大大的提高了我们的效率,比如在BaseActivity中注入了多种依赖,如果不希望创建的时候即初始化,可以使用懒加载,只有在使用的时候初始化。

  • 相关阅读:
    CodeForce VKcup A
    CNN卷积神经网络
    神经网络
    我的机器学习之路
    [OPENCV] 第一个程序 识别颜色
    Android 登录界面与首页的设计
    go web的基本原理
    Go语言标准库之http/template
    吞吐量(TPS)、QPS、并发数、响应时间(RT)概念
    数据库恢复技术
  • 原文地址:https://www.cnblogs.com/pedro-neer/p/5307964.html
Copyright © 2020-2023  润新知