关于@Singleton的使用
当需要某个Module中的某个方法是单例的时候,需要在那个方法上方加@Singleton注解;同时,在module上方也需要同时加上@Singleton注解
代码:
/**
* 用来提供对象
* */
@Singleton//内部有一个方法写了单例,module也要必须加单例
@Module
public class HttpModule {
@Singleton
@Provides//@Provides 用以标注 Module 类中的方法,它的作用是 标注该 Module 可以向外界提供的类的实例对象的方法
public HttpObject providerHttpObject(){//提供Http对象
return new HttpObject();
}
}
可能存在的问题:当使用包含@Singleton注解的module的时候,代码注入需要写在application中
因为如果注入代码写在activity中,他就会每个activity在注入的时候,产生一个新的对象,从而导致其只能是一个局部单例(单个activity内部单例,但是不同activity间不是单例)
正确用法
application中的注入代码:
public class MyApplication extends Application {
private MyComponent myComponent;
@Override
public void onCreate() {
super.onCreate();
//将初始化放在Application中,就可以防止每个activity都新建一个对象,然后每个对象都拥有自己的单例的出现
//只有一个Component的时候
myComponent = DaggerMyComponent.builder()
.httpModule(new HttpModule())//执行到这儿的时候,在内存中,这个module就已经产生了
.build();//到了这里,就已经注入完成,产生了module和componet对象
}
public MyComponent getAppComponent(){
return myComponent;
}
}
在activity中的使用
public class MainActivity extends AppCompatActivity {
@Inject//任何Object都可以注入,这儿不能写私有
HttpObject httpObject;//=new HttpObject();
@Inject
HttpObject httpObject2;
//当module中没有单例注解时,每次注入,他都是不同的对象
@Inject
DatabaseObject databaseObject;
@Inject
DatabaseObject databaseObject2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//要编译过之后,才会出现DaggerMyComponent这个实现类,这个类是编译之后注解编译器帮我们完成的,也就是要rebuild之后才会有,不然会拿不到DaggerMyComponent
// //这种写法每次注入,他都是不同的对象
// DaggerMyComponent.create().injectMainActivity(this);
// //这种写法每次注入,他都相当于不同的对象;因此,如果两个不同的activity都单独写了这内容,并且都使用了单例,那么他们也只是各自对象里的单例而已,而不是全局单例。
// DaggerMyComponent.builder().databaseModule(new DatabaseModule())
// .build()
// //到了这里,就已经产生了module和componet对象
// .injectMainActivity(this);
//这样写,就不会每次都生成新的对象,这样里面方法的单例就不会每个activity新建了,因为injectMainActivity每个都不同,所以这个不能抽到application
((MyApplication)getApplication()).getAppComponent().injectMainActivity(this);
}
}
当有多个组件进行注入的时候
出现的坑的原因:当注入到activity中时,只能生成一个activity相关的文件,而两个地方都用到了注入,就会报错;所以要间接注入:即一个组件依赖另一个组件,然后将两个组件作为一个整体注入(注:没有scope的组件不能依赖有scope的组件)
/**
* 这就是一个组件
*
* @Singleton只是一个模板,我们需要scope都自定义
* dependencies:组件依赖的过程中
* 1、多个组件之间的scope不能相同
* 2、没有scope的,不能依赖有sope的组件
* */
//@Singleton//有一个Module写了单例,这边也必须要加单例
@AppScope//这边自定义掉Singleton的代码,然后用来替代Singleton;因为Singleton使用的时候需要符合十多条原则,而一模一样自定义重写一遍,只需要符合两条原则
@Component(modules = {HttpModule.class,DatabaseModule.class}//这里的modules是个数组
,dependencies = {PresenterComponent.class})//因为只能一个地方注入,所以这儿把另一个地方需要注入的进行依赖,而另一个地方不做依赖操作,只提供对象
public interface MyComponent {
void injectMainActivity(MainActivity mainActivity);//这里不能进行多态转换的,如果有多个,这个方法就写多个
void injectSecActivity(SecActivity secActivity);
}
依赖项
//@AppScope//因为已经有一个组件用到了这个Scope了,所以需要重新定义一个一样的,但是名字不同的Scope
/**
* 这儿作为依赖项,在生成文件里面也会作为一个单独的Component,因此扩展性不错
* */
@UserScope
@Component(modules = {PresenterMoudle.class})
public interface PresenterComponent {
//因为注入到activity,只能生成一个文件,而两个地方都用到了注入,就会报错;所以要间接注入
// void injectMainActivity(MainActivity mainActivity);
//为了解决问题,这儿就不再使用注入,而是只负责了一个对象的提供
public Presenter providePresenter();
}
在Application中的使用
//有多个Component的时候
myComponent = DaggerMyComponent.builder()
.httpModule(new HttpModule())//执行到这儿的时候,在内存中,这个module就已经产生了
.presenterComponent(DaggerPresenterComponent.builder()
.presenterMoudle(new PresenterMoudle())
.build())//这是第二Component,在这儿进行连接;这样两个Component就连接在一起了,并且new module的时候,两者都可以带参数,这样灵活性会好很多
.build();
多个组件一起使用@Singleton的坑
原因:多个组件一起使用@Singleton的时候,会出现编译通不过的情况。
解决方法:自定义不同名字的注解,并且将Singleton的代码一模一样放入其中。因为当多处一起使用的时候,需要符合十多条原则,而一模一样自定义重写一遍,只需要符合两条原则.
多个组件一起使用的时候,就每个组件自定义一个不同名的自定义注解
//自定义注解,这边是直接把@Singleton下边的代码拿来用了,相当于就是单例;
//用AppScope替代Singleton
@Scope
@Documented
@Retention(RUNTIME)
public @interface AppScope {}
//自定义注解,这边是直接把@Singleton下边的代码拿来用了,因为拥有Scope的注解,不能多个组件用同一个名字的,所以这边要再自定义注解
//用UserScope 替代Singleton
@Scope
@Documented
@Retention(RUNTIME)
public @interface UserScope {}
使用
/**
* 这就是一个组件
*
* @Singleton只是一个模板,我们需要scope都自定义
* dependencies:组件依赖的过程中
* 1、多个组件之间的scope不能相同
* 2、没有scope的,不能依赖有sope的组件
* */
@AppScope//这边自定义掉Singleton的代码,然后用来替代Singleton;因为Singleton使用的时候需要符合十多条原则,而一模一样自定义重写一遍,只需要符合两条原则
@Component(modules = {HttpModule.class,DatabaseModule.class}//这里的modules是个数组
,dependencies = {PresenterComponent.class})//因为只能一个地方注入,所以这儿把另一个地方需要注入的进行依赖,而另一个地方不做依赖操作,只提供对象
public interface MyComponent {
void injectMainActivity(MainActivity mainActivity);//这里不能进行多态转换的,如果有多个,这个方法就写多个
void injectSecActivity(SecActivity secActivity);
}
//@AppScope//因为已经有一个组件用到了这个Scope了,所以需要重新定义一个一样的,但是名字不同的Scope
/**
* 这儿作为依赖项,在生成文件里面也会作为一个单独的Component,因此扩展性不错
* */
@UserScope//因为已经有一个组件用到了AppScope了,所以需要新建一个不同名字但是代码一样的UserScope
@Component(modules = {PresenterMoudle.class})
public interface PresenterComponent {
//因为注入到activity,只能生成一个文件,而两个地方都用到了注入,就会报错;所以要间接注入
// void injectMainActivity(MainActivity mainActivity);
//为了解决问题,这儿就不再使用注入,而是只负责了一个对象的提供
public Presenter providePresenter();
}
使用@Inject注解进行对象注入的过程分析
当你写了一个含@Component注解的组件时(例如起文件名MyComponent),当你rebuild代码后会生成一个 “Dagger+你的组件名” (即生成DaggerMyComponent)的一个文件和一个
MyComponet代码:
@Singleton//有一个Module写了单例,这边也必须要加单例
@Component(modules = {HttpModule.class,DatabaseModule.class})//这里的modules是个数组
public interface MyComponent {
void injectMainActivity(MainActivity mainActivity);//这里不能进行多态转换的,如果有多个,这个方法就写多个
}
在DaggerMyComponent会将相关module和activity进行初始化,并且实现MyComponent的接口方法injectMainActivity
private void initialize(final Builder builder) {
//对module进行初始化
//单例情况下,会有个DoubleCheck.provider
this.providerHttpObjectProvider =
DoubleCheck.provider(HttpModule_ProviderHttpObjectFactory.create(builder.httpModule));
//无单例
this.providerDatabaseObjectProvider =
DatabaseModule_ProviderDatabaseObjectFactory.create(builder.databaseModule);
//对相关activity进行初始化
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(
providerHttpObjectProvider, providerDatabaseObjectProvider, providePresenterProvider);
}
@Override
public void injectMainActivity(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
同时还会为每个用到@Inject注解的activity生成一个(activity名字+_MembersInjector)的文件,例如:MainActivity_MembersInjector
DaggerMyComponent中实现的接口方法,就会调用MainActivity_MembersInjector中的injectMembers方法:activity中,写了几个@Inject注解,里面就会生成几个instance.xxx = xxxAndxxx2Provider.get();
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.httpObject = httpObjectAndHttpObject2Provider.get();
instance.httpObject2 = httpObjectAndHttpObject2Provider.get();
instance.databaseObject = databaseObjectAndDatabaseObject2Provider.get();
instance.databaseObject2 = databaseObjectAndDatabaseObject2Provider.get();
instance.presenter = presenterProvider.get();
}
里面的get()方法调用了Provider接口类,
而module类会生成(module名字+_+方法名字[首字母大写]+Factory)的工厂类,例如:HttpModule_ProviderHttpObjectFactory
public final class HttpModule_ProviderHttpObjectFactory implements Factory<HttpObject> {
private final HttpModule module;
public HttpModule_ProviderHttpObjectFactory(HttpModule module) {
assert module != null;
this.module = module;
}
@Override
public HttpObject get() {//获取对象
return Preconditions.checkNotNull(
module.providerHttpObject(), "Cannot return null from a non-@Nullable @Provides method");
}
public static Factory<HttpObject> create(HttpModule module) {//创建及返回对象
return new HttpModule_ProviderHttpObjectFactory(module);
}
}
如果在没单例的情况下,最后调用到module生成的工厂类中的get()方法的实现类,实现类返回了module.providerHttpObject(),而module.providerHttpObject()调用了module类里面的providerHttpObject方法,providerHttpObject做了new HttpObject()并且返回,最终获取到相应的对象
而在有单例的情况下,最后会走DoubleCheck.provider的代码
package dagger.internal;
import dagger.Lazy;
import javax.inject.Provider;
import static dagger.internal.Preconditions.checkNotNull;
public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
private static final Object UNINITIALIZED = new Object();
private volatile Provider<T> provider;
private volatile Object instance = UNINITIALIZED;
private DoubleCheck(Provider<T> provider) {
assert provider != null;
this.provider = provider;
}
@SuppressWarnings("unchecked") // cast only happens when result comes from the provider
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {//第二次进来的时候result 已经是我们自己的对象了,而UNINITIALIZED还是原先的Object对象,所以再也不会进去了,从而实现单例
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
instance = result = provider.get();//这儿调用回我们的module.providerHttpObject()方法,instance 就被赋值为我们自己的对象
provider = null;//这儿就可以被GC回收了
}
}
}
return (T) result;
}
/** Returns a {@link Provider} that caches the value from the given delegate provider. */
public static <T> Provider<T> provider(Provider<T> delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
/* This should be a rare case, but if we have a scoped @Bind that delegates to a scoped
* binding, we shouldn't cache the value again. */
return delegate;
}
return new DoubleCheck<T>(delegate);
}
/** Returns a {@link Lazy} that caches the value from the given provider. */
public static <T> Lazy<T> lazy(Provider<T> provider) {
if (provider instanceof Lazy) {
@SuppressWarnings("unchecked")
final Lazy<T> lazy = (Lazy<T>) provider;
// Avoids memoizing a value that is already memoized.
// NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
// are different types using covariant return on get(). Right now this is used with
// DoubleCheck<T> exclusively, which is implemented such that P and L are always
// the same, so it will be fine for that case.
return lazy;
}
return new DoubleCheck<T>(checkNotNull(provider));
}
}
子组件的使用
@Component(modules = {HttpModule.class})
public interface HttpComponent {
DatabaseComponent buildDatabaseComponent();//这样就把子组件拼装到一起了
}
子组件
/**
* 这是一个子组件
*
* 一般不建议用Subcomponent,因为用了这个,在生成的文件中,只有一个component,
* 而Subcomponent作为子组件,会在生成的component中作为的一个内部类,
* 其在作为内部类在new Module的时候,没有参数,
* 因此不能传参,意味着灵活性、扩展性很差。
* */
@Subcomponent(modules = {DatabaseModule.class})
public interface DatabaseComponent {
void injectMainActivity(MainActivity mainActivity);
}
子组件不能带参的原因:而Subcomponent作为子组件,会在生成的component中作为的一个内部类,其在作为内部类在new Module的时候,没有参数,因此不能传参,意味着灵活性、扩展性很差。
生成的内部类代码:
private final class DatabaseComponentImpl implements DatabaseComponent {
private final DatabaseModule databaseModule;
private Provider<DatabaseObject> providerDatabaseObjectProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DatabaseComponentImpl() {
this.databaseModule = new DatabaseModule();//这儿在new对象的时候,没有参数,因此无法传参,不灵活,扩展性差
initialize();
}
@SuppressWarnings("unchecked")
private void initialize() {
this.providerDatabaseObjectProvider =
DatabaseModule_ProviderDatabaseObjectFactory.create(databaseModule);
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(
DaggerHttpComponent.this.providerHttpObjectProvider,
DaggerHttpComponent.this.providerHttpObject1Provider,
providerDatabaseObjectProvider);
}
@Override
public void injectMainActivity(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
}
关于传参的形式使用
例如项目中用到多个服务器
调用含参module
@Named("xxx")//用来指定相关名字的方法
public class MainActivity extends AppCompatActivity {
//多个服务器
String url1 = "www.sina.com.cn";
String url2 = "www.163.com.cn";
@Inject
@Named("base1")//用这个指定方法
HttpObject httpObject1;
@Inject
@Named("base2")//用这个指定方法
HttpObject httpObject2;
@Inject
DatabaseObject databaseObject;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<String> urlList = new ArrayList<>();
urlList.add(url1);
urlList.add(url2);
DaggerHttpComponent.builder()
.httpModule(new HttpModule(urlList))//传入多个服务器地址
.build()
.buildDatabaseComponent()//必须把buildDatabaseComponent写进来
.injectMainActivity(this);
Log.i("DaggerDemo",httpObject1.baseUrl+"");
Log.i("DaggerDemo",httpObject2.baseUrl+"");
Log.i("DaggerDemo",databaseObject.hashCode()+"");
}
}
含参module
/**
* 用来提供对象
* 包名di表示依赖注入单词的缩写
* */
@Module
public class HttpModule {
ArrayList<String> baseUrl;
public HttpModule(ArrayList baseUrl){
this.baseUrl = baseUrl;
}
//如果有多个主链接的话,外边根据方法名来确定使用的是哪个主链接
@Named("base1")//用这个来区分方法
@Provides//加了这个就有了提供对象的功能
public HttpObject providerHttpObject() {//提供Http对象
return new HttpObject(baseUrl.get(0));
}
@Named("base2")//用这个来区分方法
@Provides//加了这个就有了提供对象的功能
public HttpObject providerHttpObject1() {//提供Http对象
return new HttpObject(baseUrl.get(1));
}
}
网友评论