• 40、Android--Dagger2


    Dagger2

    Dagger2是一款基于 Java 注解来实现的完全在编译阶段完成依赖注入的开源库。Dagger2 应于 Java 和 Android 开发而不单单是 Android,主要用于模块间解耦、提高代码的健壮性和可维护性。

    依赖引入

    在app/build.gradle中的dependencies{ }加入以下代码同步即可:

    implementation 'com.google.dagger:dagger:2.23.1'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.23.1'
    

    三大组成部分

    dagger2三个重要组成部分为:module、component,、container,三者的关系如下图所示:

    由于Dagger2主要是编译期对注解内容进行获取,所以这三个组成部分也有相应的注解符号。

    Container

    @Inject : 用于标记需要注入的依赖。

    Module

    @Module : 用该注解修饰的类,里面主要提供该依赖对象的实例化方法,方法名一般为provide + 对象名。

    @Provides : 用这个来修饰@Module注解类中的方法,表明要提供的实例化对象

    Componet

    @Componet : 可以理解为注入器,它会把目标类依赖的实例注入到目标类中。。

    在构建好注解之后,项目需要rebuild一下,整个依赖注入的过程就算完成。

    Dagger2基本使用

    @Inject和@Component

    1、首先编写Person类,使用@Inject注解在构造函数上,用于标记需要注入的依赖。如下所示:

    public class Person {
        private static final String TAG = "Person";
        @Inject
        public Person() {
        }
    
        public void eat(){
            Log.e(TAG, "eat....");
        }
    }
    

    2、接下来用@Component注解来完成依赖注入。我们需要定义一个接口,接口命名建议为:目标类+Component,在编译后Dagger2就会为我们生成名为Dagger+目标类名+Component的辅助类。具体代码如下所示:

    @Component
    public interface MainActivityComponent {
        void inject(MainActivity activity);
    }
    

    Component则可以理解为注入器,它会把目标类依赖的实例注入到目标类中。在这里需要定义inject方法,传入需要注入依赖的目标类。

    3、在MainActivity中使用@Inject构建Person对象

    public class MainActivity extends AppCompatActivity {
        @Inject
        Person person;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            DaggerMainActivityComponent.create().inject(this);
            person.eat();
        }
    }
    

    @Module和@Provides

    如果项目中使用了第三方的类库,可以采用@Module和@Provides来处理。我们创建GsonModule类,如下所示:

    @Module
    public class GsonModule {
        @Provides
        public Gson provideGson(){
            return new Gson();
        }
    }
    

    @Module标注在类上,用来告诉Component,可以从这个类中获取依赖对象,也就是Gson类。

    @Provodes 标记在方法上,表示可以通过这个方法来获取依赖对象的实例。

    @Module标注的类其实就是一个工厂,用来生成各种类;@Provodes标记的方法,就是用来生成这些类的实例的。接着来编写Component类,如下所示:

    @Component(modules = GsonModule.class)
    public interface MainActivityComponent {
        void inject(MainActivity activity);
    }
    

    这和此前的区别就是加上了modules=GsonModule.class,用来指定Module。需要注意的是,Component 中可以指定多个Module。接下来在MainActivity中使用Gson,如下所示:

    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        @Inject
        Gson gson;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            DaggerMainActivityComponent.create().inject(this);
            User user = new User("Legend", 31);
            String json = gson.toJson(user);
            Log.e(TAG, json);
        }
    }
    

    其中,User类就是一个包含name和age的JavaBean,非常简单。

    还有一种情况,当我们使用依赖注入的时候,如果需要注入的对象是抽象的,则@Inject也无法使用,因为抽象的类并不能实例化。这时也可以采用@Module和@Provides。

    1、定义一个Engine抽象类

    public abstract class Engine {
        public abstract void work();
    }
    

    2、接着定义它的实现类GasolineEngine:

    public class GasolineEngine extends Engine{
        @Override
        public String work() {
            return "汽油发动机发动";
        }
    }
    

    3、随后在Car类中引用Engine,如下所示:

    public class Car {
        private Engine engine;
        @Inject
        public Car(Engine engine) {
            this.engine = engine;
        }
    
        public String run(){
            return engine.work();
        }
    }
    

    4、创建EngineModule类,如下所示:

    @Module
    public class EngineModule {
        @Provides
        public Engine provideEngine(){
            return new GasolineEngine();
        }
    }
    

    5、接着在Component中指定EngineModule:

    @Component(modules = EngineModule.class)
    public interface MainActivityComponent {
        void inject(MainActivity activity);
    }
    

    6、最后在MainActivity中使用,如下所示:

    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        @Inject
        Car car;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            DaggerMainActivityComponent.create().inject(this);
            Log.e(TAG, car.run());
        }
    }
    

    @Named和@Qualifier

    @Qualifier 是限定符,@Named 则是@Qualifier 的一种实现。

    当有两个相同的依赖时,它们都继承同一个父类或者均实现同一个接口。当它们被提供给高层时,Component 就不知

    道我们到底要提供哪一个依赖 对象了,因为它找到了两个。

    比如在上面的汽车例子中,如果我们再提供一个DieselEngine给它, EngineModule可以改写为如下代码所示:

    @Module
    public class EngineModule {
        @Provides
        @Named("Gasoline")
        public Engine provideGasoline(){
            return new GasolineEngine();
        }
    
        @Provides
        @Named("Diesel")
        public Engine provideDiesel(){
            return new DieselEngine();
        }
    }
    

    给不同的Provides定义不同的@Named,接下来在Car类中指定要采用哪种Provides:

    public class Car {
        private Engine engine;
        @Inject
        public Car(@Named("Diesel") Engine engine) {
            this.engine = engine;
        }
    
        public String run(){
            return engine.work();
        }
    }
    

    上面的例子也可以用@Qualifier来 实现,@Named传递的值只能是字符串;而@Qualifier则更灵活一些,@Qualifier不是直接标记在属性上的,而是用来自定义注解的,如下所示:

    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Gasoline {
    }
    
    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Diesel {
    }
    

    分别自定义两个注解@Gasoline和@Diesel,接下来修改EngineModule类,如下所示:

    @Module
    public class EngineModule {
        @Provides
        @Gasoline
        public Engine provideGasoline(){
            return new GasolineEngine();
        }
    
        @Provides
        @Diesel
        public Engine provideDiesel(){
            return new DieselEngine();
        }
    }
    

    最后在Car类指定需要哪种依赖:

    public class Car {
        private Engine engine;
        @Inject
        public Car(@Gasoline Engine engine) {
            this.engine = engine;
        }
    
        public String run(){
            return engine.work();
        }
    }
    

    @Singleton和@Scope

    @Scope是用来自定义注解的,而@Singleton则是用来配合实现局部单例和全局单例的。

    首先在 GsonModule 中添加@Singleton,如下所示:

    @Module
    public class GsonModule {
        @Singleton
        @Provides
        public Gson provideGson(){
            return new Gson();
        }
    }
    

    接下来在MainActivityComponent中添加@Singleton:

    @Singleton
    @Component(modules = GsonModule.class)
    public interface MainActivityComponent {
        void inject(MainActivity activity);
    }
    

    我们在MainActivity中打印Gson的hashCode值,就会发现值是相同的,如下所示:

    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        @Inject
        Gson gson;
        @Inject
        Gson gson1;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            DaggerMainActivityComponent.create().inject(this);
            Log.e(TAG, gson.hashCode() + "--------" + gson1.hashCode());
        }
    }
    

    Gson 在 MainActivity 中是单例,如果在其他Activity中则不再是单例的形式。(局部单例)

    如果想要实现全局单例,就需要保证对应的Component只有一个实例。可以用@Singleton结合Application来实现:

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

    接下来在GsonModule中使用@ApplicationScope:

    @Module
    public class GsonModule {
        @ApplicationScope
        @Provides
        public Gson provideGson(){
            return new Gson();
        }
    }
    

    为了处理多个Activity,我们创建ActivityComponent,并使用@ApplicationScope,如下所示:

    @ApplicationScope
    @Component(modules = GsonModule.class)
    public interface ActivityComponent {
        void inject(MainActivity activity);
        void inject(SecondActivity activity);
    }
    

    创建App类继承自Application,用来提供ActivityComponent实例,如下所示:

    public class App extends Application {
        ActivityComponent activityComponent;
        @Override
        public void onCreate() {
            super.onCreate();
            activityComponent = DaggerActivityComponent.builder().build();
        }
    
        public static App get(Context context){
            return (App) context.getApplicationContext();
        }
    
        public ActivityComponent getActivityComponent() {
            return activityComponent;
        }
    }
    

    最后在MainActivity中实现如下代码:

    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        @Inject
        Gson gson;
        @Inject
        Gson gson1;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            App.get(this).getActivityComponent().inject(this);
            Button btnJump = findViewById(R.id.btn_jump);
            btnJump.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivity(new Intent(MainActivity.this, SecondActivity.class));
                }
            });
        }
    }
    

    SecondActivity中的代码也是类似的,运行程序,发现MainActivity和SecondActivity的Gson的内存地址是一样的。

    @Component的dependencies

    @Component也可以用dependencies依赖于其他Component。我们重新创建一个Swordsman类:

    public class Swordsman {
        @Inject
        public Swordsman() {
        }
    
        public String fighting(){
            return "真是麻烦呀!";
        }
    }
    

    接下来,按照惯例创建SwordsmanModule和SwordsmanComponent,如下所示:

    @Module
    public class SwordsmanModule {
        @Provides
        public Swordsman providerSwordsman(){
            return new Swordsman();
        }
    }
    
    @Component(modules = SwordsmanModule.class)
    public interface SwordsmanComponent {
        Swordsman getSwordsman();
    }
    

    随后在此前定义的App中引入SwordsmanComponent,如下所示:

    public class App extends Application {
        ActivityComponent activityComponent;
        @Override
        public void onCreate() {
            super.onCreate();
            activityComponent = DaggerActivityComponent.builder().build();
            DaggerSwordsmanComponent.builder().build();
    
        }
    
        public static App get(Context context){
            return (App) context.getApplicationContext();
        }
    
        public ActivityComponent getActivityComponent() {
            return activityComponent;
        }
    }
    

    最后我们在SecondActivity中使用Swordsman,如下所示:

    public class SecondActivity extends AppCompatActivity {
        private static final String TAG = "SecondActivity";
        @Inject
        Swordsman swordsman;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            App.get(this).getActivityComponent().inject(this);
            String content = swordsman.fighting();
            Log.e(TAG, "swordsman:" + content);
        }
    }
    

    注意:自定义的全局Application需要在清单文件中定义:

    android:name=".App"
    

    懒加载

    Dagger2提供了懒加载模式,在@Inject的时候不初始化,而是使用的时候,调用get方法来获取实例。 接着我们改写上面提到的Swordsman,将它改写为懒加载模式。

    public class SecondActivity extends AppCompatActivity {
        private static final String TAG = "SecondActivity";
        @Inject
        Lazy<Swordsman> swordsmanLazy;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            App.get(this).getActivityComponent().inject(this);
            Swordsman swordsman = swordsmanLazy.get();
            String content = swordsman.fighting();
            Log.e(TAG, "swordsman:" + content);
        }
    }
    
  • 相关阅读:
    传统工业制造商Opto 22宣布加入Linux基金会
    Android Stack
    An Overview of the Android Architecture (Android Studio)
    基于嵌入式操作系统的物联网安全
    什么是雾计算?它与云计算有什么区别?
    《速度与激情8》中的信息安全技术
    专利驳回的三大基本原因
    专利
    【习题 4-2 Uva201】Squares
    【习题4-1 Uva1589】Xiangqi
  • 原文地址:https://www.cnblogs.com/pengjingya/p/15002458.html
Copyright © 2020-2023  润新知