Dagger2 知识梳理(4) @Scope 注解的使用

1、前言

对于@Scope注解,不少同窗都疑惑,今天咱们就来了解一下@Scope相关的知识,这里将会分为两部分介绍:java

  • 单个Component状况下@Scope的做用
  • 组织多个Component状况下对于@Scope的限制

首先,咱们须要了解@Scope实际上是一个 元注解,它和咱们在 Dagger2 知识梳理(2) - @Qulifier 和 @Named 解决依赖注入迷失 一文中介绍的@Qualifier同样,是用于 描述注解的注解,关于元注解更多的知识能够参考以前的这篇文章 Java&Android 基础知识梳理(1) - 注解git

@Scope所描述的注解用于两个地方:github

  • Component
  • Module中用于建立实例的provideXXX方法

而咱们常常看见的@Singleton注解其实就是用@Scope描述的注解,虽然它的表面意思是“单例”,可是咱们后面会看到它和单例其实并无必然的关系。ide

2、单个 Component 状况下 @Scope 的使用

@Scope描述的注解相似于下面这样,这里的PerScopeActivity就是用@Scope描述的注解:ui

@Documented
@Retention(RUNTIME)
@Scope
public @interface PerScopeActivity {}
复制代码

有可能会用到该注解的有两个地方:this

  • Component
@Component(dependencies = {ScopeAppComponent.class}, modules = {ScopeActivityModule.class})
@PerScopeActivity
public interface ScopeActivityComponent {
    public void inject(ScopeActivity scopeActivity);
    ScopeFragmentComponent scopeFragmentComponent();
}
复制代码
  • Module中用于建立实例的provideXXX方法
@Module
public class ScopeActivityModule {

    @Provides
    @PerScopeActivity
    public ScopeActivitySharedData provideScopeActivityData() {
        return new ScopeActivitySharedData();
    }

    @Provides
    public ScopeActivityNormalData provideScopeActivityNormalData() {
        return new ScopeActivityNormalData();
    }
}
复制代码

在单个Component状况下使用@Scope有如下几点说明:spa

  • 若是在ModuleprovideXXX方法上加上了@Scope声明,那么在与他关联的Component上也必须加上相同的@Scope声明
  • 若是Component加上了@Scope声明,provideXXX,那么和Component不加声明的状况相同。
  • ModuleprovideXXX方法和Component都加上了@Scope声明,那么在Component实例的生命周期内,只会建立一个由provideXXX方法返回的实例。也就是说,该Component会持有以前经过provideXXX方法建立的实例的引用,若是以前建立过,那么就再也不调用ModuleprovideXXX去建立新的实例,而是直接返回它以前持有的那一份。

上面的例子中,咱们经过ScopeActivityModule建立了两种类型的数据,provideScopeActivityData()方法上加上了@PerScopeActivity,而提供ScopeActivityNormalDataprovideScopeActivityNormalData()方法则没有,后面咱们将会看到,若是在目标类中使用同一个ScopeActivityComponent注入,而有多个ScopeActivitySharedData变量的状况下它们指向的是同一块内存地址,而ScopeActivityNormalData则会指向不一样的内存地址。3d

3、组织多个 Component 状况下对于 @Scope 的限制

对于单个Component还比较好理解,可是在组织多个Component的状况下就有些复杂了,这里的“组织”就是咱们在前一篇 Dagger2 知识梳理(3) - 使用 dependencies 和 @SubComponent 完成依赖注入 谈到的 依赖方式继承方式code

  • 在依赖或者继承的组织方式中,若是其中一个Component声明了@Scope,那么其它的Component也须要声明。
  • 在依赖关系中,被依赖的Component和须要依赖的Component@Scope不能相同
  • 在依赖关系中,须要依赖的Component@Scope不能够为@Singleton
  • 在组织关系中,子Component@Scope不能够和父Component@Scope相同:
  • 在组织关系中,若是父Component@Scope不为@Singleton,那么子Component@Scope能够为@Singleton

这些限制是由Dagger2在编译时去检查的,其目的是保证使用者不要对@Scope产生滥用的现象,由于@Scope的目的是 在特定做用域内控制被注入实例的复用component

4、示例

为了让你们更好的验证上面关于@Scope的解释,下面用一个Demo来演示,完整代码能够从 Dagger2Sample 的第四章获取,这个Demo包括三个大部分:

(1) ScopeApp

对应于咱们平时的Application类,并提供了全局的ScopeAppData类,在其ScopeAppComponent上有@Singleton注解。

@Singleton
@Component(modules = {ScopeAppModule.class})
public interface ScopeAppComponent {
    public ScopeAppData getScopeAppData(); //若是它被其它的Component依赖,那么须要声明getXXX方法。
}
复制代码
@Module
public class ScopeAppModule {

    @Provides
    @Singleton
    public ScopeAppData provideScopeAppData() {
        return new ScopeAppData();
    }
}
复制代码

(2) ScopeActivity

对应于一个主页面,其内部包含了ScopeActivitySharedDataScopeActivityNormalData,前者在ScopeActivityComponent的生命周期内保持惟一性,并带有PerScopeActivity注解。

@Component(dependencies = {ScopeAppComponent.class}, modules = {ScopeActivityModule.class})
@PerScopeActivity
public interface ScopeActivityComponent {
    public void inject(ScopeActivity scopeActivity);
    ScopeFragmentComponent scopeFragmentComponent();
}
复制代码
@Module
public class ScopeActivityModule {

    @Provides
    @PerScopeActivity
    public ScopeActivitySharedData provideScopeActivityData() {
        return new ScopeActivitySharedData();
    }

    @Provides
    public ScopeActivityNormalData provideScopeActivityNormalData() {
        return new ScopeActivityNormalData();
    }
}
复制代码

(3) ScopeFragment

对于于Activity下的一个子界面,它和ScopeActivityComponent是继承关系,并带有@PerScopeFragment注解:

@Subcomponent(modules = {ScopeFragmentModule.class})
@PerScopeFragment
public interface ScopeFragmentComponent {
    public void inject(ScopeFragment scopeFragment);
}
复制代码
@Module
public class ScopeFragmentModule {

    @Provides
    @PerScopeFragment
    public ScopeFragmentData provideScopeFragmentData() {
        return new ScopeFragmentData();
    }
}
复制代码

以上三个部分的关系为:

  • ScopeActivityComponent依赖于ScopeAppComponent
  • ScopeFragmentComponent继承于ScopeActivityComponent
  • 它们的Module上都有用@Scope描述的注解:@Singleton@PerScopeActivity@PerScopeFragment

示例验证

经过这个例子能够覆盖到上面咱们介绍的全部场景,你们能够直接在Github上查看,也能够clone下来,进行修改验证。

ActivityFragment中,咱们打印出变量的地址来验证前面的结论:

  • App
public class ScopeApp extends Application {

    private ScopeAppComponent mScopeAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mScopeAppComponent = DaggerScopeAppComponent.builder().scopeAppModule(new ScopeAppModule()).build();
    }

    public ScopeAppComponent getAppComponent() {
        return mScopeAppComponent;
    }
}
复制代码
  • Activity
public class ScopeActivity extends AppCompatActivity {

    private static final String TAG = ScopeActivity.class.getSimpleName();

    private ScopeActivityComponent mScopeActivityComponent;

    @Inject
    ScopeAppData mScopeAppData;

    @Inject
    ScopeActivitySharedData mScopeActivitySharedData1;

    @Inject
    ScopeActivitySharedData mScopeActivitySharedData2;

    @Inject
    ScopeActivityNormalData mScopeActivityNormalData1;

    @Inject
    ScopeActivityNormalData mScopeActivityNormalData2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scope);
        getScopeActivityComponent().inject(this);
        TextView tvData = (TextView) findViewById(R.id.tv_scope_activity);
        String result = "[ScopeActivity Space] \n mScopeAppData=" + mScopeAppData
                + "\n\n" + "mScopeActivitySharedData1=" + mScopeActivitySharedData1
                + "\n\n" + "mScopeActivitySharedData2=" + mScopeActivitySharedData2
                + "\n\n" + "mScopeActivityNormalData1=" + mScopeActivityNormalData1
                + "\n\n" + "mScopeActivityNormalData2=" + mScopeActivityNormalData2;
        tvData.setText(result);
    }

    public ScopeActivityComponent getScopeActivityComponent() {
        if (mScopeActivityComponent == null) {
            ScopeAppComponent scopeAppComponent = ((ScopeApp) getApplication()).getAppComponent();
            mScopeActivityComponent = DaggerScopeActivityComponent.builder().scopeAppComponent(scopeAppComponent).build();
        }
        return mScopeActivityComponent;
    }
}
复制代码
  • Fragment
public class ScopeFragment extends Fragment {

    private ScopeActivity mScopeActivity;

    @Inject
    ScopeAppData mScopeAppData;

    @Inject
    ScopeActivitySharedData mScopeActivitySharedData;

    @Inject
    ScopeActivityNormalData ScopeActivityNormalData;

    @Inject
    ScopeFragmentData mScopeFragmentData;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mScopeActivity = (ScopeActivity) context;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_scope, container, false);
        mScopeActivity.getScopeActivityComponent().scopeFragmentComponent().inject(this);
        TextView tv = (TextView) rootView.findViewById(R.id.tv_scope_fragment);
        String result = "[ScopeFragment Space] \n mScopeAppData=" + mScopeAppData
                + "\n\n" + "mScopeActivitySharedData1=" + mScopeActivitySharedData
                + "\n\n" + "ScopeActivityNormalData=" + ScopeActivityNormalData
                + "\n\n" + "mScopeFragmentData=" + mScopeFragmentData;
        tv.setText(result);
        return rootView;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
复制代码

结果为:

由上面例子中的现象,能够总结出如下几点:

  • ScopeAppData:该数据是由ScopeAppModule提供的,而它加上了@Singleton注解,而且咱们调用的是同一个对象,所以在ActivityFragment中地址相同。
  • ScopeActivitySharedData:在它的provide方法上,咱们加上了@PerScopeActivity注解,所以在ActivityFragment中,它的地址相同。
  • ScopeActivityNormalData:虽然在提供它的ScopeActivityModule中加上了@PerScopeActivity注解,可是在provide方法上没有声明,所以不管是在Activity,仍是在Fragment中,都是指向不一样的地址。
  • ScopeFragmentData:用于演示如何经过继承的方式,来实现依赖注入。

更多文章,欢迎访问个人 Android 知识梳理系列:

相关文章
相关标签/搜索