且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Android Dagger2依赖注入

更新时间:2022-05-27 14:15:21

Dagger2项目主页

使用依赖

    annotationProcessor 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'javax.annotation:jsr250-api:1.0'

1. 先创建最基本的四个类

Android Dagger2依赖注入
图1.png

代码:

/**
 * @author mazaiting
 * @date 2018/1/8
 */

public class Config {
    public static final String TAG = "Config";
}

/**
 * 引擎
 * @author mazaiting
 * @date 2018/1/8
 */

public class Engine {
    public Engine(){
        Log.e(Config.TAG, "Engine: new Engine()");
    }
}

/**
 * ***
 * @author mazaiting
 * @date 2018/1/8
 */

public class Wheel {
    public Wheel(){
        Log.e(Config.TAG, "Wheel: new Wheel()");
    }
}

/**
 * 车座
 * @author mazaiting
 * @date 2018/1/8
 */

public class Seat {

    public Seat(){
        Log.e(Config.TAG, "Seat: new Seat()");
    }

}

2. Dagger2是一个依赖注入的框架。

  • 之前我们写代码如下, Car类依赖于其他三个类,并且在构造方法中创建其他三个类的对象,这样,Car类在构造方法中对其他三个类的耦合度较高
/**
 * 车
 * @author mazaiting
 * @date 2018/1/8
 */

public class Car {
    private Engine mEngine;
    private Seat mSeat;
    private Wheel mWheel;

    public Car() {
        mEngine = new Engine();
        mSeat = new Seat();
        mWheel = new Wheel();
        Log.e(Config.TAG, "Car: new Car()");
    }
}

执行Android单元测试:


Android Dagger2依赖注入
图2.png
/**
 * @author mazaiting
 * @date 2018/1/9
 */
public class CarTest {

    @Test
    public void testCar(){
        new Car();
    }
}
  • 使用Dagger2之后,先要创建一个CarModule类,再创建一个CarComponent接口或者抽象类,接下来Ctrl+F9进行编译,如果编译成功,则会生成DaggerCarComponent类(生成的Dagger开头,并且后面为Component组件的接口名/抽象类名),这个类提供注册Module类和注入当前类。


    Android Dagger2依赖注入
    图3.png
/**
 * @Module用来管理并提供依赖
 * @Provides用来提供依赖
 * @author mazaiting
 * @date 2018/1/8
 */
@Module
public class CarModule {
    @Provides
    public Engine provideEngine(){
        return new Engine();
    }

    @Provides
    public Seat provideSeat(){
        return new Seat();
    }

    @Provides
    public Wheel provideWheel(){
        return new Wheel();
    }
    
}


/**
 * 组件
 * @author mazaiting
 * @date 2018/1/8
 */
@Component(modules = CarModule.class)
public interface CarComponent {

    /**
     * 实现注入
     * @param car
     */
    void inject(Car car);
}

/**
 * 车
 * @author mazaiting
 * @date 2018/1/8
 */

public class Car {

    @Inject Engine mEngine;
    @Inject Seat mSeat;
    @Inject Wheel mWheel;

    public Car() {
        DaggerCarComponent
                .builder()
                .carModule(new CarModule())
                .build()
                .inject(this);
        Log.e(Config.TAG, "Car: new Car()");
    }
}


//  Component抽象类实现法
/**
 * @author mazaiting
 * @date 2018/1/9
 */
@Component(modules = CarModule.class)
public abstract class CarComponent {
    /**
     * @param car
     */
    public abstract void inject(Car car);
}

执行上一步的Android单元测试,打印结果


Android Dagger2依赖注入
图4.png

3. 以上用到的注解解释

  • @Module 标注用来管理并提供依赖。里面定义一些用@Provides注解的以provide开头的方法,这些方法就是所提供的依赖,Dagger2会在该类中寻找实例化某个类所需要的依赖。
  • @Provides 标注用来提供依赖
  • @Component 标注接口或者抽象类,用来将@Inject和@Module联系起来的桥梁,从@Module中获取依赖并将依赖注入给@Inject。参数module,用来指定用注解@Module标识的类。
  • @Inject 标注目标类中所依赖的其他类,标注所依赖的其他类的构造函数

4. 多个构造方法

1). 给Engine类添加一个构造方法

/**
 * 引擎
 * @author mazaiting
 * @date 2018/1/8
 */

public class Engine {
    public Engine(){
        Log.e(Config.TAG, "Engine: new Engine()");
    }

    public Engine(String string){
        Log.e(Config.TAG, "Engine: " + string);
    }
}

2). 此时运行之前的Android单元测试,发现它默认调用的是无参数的构造方法


Android Dagger2依赖注入
图5.png

3). 但此时我们就想让它去调用含一个参数的构造方法,我们可以这么做
I>. 自定义一个注解,必须使用@Qualifier修饰器

/**
 * 标识引擎的颜色
 * @author mazaiting
 * @date 2018/1/9
 */
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface EngineString {
    String string() default "white";
}

II>. 在CarModule中添加一个提供Engine的方法,并使用刚刚自定义的注解

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

// ... 省略其他的方法

    @EngineString(string = "black")
    @Provides
    public Engine provideStringEngine(){
        return new Engine("black");
    }

}

III>. 在Car类中,声明Engine的使用,同样也使用刚刚自定义的注解标识

/**
 * 车
 * @author mazaiting
 * @date 2018/1/8
 */

public class Car {
    @EngineString(string = "black")
    @Inject Engine mEngine;
    @Inject Seat mSeat;
    @Inject Wheel mWheel;

    public Car() {
        DaggerCarComponent
                .builder()
                .carModule(new CarModule())
                .build()
                .inject(this);
        Log.e(Config.TAG, "Car: new Car()");
    }
}

IV>. 使用之前的Android单元测试


Android Dagger2依赖注入
图6.png

5. @Named注解

@Named 是基于@Qualifier的注解,不过只能传递字符串
@Qualifier 包括但不限于字符串,如枚举等。

6. Lazy<T>懒加载数据


public class Car {
    @EngineString(string = "black")
    @Inject Engine mEngineBlack;
    @Named(value = "10")
    @Inject Engine mEngine;
    @Inject Seat mSeat;
    @Inject
    Lazy<Wheel> mWheelLazy;

    public Car() {
        DaggerCarComponent
                .builder()
                .carModule(new CarModule())
                .build()
                .inject(this);
        Log.e(Config.TAG, "Car: new Car()");
    }
    // 使用懒加载的变量
    public void useWheel(){
        mWheelLazy.get();
    }
}

新建Android 单元测试

    @Test
    public void testCar1(){
        final Car car = new Car();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e("tag", "useWheel" + Thread.currentThread());
                car.useWheel();
            }
        }).start();
    }

单元测试打印结果:


Android Dagger2依赖注入
图7.png

7. @Singleton 当前提供的对象将是单例模式 ,一般配合@Provides一起出现。

8. 使用示例

AppModule.java

/**
 * @author mazaiting
 * @date 2018/1/9
 */
@Module
public class AppModule {
    Application mApplication;
    public AppModule(Application application){
        this.mApplication = application;
    }

    @Provides
    @Singleton
    Application provideApplication(){
        return this.mApplication;
    }
}

NetModule.java

/**
 * @author mazaiting
 * @date 2018/1/9
 */

@Module
public class NetModule {
    String mBaseUrl;

    // Constructor needs one parameter to instantiate.
    public NetModule(String baseUrl) {
        this.mBaseUrl = baseUrl;
    }

    // Dagger will only look for methods annotated with @Provides
    @Provides
    @Singleton
    // Application reference must come from AppModule.class
    SharedPreferences providesSharedPreferences(Application application) {
        return PreferenceManager.getDefaultSharedPreferences(application);
    }

    @Provides
    @Singleton
    Cache provideOkHttpCache(Application application) {
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        Cache cache = new Cache(application.getCacheDir(), cacheSize);
        return cache;
    }

    @Provides
    @Singleton
    Gson provideGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
        return gsonBuilder.create();
    }

    @Provides @Named("cached")
    @Singleton
    OkHttpClient provideOkHttpClientCache(Cache cache) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.cache(cache);
        return builder.build();
    }

    @Provides @Named("non_cached")
    @Singleton
    OkHttpClient provideOkHttpClient() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        return builder.build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .baseUrl(mBaseUrl)
                .client(okHttpClient)
                .build();
        return retrofit;
    }

}

NetComponent.java

@Singleton
@Component(modules = {AppModule.class, NetModule.class})
public interface NetComponent {
    /**
     * @param activity
     */
    void inject(MainActivity activity);
}

完成前三个类之后编译,生成DaggerNetComponent.java.
MyApp.java

public class MyApp extends Application {
    private NetComponent mNetComponent;
    @Override
    public void onCreate() {
        super.onCreate();

        mNetComponent =
                DaggerNetComponent
                .builder()
                .appModule(new AppModule(this))
                .netModule(new NetModule("http://www.baidu.com/"))
                .build();
    }

    public NetComponent getNetComponent(){
        return mNetComponent;
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Inject @Named("cached")
    OkHttpClient mClient;
    @Inject @Named("non_cached")
    OkHttpClient mOkHttpClient;
    @Inject
    SharedPreferences mSharedPreferences;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

//        new Car();
        ((MyApp)getApplication()).getNetComponent().inject(this);
    }


    // 写数据
    public void write(View view){
        mSharedPreferences.edit().putString("key", "value").apply();
    }

    // 读数据
    public void read(View view){
        Toast.makeText(this, mSharedPreferences.getString("key", "string"), Toast.LENGTH_SHORT).show();
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.mazaiting.dagger2test.MainActivity">

    <Button
        android:onClick="write"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="write"/>


    <Button
        android:onClick="read"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="read"/>
</LinearLayout>