美文网首页
dagger2框架的使用及一些坑及注入代码的执行流程分析

dagger2框架的使用及一些坑及注入代码的执行流程分析

作者: 逐鹿者不见山 | 来源:发表于2020-03-06 19:26 被阅读0次

关于@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));
    }


}

相关文章

网友评论

      本文标题:dagger2框架的使用及一些坑及注入代码的执行流程分析

      本文链接:https://www.haomeiwen.com/subject/kcrvrhtx.html