dagger的使用

189 阅读10分钟
dagger的使用注意事项

1、component的inject方法接受父类型参数,而调入时传入的是子类型对象则无法注入

2、component关联的modules中不能有重复的provide

3、module的peovide方法使用了scope,那么component必须使用同一个注解

4、module的peovide方法没有使用了scope,那么component和module是否加注解都无关紧要 5、component的dependencies与自身的scope不能相同

6、SingleTon的组件不能依赖其他的scope组件,只能其他scope的组件依赖Single的组件

7、没有scope的component不能依赖有scope的component

8、一个component不能同时有多个scope,Subcomponent除外

9、@Singleton的生命周期依附于component。要想保证真正的单例,需要在application中进行初始化

10、Subcomponent同时具备两种不同生命周期的scope,具备父component的,也具备自己的

11、Subcomponent的scope范围小于父Component

注解
@Inject

作用:

1:标注哪些东西需要注入

2:标注这些东西怎么创建

既可以作用在对象上,也可以作用在构造函数上,比如:

 @Inject
 UserManager userManager;
 
 @Inject
 public ApiService(OkHttpClient okHttpClient,String url){
        this.url = url;
        this.mOkHttpClient = okHttpClient;
        Log.e("zzf","------------ApiService的有参构造函数-----------");
    }
@Component

作用:相当于一个桥梁,连接Module和需要注入的对象(可以理解为Module和Inject)

/**
 * Component是连接module和inject的中间人
 * @Component(modules = {UserModule.class})表示关联到module
 * void inject(MainActivity mainActivity);表示与activity联系起来,因为在activity里需要注入对象
 */
@Component(modules = {UserModule.class},dependencies = {HttpComponet.class})
public interface UserComponet {
    void inject(MainActivity mainActivity);
}
@Module

作用:提供创建实例的方法。

一个 Module 可以被多个 Component 引用。

    //需要添加@Provides注解
    @Provides
    public UserManager getUserManager(ApiService apiService,UserStore userStore){
        return new UserManager(apiService,userStore);
    }
@Provides

作用:为第三方库不能修改类代码的时候,加上@Provides注解来解决

    @Provides
    public OkHttpClient getOkHttpClient(){
        return new OkHttpClient().newBuilder().build();
    }
@Named

作用:两个方法返回类型都是提供同一类型的依赖的时候,用 该注解进行区分。

    @Named("dev")
    @Inject
    HttpService apiService_dev;


    @Named("release")
    @Inject
    HttpService apiService_release;
@Qualifier

作用:用来自定义的类似@Named注解的注解

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Dev {

    /** The name. */
    String value() default "";
}
@Singleton

作用:实现单例的效果

只能在同一个activity里面保证单例,在不同的acticvity中不能保证。想要保证在所有的activity里面实现单例,可以提升为application级别,在application进行初始化。

@Module
public class HttpModule {

    /**
     * @Singleton 表示单例的注解
     * 但是它有一个缺点,只能在同一个activity里面保证单例,在不同的actiovity不能保持单例
     * 解决办法:
     *
     * 在Application的onCreate方法中进行Componet的初始化,这样就能保证application级别的范围
     * @return
     */
    @Singleton
    @Provides
    public OkHttpClient getOkHttpClient(){
        return new OkHttpClient().newBuilder().build();
    }

}

@Singleton
@Component(modules = {HttpModule.class})
public interface HttpComponet {
    OkHttpClient getOkHttpClient();
}

public class App extends Application {

    private static HttpComponet httpComponet;

    public static HttpComponet getHttpComponet(){
        return httpComponet;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        httpComponet = DaggerHttpComponet
                .builder()
                .httpModule(new HttpModule())
                .build();
    }
}

@Scope

作用:用来自定义的类似@Singleton注解的注解

@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {
}

具体使用
没有参数的对象

首先来看该类。

public class HttpService {

    public void register(){
        Log.e("zzf","-------HttpService-------register---------");
    }
}

该类没有其他的构造函数,只有一个默认的空构造函数。

先创建一个UserModule

@Module
public class UserModule {

    @Provides
    public HttpService getHttpService(){
        return new HttpService();
    }

}

获取到HttpService的对象有两种实现方式。

第一种就是上面这种,在moudle里面提供一个方法。

第二种就是直接在HttpService类的构造方法中添加上@Inject注解,即:

    @Inject
    public HttpService(){
        
    }

然后创建一个Component

@Component(modules = {UserModule.class})
public interface UserComponet {
    void inject(MainActivity mainActivity);
}

然后在activity里面初始化component.

public class MainActivity extends AppCompatActivity {

    /**
     * 这个标识需要注入的对象
     */
    @Inject
    HttpService apiService_dev;

    private boolean is_dev = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /**
         * 下面的两种方式都可以,
         * 第一种:
         * DaggerUserComponet.create().inject(this);
         *
         * 第二种的主要是为了提供有参数的方法
         * 比如:
         * public UserModule(Context context){
         *         mContext = context;
         *     }
         * 这种就需要第二种方式
         */
        //DaggerUserComponet.create().inject(this);
        DaggerUserComponet.builder()
                .httpComponet(App.getHttpComponet())
                .userModule(new UserModule(this))
                //.httpModule(new HttpModule())
                .build()
                .inject(this);//这一步是做真正的注入

        if(is_dev){
            apiService_dev.register();
        }else {
            apiService_release.register();
        }
    }
}

build的时候会生成一个DaggerUserComponet类,这个用来操作真正的注入。初始化有两种方式。

第一种是moudle里面其构造函数没有参数,则可以按下面的方法初始化。

DaggerUserComponet.create().inject(this);

当有参数时,我们则需要按照第二种方法初始化,不然会报错。

DaggerUserComponet.builder()
                .httpComponet(App.getHttpComponet())
                .userModule(new UserModule(this))
                //.httpModule(new HttpModule())
                .build()
                .inject(this);//这一步是做真正的注入

有参数的对象

首先来看该UserManager类和ApiService类UserStore类。

public class UserManager {

    private ApiService apiService;
    private UserStore userStore;

    public UserManager(ApiService apiService, UserStore userStore) {
        this.apiService = apiService;
        this.userStore = userStore;
    }

    public void register(){
        apiService.register();
        userStore.register();
    }
}


public class ApiService {

    private OkHttpClient mOkHttpClient;
    public static final MediaType JSON = MediaType.parse("application/json;charset = utf-8");

    private String url;
    /**
     * @Inject 在一个类中不能同时注解两个构造函数,不然会报下面的错误
     * Types may only contain one @Inject constructor
     */
    //@Inject
    /*public ApiService(){

    }*/

    /**
     * 在一个类中,其构造函数中有多少个参数,我们需要在module中提供对应的方法
     *
     * 比如:
     *      @Provides
     *     public String url(){
     *         return "Http://baidu.com";
     *     }
     *
     */

    public ApiService(OkHttpClient okHttpClient,String url){
        this.url = url;
        this.mOkHttpClient = okHttpClient;
        Log.e("zzf","------------ApiService的有参构造函数-----------");
    }

    public void register(){
        Log.e("zzf","------ApiService-----register----------");

        RequestBody requestBody = RequestBody.create(JSON,"");
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });
    }
}

public class UserStore {

    public UserStore(Context context){

    }

    public void register(){
        Log.e("zzf","------UserStore-----register----------");
    }
}

从上面可以看出,

UserManager类中包含ApiService和UserStore对象。ApiService中又包含OkHttpClient对象。

首先还是需要创建一个module,在里面提供这写对象的方法。

@Module/*(includes = {HttpModule.class})*/
public class UserModule {

    /**
     * 我们需要的Context,其实也可以创建一个application级别的module
     */
    private Context mContext;

    public UserModule(Context context){
        mContext = context;
    }

    @Provides
    public HttpService getHttpService(){
        return new HttpService();
    }

    /**
     * 在module提供的方法,必须为public,不然在build的时候会报错
     * @return
     */

    /**
     * error:Cannot have more than one binding method with the same name in a single module
     * 意思在同一个single module不能出现两个相同名字的方法
     *
     * 改成getApiService2后有报错
     * error:ApiService is bound multiple times
     * 意思是ApiService多次绑定,表示同一个类虽然可以有不同的狗仔函数,但是只能绑定一次
     *
     */
    /*@Provides
    public ApiService getApiService2(){
        return new ApiService();
    }*/

    /**
     * error:UserComponet (unscoped) may not reference scoped bindings:
     *
     */
    @Provides
    public ApiService getApiService(OkHttpClient okHttpClient,String url){
        return new ApiService(okHttpClient,url);
    }

    /*@Provides
    public UserStore getUserStore(){
        return new UserStore();
    }
*/

    /**
     * 我们知道一般OkHttpClient都是以单例的形式去实现,如果按照这种方法实现的话
     * 会导致每次调用OkHttpClient进行网络请求的似乎都会重新创建一个新的对象
     * 这样浪费资源,所以需要曹勇其他的方法,重新创建一个HttpModule。
     * @return
     */
    /*@Provides
    public OkHttpClient getOkHttpClient(){
        return new OkHttpClient().newBuilder().build();
    }*/

    @Provides
    public String url(){
        return "Http://baidu.com";
    }

    @Provides
    public UserStore getUserStore(){
        /**
         * 此时需要一个上下文,需要从module的构造函数进行传递
         */
        return new UserStore(mContext);
    }

    /**
     * com.example.daggerdemo2.dagger.UserManager cannot be provided without an @Inject constructor or an @Provides-annotated method.
     * public interface UserComponet
     * 意思是没有提供ApiService的构造函数。
     * 有两种方法提供
     * 1、@Provides
     *     public ApiService getApiService(){
     *         return new ApiService();
     *     }
     *
     * 2、直接在ApiService的构造函数上加个注解
     *
     *     @Inject
     *     public ApiService(){
     *
     *     }
     * @param apiService
     * @return
     */

    @Provides
    public UserManager getUserManager(ApiService apiService,UserStore userStore){
        return new UserManager(apiService,userStore);
    }
}

因为OkHttpCient对象,在我们做开发中一般保持单例,不然每发生一次请求,就会创建一个实例,这样极其浪费资源。

先创建一个HttpModule,

@Module
public class HttpModule {

    /**
     * @Singleton 表示单例的注解
     * 但是它有一个缺点,只能在同一个activity里面保证单例,在不同的actiovity不能保持单例
     * 解决办法:
     *
     * 在Application的onCreate方法中进行Componet的初始化,这样就能保证application级别的范围
     * @return
     */
    @Singleton
    @Provides
    public OkHttpClient getOkHttpClient(){
        return new OkHttpClient().newBuilder().build();
    }

}

因为要保证单例,所以需要用加入@Singleton。

然后提供OkHttpClient的对象方式有三种;

/**
 * 1、@Module(includes = {HttpModule.class})
 * 因为UserModule使用到了HttpModule里面的OkHttpClient对象,所以需要下面的方法进行关联获取其OkHttpClient对象
 * 2、@Component(modules = {UserModule.class,HttpModule.class})
 * 这时需要修改MainActivity里面Component的初始化
 * DaggerUserComponet.builder()
 *                 .userModule(new UserModule(this))
 *                 .httpModule(new HttpModule())//多加了这一句
 *                 .build()
 *                 .inject(this);
 * 3、添加一个Component,
 * @Singleton
 * @Component(modules = {HttpModule.class})
 * public interface HttpComponet {
 *
 * }
 * //@Singleton
 * @Component(modules = {UserModule.class},dependencies={HttpComponet.class})
 *
 * public interface UserComponet {
 *     void inject(MainActivity mainActivity);
 * }
 * 此时UserComponet就不需要添加@Singleton注解,只需要在HttpComponet上面添加。
 * okHttpClient在HttpModule中是单例的,所以HttpComponet中必须添加@Singleton
 * 但是UserComponet是依赖HttpComponet的,我们不加注解就会报错。
 *
 * error:UserComponet (unscoped) cannot depend on scoped components
 * 意思没有 unscoped的不能依赖有scope的components,所以我们也给UserComponet添加上@Singleton注解
 *
 * error:This @Singleton component cannot depend on scoped components
 *
 * 意思是@Singleton不能作用在不同的component上,也就是我们需要保证两个component的csope不同,所以我们需要自定义scope
 *
 * 解决方法:
 *
 * @Scope
 * @Documented
 * @Retention(RUNTIME)
 * public @interface AppScope {
 * }
 *
 * 然后在UserComponet加上即可。
 *
 * 为了保证是在Http全局单例,HttpComponet初始化在application里面初始化
 *  public class App extends Application {
 *
 *     private static HttpComponet httpComponet;
 *
 *     public static HttpComponet getHttpComponet(){
 *         return httpComponet;
 *     }
 *
 *     @Override
 *     public void onCreate() {
 *         super.onCreate();
 *         httpComponet = DaggerHttpComponet
 *                 .builder()
 *                 .httpModule(new HttpModule())
 *                 .build();
 *     }
 * }
 *
 */

第一种,在@Module注解中有一个方法,

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Module {
  /**
   * Additional {@code @Module}-annotated classes from which this module is
   * composed. The de-duplicated contributions of the modules in
   * {@code includes}, and of their inclusions recursively, are all contributed
   * to the object graph.
   */
  Class<?>[] includes() default {};

  /**
   * Any {@link Subcomponent}- or {@code @ProductionSubcomponent}-annotated classes which should be
   * children of the component in which this module is installed. A subcomponent may be listed in
   * more than one module in a component.
   *
   * @since 2.7
   */
  @Beta
  Class<?>[] subcomponents() default {};
}

可以直接用includes()进行关联。

第二种是直接加入到component中,

@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
@Target(TYPE)
@Documented
public @interface Component {

  Class<?>[] modules() default {};

  
  Class<?>[] dependencies() default {};


  @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
  @Target(TYPE)
  @Documented
  @interface Builder {}


  @Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
  @Target(TYPE)
  @Documented
  @interface Factory {}
}

可以直接用modules()的方式进行关联。

第三中直接重新创建一个component,然后通过dependencies()进行关联。

@Singleton
@Component(modules = {HttpModule.class})
public interface HttpComponet {
    OkHttpClient getOkHttpClient();
}

因为HttpModule里面提供的方法是@Singleton修饰的,所以其Component也需要这个注解修饰。

然后在UserComponet进行关联,

//@Singleton
@AppScope
@Component(modules = {UserModule.class},dependencies = {HttpComponet.class})
public interface UserComponet {
    void inject(MainActivity mainActivity);
}

根据最前面讲的第5点和第6点,

5、component的dependencies与自身的scope不能相同

6、SingleTon的组件不能依赖其他的scope组件,只能其他scope的组件依赖Single的组件

所以在UserComponet不能用@Singleton修饰,我们需要创建一个新的scope。也就是@AppScope。

@Scope
@Documented
@Retention(RUNTIME)
public @interface AppScope {
}

最终在activity中使用。

public class MainActivity extends AppCompatActivity {

    @Inject
    UserManager userManager;

    private boolean is_dev = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerUserComponet.builder()
                .httpComponet(App.getHttpComponet())
                .userModule(new UserModule(this))
                //.httpModule(new HttpModule())
                .build()
                .inject(this);//这一步是做真正的注入
        userManager.register();
    }
}

因为为了保证全局单例,所以在application中初始化HttpComponent。

public class App extends Application {

    /**
     * dagger的使用注意事项
     * 1、component的inject方法接受父类型参数,而调入时传入的是子类型对象则无法注入
     * 2、component关联的modules中不能有重复的provide
     * 3、module的peovide方法使用了scope,那么component必须使用同一个注解
     * 4、module的peovide方法没有使用了scope,那么component和module是否加注解都无关紧要
     * 5、component的dependencies与自身的scope不能相同
     * 6、SingleTon的组件不能依赖其他的scope组件,只能其他scope的组件依赖Single的组件
     * 7、没有scope的component不能依赖有scope的component
     * 8、一个component不能同时有多个scope,Subcomponent除外
     * 9、@Singleton的生命周期依附于component。要想保证真正的单例,需要在application中进行初始化
     * 10、Subcomponent同时具备两种不同生命周期的scope,具备父component的,也具备自己的
     * 11、Subcomponent的scope范围小于父Component
     */
    private static HttpComponet httpComponet;

    public static HttpComponet getHttpComponet(){
        return httpComponet;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        httpComponet = DaggerHttpComponet
                .builder()
                .httpModule(new HttpModule())
                .build();
    }
}

SubComponent的使用
@ActivityScope
@Subcomponent(modules = UtilModule.class)
public interface CComponent {
    void inject(DaggerActivity daggerActivity);
}

@Singleton
@Component(modules = HttpModule.class)
public interface FComponent {
    CComponent getChildComponent();
}

@Module
public class UtilModule {
    @Singleton
    @Provides
    public Gson getGson(){
        return new Gson();
    }
}

因为HttpModule里面得方法事@Singleton的,根据第3点可知,所以其component必须为@Singleton

又根据第7、11点可以知道,在CComponent中,只能用低于@Singleton得scope。

具体代码在 github.com/fengyuehan/…