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/…