文接《Dagger2 依赖的接力游戏(六)》
经过前面的介绍,我们对dagger2的结构和原理已经有了比较清楚的了解,但是dagger2直接拿来在android平台上使用,还会有些问题,因为android平台定义了很多框架组件,是没法直接创建实例的。因此google针对android平台特地封装了一套模板库dagger-android,来帮我们实现框架组件的依赖注入。这篇文章,我们从头开始讲解如何使用这个依赖库,为什么要使用这个依赖库,以及这个依赖库是怎么工作的。
示例介绍
我们会通过一个示例项目,来讲解这篇文章的内容。这个项目实现一个非常简单的随机展示一个汽车对象的功能,像这样:
我们点击一下NEXT CAR按钮,就会展示名称和气缸数目随机的另外一个Car对象。
这个示例,我们分为三个阶段来完成,第一个阶段,我们不使用dagger框架,直接是正常的mvp + repository架构;第二个阶段,我们使用dagger2对前面的代码进行依赖优化;第三个阶段,我们针对dagger2优化过的依赖,讲解依旧存在的一些问题点,并使用dagger-android库进行优化,然后进行原理解析。每个阶段,我们都会进行依赖关系的分析,通过逐步演进的方式,体现优化的必要性。
数据部分
Car 和 Engine类
这部分内容我们复用以前项目的基础类型定义CarResouce接口用于获取一个随机的Car对象
public interface CarResource {
Car getRandomCar();
}
结构部分
- mvp结构
public interface IView {}
public interface IPresenter {
void takeView(IView baseView);
void dropView();
}
public interface HomeContract {
interface IHomeView extends IView {
void showCar(Car car);
}
interface IHomePresenter extends IPresenter {
void start();
void nextRandomCar();
}
}
- App结构
我们使用Application + Activity + Fragment的常见结构来完成这个示例,Activity作为容器,Fragment作为MVP的实现层。这里先看下布局文件:
- Activity布局 activity_home.xml :
- Fragment布局 fragment_home.xml :
正常模式
本节示例代码收录在项目的sample-without-dagger2分支
数据部分
数据部分我们直接实现一个CarRepository,并将它做成单例:
- 代码实现
public class CarRepository implements CarResource {
private static volatile CarRepository mInstance;
private CarRepository() {
}
public static CarRepository getInstance() {
if (mInstance == null) {
synchronized (CarRepository.class) {
if (mInstance == null) {
return new CarRepository();
}
}
}
return mInstance;
}
@Override
public Car getRandomCar() {
String name = UUID.randomUUID().toString().substring(0, 4);
int cylinders = (int) (Math.random() * 10);
Car car = new Car(new Engine(cylinders));
car.setName(name);
return car;
}
}
- 问题分析
我们在Repo里直接创建Car对象,对Car和Engine类产生了直接依赖,这部分是可以使用Dagger进行代理创建的。但是这样的话,由于我们的示例过于简单,只有随机产生Car对象这个一个功能,我们Repo层就没有存在的必要了。但是在实际的对象维护中,还会涉及更复杂的操作,比如添加Car,编辑Car,更新Car,删除Car等等,Repo层的封装还是必要的,所以我们在这里保留这部分的逻辑,着重关注Android组件的依赖注入。
我们在CarRepo里还手动实现了单例模式,这个让我们想到dagger的singleton功能,我们可以对它进行优化。因为我们的数据仓库是全局唯一的,我们可以放在AppComponent层来提供它。
MVP实现
- Activity实现
public class HomeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
Fragment fragment = new HomeFragment(new HomePresenter(CarRepository.getInstance()));
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container_home, fragment)
.commit();
}
}
-
Activity问题分析
这里的依赖问题是最多的,我们为了装配MVP组合,直接依赖了HomeFragment、HomePresenter、CarRepository三个实现类型,我们需要对这三个类型进行解耦。
我们分析一下,这里的目的就是获取一个Fragmet,然后加载它,实际上它不需要Fragmet以外的其他任何类型和对象,而之所以这里这么臃肿,正式因为我们采用直接依赖和手动注入的方式,让这里多出了很多不必要的类型依赖。所以我们为它做一个HomeActivityComponent,进行Fragment的注入。
Framgment实现
public class HomeFragment extends Fragment implements HomeContract.IHomeView, View.OnClickListener {
HomeContract.IHomePresenter mHomePresenter;
private TextView mTvCarInstruction;
private Button mBtnNextCar;
public HomeFragment() {
}
@SuppressLint("ValidFragment")
public HomeFragment(HomeContract.IHomePresenter presenter) {
mHomePresenter = presenter;
}
@Override
public void onResume() {
super.onResume();
mHomePresenter.takeView(this);
mHomePresenter.start();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
mTvCarInstruction = view.findViewById(R.id.tv_car_instruction);
mBtnNextCar = view.findViewById(R.id.btn_next_car);
mBtnNextCar.setOnClickListener(this);
return view;
}
@Override
public void onDestroy() {
super.onDestroy();
mHomePresenter.dropView();
}
@Override
public void showCar(Car car) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Car : ").append("\n")
.append("Name : ").append(car.getName()).append("\n")
.append("Engine cylinders : ").append(car.getEngine().getCylinderNumbers());
mTvCarInstruction.setText(stringBuilder.toString());
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_next_car) {
mHomePresenter.nextRandomCar();
}
}
}
- Fragment问题分析
我们的Fragment依赖的Presenter,是通过构造方法传进来的,可以看到构造方法上面还有@SuppressLint("ValidFragment")注解,否则AndroidStuido会提示你不要使用带参的构造方法来创建Fragment,而使用默认的构造方法 + Fragment.setArguments来实现参数的传递,否则设备配置参数发生变化时(比如屏幕旋转),Activity重新实例化Fragment的时候,会调用默认构造方法,从而导致参数丢失。
所以这里我们有两个选择,一个是在Fragment中直接创建Presenter,这就产生了直接依赖,所以我们采用第二种,使用dagger进行依赖注入。我们可以把Fragment的注入需求放在HomeActivityComponent里来实现,但是这样Fragment和Activity就形成了绑定关系了,不能被其他页面复用,所以我们还得独立给它做一个HomeFragmentComponent。
- Presenter 实现
public class HomePresenter implements HomeContract.IHomePresenter {
private HomeContract.IHomeView mView;
private CarResource mCarResource;
public HomePresenter(CarResource carResource) {
mCarResource = carResource;
}
@Override
public void takeView(IView iView) {
mView = (HomeContract.IHomeView) iView;
}
@Override
public void dropView() {
mView = null;
}
@Override
public void start() {
nextRandomCar();
}
@Override
public void nextRandomCar() {
mView.showCar(mCarResource.getRandomCar());
}
}
-
Presenter问题分析
Presenter在类型依赖上没啥问题,它唯一依赖的CarResource是外部传入的,但是如果我们通过参数来注入,外部势必就会依赖某一个直接的类型,所以我们也通过dagger来注入。
问题总结
我们在上面分段总结了各个部分的依赖问题及解决方案,总结起来就是使用AppComponent、HomeActivityComponent、HomeFragmentComponent,因为我们要把CarRepository做成单例,所以我们把后面的Component做成前面子组件,否则会创建多个CarRepository的实例。下面我们使用这个方式来改造我们的实现。
Dagger2模式
本节示例代码收录在项目的sample-with-dagger2分支
App注入
AppComponent实现:
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
CarResource getCarResource();
HomeActivityComponent.Builder homeActivityComponent();
@Component.Builder
interface Builder {
AppComponent build();
}
}
HomeActivityComponent是我们用于注入Activity的组件,这里把它做成子组件,保证它使用AppComponent里的CarResource。然后我们声明Singleton作用域,保证CarResource是一个单例。我们再看看Module实现:
@Module(subcomponents = HomeActivityComponent.class)
public abstract class AppModule {
@Binds
abstract CarResource carResource(CarRepository carRepository);
}
Module中我将HomeActivityComponent声明为子组件,并通过Binds将CarResource的需求嫁接到CarRepository实现上。
@Singleton
public class CarRepository implements CarResource {
@Inject
public CarRepository() {
}
@Override
public Car getRandomCar() {
String name = UUID.randomUUID().toString().substring(0, 4);
int cylinders = (int) (Math.random() * 10);
Car car = new Car(new Engine(cylinders));
car.setName(name);
return car;
}
}
CarRepository中去掉了单例的实现,使用Singleton来实现,是不是清爽很多?我们看看调用代码:
public class CarApp extends Application {
AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
mAppComponent = DaggerAppComponent.builder().build();
}
public AppComponent appComponent() {
return mAppComponent;
}
}
我们创建一个AppComponent实例,将它维护起来,让其他地方获取。
Activity注入
HomeActivityComponent实现:
@Subcomponent(modules = HomeActivityModule.class)
public interface HomeActivityComponent {
void inject(HomeActivity activity);
HomeFragmentComponent.Builder homeFragmentComponent();
@Subcomponent.Builder
interface Builder {
HomeActivityComponent build();
}
}
因为我们要让Fragment也使用AppComponent中的CarResource,所以我们也把Fragment做成Activity的子组件。我们看看Module实现:
@Module(subcomponents = HomeFragmentComponent.class)
public interface HomeActivityModule {
@Binds
Fragment provideHomeFragment(HomeFragment fragment);
@Binds
HomeContract.IHomePresenter providePresenter(HomePresenter homePresenter);
}
我们在Activity中并没有用到Presenter,但是这里却提供了Presenter的依赖,这是因为父组件依赖了HomeFragment,而HomeFragment依赖的是HomeContract.IHomePresenter类型,由于父组件自身的依赖必须是完整的,如果没有这个类型绑定,就会编译失败。同样,这里声明了HomeFragmentComponent作为自己的子组件。我们看看Activity中的调用代码:
public class HomeActivity extends AppCompatActivity {
HomeActivityComponent mComponent;
@Inject
Fragment mHomeFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
mComponent = ((CarApp) (getApplication())).appComponent()
.homeActivityComponent().build();
mComponent.inject(this);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container_home, mHomeFragment)
.commit();
}
public HomeActivityComponent getComponent() {
return mComponent;
}
}
这里和AppComponent中一样,我们要把自己的Component保存起来,以便Framgent中使用。
Fragment注入
FragmentComponent实现:
@Subcomponent
public interface HomeFragmentComponent {
void inject(HomeFragment homeFragment);
@Subcomponent.Builder
interface Builder {
HomeFragmentComponent build();
}
}
因为我们的Presenter在父组件中已经绑定过了,所以我们这里不需要Module,我们再看看调用:
public class HomeFragment extends Fragment implements HomeContract.IHomeView, View.OnClickListener {
@Inject
HomeContract.IHomePresenter mHomePresenter;
private TextView mTvCarInstruction;
private Button mBtnNextCar;
@Inject
public HomeFragment() {
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
HomeActivity homeActivity = (HomeActivity) getActivity();
homeActivity.getComponent().homeFragmentComponent().build().inject(this);
}
//省略相同部分代码
}
最后看看Presenter的注入实现:
public class HomePresenter implements HomeContract.IHomePresenter {
private HomeContract.IHomeView mView;
private CarResource mCarResource;
@Inject
public HomePresenter(CarResource carResource) {
mCarResource = carResource;
}
//省略相同部分代码
}
问题分析
经过上述的改造,我们已经实现了使用dagger来完成我们的单例和依赖注入,和第一版比起来,我们已经没有了对各种实现类型的直接依赖了,而是使用dagger来注入我们要使用的对象,简洁很多,但是还是存在问题。
对比HomePresenter的依赖注入,我们会发现Fragment和Acitivty的依赖注入需要我们手动调用组件进行注入,而HomePresenter在创建的时候dagger自动将它依赖的对象构建好,并通过构造方法初始化好了。这就暴露出了dagger2一个问题,dagger2在管理我们自己创建的对象的时候,可以通过构造方法自动补全依赖,进而托管我们全部的依赖构造,而在遇到不能自己创建对象的时候,我们只能在对象的某个回调中,对对象进行依赖注入。这就会导致两个问题:
- 我们需要创建很多个SubComponent来实现对各个android组件的注入
- 我们需要手动获取父组件的Component来构建自己的Component,实现注入过程,当组件多的时候,这会很难维护,并且还会出现类型依赖问题。比如我们的HomeFragment就依赖了HomeActivity类型,导致它无法被其他Activity复用。
- 我们定义了组件的依赖层次,同时使用子组件来实现了它们,但是并有Scope来约束它们,这是不安全的,同层次的组件也可能会互相依赖,是有可能会导致对象反向持有,引起内存泄露的。
优化策略
1. AndroidInjector + @ContributesAndroidInjector
针对第一个问题,dagger-android采用了一个策略,它定义了一个模板类AndroidInjector
2. HasXXXInjector + DaggerXXX(XXX代表android各种组件)
第二个问题其实可以拆分成两个部分。
第一部分是类型依赖问题,dagger-android库将我们依赖的Application和Activity等框架组件类型抽象成了HasXXXInjector,避免父类行的暴露和依赖。我们举个Fragment的例子来看看:
@Beta
public interface HasFragmentInjector {
/** Returns an {@link AndroidInjector} of {@link Fragment}s. */
AndroidInjector fragmentInjector();
}
它能够获取一个Fragment注入器,我们看看这个AndroidInjector是啥:
public interface AndroidInjector {
void inject(T instance);
interface Factory {
AndroidInjector create(T instance);
}
abstract class Builder implements AndroidInjector.Factory {
@Override
public final AndroidInjector create(T instance) {
seedInstance(instance);
return build();
}
@BindsInstance
public abstract void seedInstance(T instance);
public abstract AndroidInjector build();
}
}
这里我去掉了注释,只看核心部分。首先它定义了一个工厂类接口,用于创建自己,然后定义了一个Builder实现了这个接口。如果给它们分别加上@Component和@Component.Builder注解,它是不是就是一个常规的Component了?所以它实际上就是Component的模板,核心方法就是void inject(T instance),是用来注入模板类的。
代入来说,它将我们的Activity抽象成HasFragmentInjector,我们在Fragment的onCreate方法里,获取通过Activity获取AndroidInject
第二部分是Component手动获取和手动注入问题,dagger-android库对此,采用了帮我们实现一整套,支持依赖注入的,以Dagger开头的Android组件的方式来解决这个问题。这些组件能够帮我们保存子组件的注入器,能够在生命周期中从父组件获取自己的注入器对自己进行注入,这样就避免了我们手动创建Component和注入了。因为DaggerActivity处于中间层,需要承上启下,我们看看它是怎么实现的:
@Beta
public abstract class DaggerActivity extends Activity implements HasFragmentInjector {
@Inject DispatchingAndroidInjector fragmentInjector;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
@Override
public AndroidInjector fragmentInjector() {
return fragmentInjector;
}
}
很简单是不是?
它维护了一个叫做DispatchingAndroidInjector
的对象,看名字就知道提供Framgent注入器的。然后在onCreate中调用AndroidInjection.inject方法对自己进行注入。我们先看看AndroidInjection提供的注入方法:
public static void inject(Activity activity) {
checkNotNull(activity, "activity");
Application application = activity.getApplication();
if (!(application instanceof HasActivityInjector)) {
throw new RuntimeException(
String.format(
"%s does not implement %s",
application.getClass().getCanonicalName(),
HasActivityInjector.class.getCanonicalName()));
}
AndroidInjector activityInjector =
((HasActivityInjector) application).activityInjector();
checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());
activityInjector.inject(activity);
}
这个类就是整个dagger-android库的核心机制了,它实现了不同组件的注入逻辑,要了解这个框架,这个类必须得看懂来。这里我们只看Activity的注入代码。因为Activity的父组件就是Application了,所以它获取Application对象,并调用activityInjector()方法获取自己的注入器,然后对自己进行注入。我们看看DaggerApplication实现的activityInjector()方法。
@Beta
public abstract class DaggerApplication extends Application
implements HasActivityInjector,
HasFragmentInjector,
HasServiceInjector,
HasBroadcastReceiverInjector,
HasContentProviderInjector {
@Inject DispatchingAndroidInjector activityInjector;
@Inject DispatchingAndroidInjector broadcastReceiverInjector;
@Inject DispatchingAndroidInjector fragmentInjector;
@Inject DispatchingAndroidInjector serviceInjector;
@Inject DispatchingAndroidInjector contentProviderInjector;
@Override
public DispatchingAndroidInjector activityInjector() {
return activityInjector;
}
//省略部分重复代码
}
我们可以看到DaggerApplication为各大组件都维护了一份injector,它是一个DispatchingAndroidInjector对象,并且用组件对应的方法返回这个对象,而这个对象也是一个AndroidInjector
@Override
public void inject(T instance) {
boolean wasInjected = maybeInject(instance);
if (!wasInjected) {
throw new IllegalArgumentException(errorMessageSuggestions(instance));
}
}
它调用了一个maybeInject,如果不成功,就抛异常,我们看看maybeInject方法:
public boolean maybeInject(T instance) {
Provider> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) {
return false;
}
@SuppressWarnings("unchecked")
AndroidInjector.Factory factory = (AndroidInjector.Factory) factoryProvider.get();
try {
AndroidInjector injector =
checkNotNull(
factory.create(instance), "%s.create(I) should not return null.", factory.getClass());
injector.inject(instance);
return true;
} catch (ClassCastException e) {
throw new InvalidInjectorBindingException(
String.format(
"%s does not implement AndroidInjector.Factory<%s>",
factory.getClass().getCanonicalName(), instance.getClass().getCanonicalName()),
e);
}
}
这里有点复杂,我们慢点讲。它的核心代码在try&catch里面,即获取一个AndroidInject
通过目前的介绍,我们知道dagger-android库通过一套框架,将我们所有组件的注入需求,通过父组件为子组件提供注入器的形式,层层代理,最后全部搜集到了Application层,便于我们管理。这带来的一个反向问题就是,我们在application初始化的时候,所有的AndroidInjector注入器都要实例化,而这些玩意都是我们的Component,它们的实例化有可能是有不小开销的,并且Component应该跟随它服务对象的生命周期创建和销毁。最关键的是,你翻上去看AnddroidInejctor的实现,它的Builder方法里有个seedInstance是需要组件实例的。
所以我们Application创建的时候,即要作为入口注入我们整个App的依赖注入框架,又不能一次性创建这个注入框架,怎么办?加个中间层呗!这就是AndroidInjectFractory
那么FactoryProvider这层又是为了啥呢?因为我们的组件是很多的,一个Factory是不够的,所以我们要保存到集合里,还记得我们的Multibind介绍么?这里用的是class索引的Map集合,所以它才在用的时候,根据instance.class取出Factory。我们看下这个provider的定义:
private final Map, Provider>>
injectorFactories;
@Inject
DispatchingAndroidInjector(
Map, Provider>> injectorFactories) {
this.injectorFactories = injectorFactories;
}
看到了吧,和我们解释的是一样的,并且这个Map是通过构造方法,由Dagger传进来的! 这样你回头看我们的DaggerApplication和DaggerActivty代码,发现它所有的DispatchingAndroidInjector
好了,关于第二点的优化我们就解释完了。看到这里,如果还没有懵逼的同学,会发现还有两个问题:
- 为啥AndroidInject
里有seedInstance方法? - 我们如何实现在Application里使用AppComponent实现注入器的注入?
这个问题我们在下面部分演示过后进行讲解。
3. ActivityScoped和FragmentScoped
第三个问题就比较简单了,我们直接创建两个专用的Scope,让Activity和Fragment组件各自使用一个就可以了,如果还有别的独立的组件,比如Service什么的,可以另外再创建一个。
接下来,我们看使用dagger-android库优化之后的实现。
Dagger2 + dagger-android库 模式
本节示例代码收录在项目的sample-with-dagger-android分支
Application注入
AppComponent定义:
@Singleton
@Component(modules = {AppModule.class,
AndroidSupportInjectionModule.class})
public interface AppComponent extends AndroidInjector {
}
因为我们的CarResource通过组件注入传递给Activity和Fragment了,所以我们不需要暴露任何对象了。我们看到这里依赖了AndroidSupportInjectionModule类作为module,我们看看它是啥:
@Beta
@Module(includes = AndroidInjectionModule.class)
public abstract class AndroidSupportInjectionModule {
@Multibinds
abstract Map, AndroidInjector.Factory extends Fragment>>
supportFragmentInjectorFactories();
private AndroidSupportInjectionModule() {}
}
它又依赖了AndroidInjectionModule,然后提供了一个空的Fragment注入器工厂的Map集合。如果你打开AndroidInjectionModule的源码,你会发现里面是Android四大组件注入器工厂的空集合。为啥要这个东西呢?因为我们的DaggerApplication声明了要注入这些集合,但是我们App中不一定全都用到了啊!如果不提供这个module,是会报找不到提供方的。这也是我们在MultiBinds章节说的使用空集合作为类型提供,不理解回头再看看吧!
AppModule定义:
@Module
public abstract class AppModule {
@Binds
abstract CarResource carResource(CarRepository carRepository);
@ActivityScoped
@ContributesAndroidInjector(modules = HomeActivityModule.class)
abstract HomeActivity homeActivity();
}
这里我们就使用了@ContributesAndroidInjector,它有modules参数,表示用这些Modules生成一个SubComponent,这个SubComponent继承于AndroidInjector
我们看下生成之后的代码:
@Module(subcomponents = AppModule_HomeActivity.HomeActivitySubcomponent.class)
public abstract class AppModule_HomeActivity {
private AppModule_HomeActivity() {}
@Binds
@IntoMap
@ActivityKey(HomeActivity.class)
abstract AndroidInjector.Factory extends Activity> bindAndroidInjectorFactory(
HomeActivitySubcomponent.Builder builder);
@Subcomponent(modules = HomeActivityModule.class)
@ActivityScoped
public interface HomeActivitySubcomponent extends AndroidInjector {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder {}
}
}
我们看到它为我们生成了HomeActivitySubcomponent,但是用了一个Module来包装它,因为它要在这个Module里把HomeActivity的注入器,添加到Application中注入器的Map中,可以看到它用了Binds,将AndroidInject.Buidler绑定到HomeActivitySubcomponent.Builder来创建这个Factory,不明白的再去看看AndroidInjector定义。然后使用@IntoMap添加到集合中,这样当我们HomeActivity起来的时候,AndroidInjection类就能从Application实例中取出DispatchAndroidInjector
那这个AppModule_HomeActivityModule在哪里被引用了呢?正常情况它应该是AppComponent的Module,但是它是dagger2生成的中间模板,所以就没有被显示声明到Component的依赖中了,但是依赖关系是保留了的。
我们再看看CarApp:
public class CarApp extends DaggerApplication {
@Override
protected AndroidInjector extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().build();
}
}
它只要继承DaggerApplication,然后返回一个自己的注入器就可以了,是不是超级清爽!
Activity注入
Acitivty的Component我们已经在上面看过了,我们看看它的Module:
@Module
public interface HomeActivityModule {
@FragmentScoped
@ContributesAndroidInjector
HomeFragment provideHomeFragment();
@Binds
HomeContract.IHomePresenter providePresenter(HomePresenter homePresenter);
}
这里和前面的AppMoudle很相似,就不再解释了,我们的Fragment没有额外的依赖,就没有Module了。最后我们再稍微看下Activity和Fragment的实现:
public class HomeActivity extends DaggerAppCompatActivity {
@Inject
HomeFragment mHomeFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container_home, mHomeFragment)
.commit();
}
}
public class HomeFragment extends DaggerFragment implements HomeContract.IHomeView, View.OnClickListener {
@Inject
HomeContract.IHomePresenter mHomePresenter;
private TextView mTvCarInstruction;
private Button mBtnNextCar;
@Inject
public HomeFragment() {
}
//省略重复部分代码。
}
可以看到已经没有任何依赖和注入代码了,拿着对象直接玩耍咯。
现在我们回头来整理一下遗留的三个问题:
如何使用@ContributesAndroidInjector帮助我们实现自动生成子组件,它是怎么工作的?
这个问题经过演示和讲解应该已经很清楚了。为啥AndroidInject
里有seedInstance方法?
我也不是很清楚,在这个例子中,我们的AppComponent是没有使用这个方法的,并且在AndroidInjection类的各种类型的组件注入代码里,我们可以看到AndroidInjector和seedInstace传递进来的实例是没有什么逻辑关联的,它唯一的用处就是要求Builder在构建自己的时候,检查是否传进来组件实例,也就是强制要求不能在组件实例范围之外创建,所以只能推测它是用于限制Component实例创建的。我们如何实现在Application里使用AppComponent实现注入器的注入?
实现方法就是使用@ContributesAndroidInjector,从AppComponent开始为各层级android组件创建对应依赖关系的注入器咯。我们在示例中也介绍过了,有时间我会在补充一个完整的时序图来说明一下注入过程,当然,自己看源码是最深入的理解方式了。
总结
本示例中还有一些不完善的地方,比如我们的CarRepository其他地方获取不到,没法进行测试,再比如我们的Fragment在注入的时候已经实例化了,会引起额外的开销,应该使用Lazy来优化一下,再比如我们的Module划分也不是很合理,把Activity绑定和数据提供放在了一起,但是我们的使用方法和原理已经基本讲到了,剩下的就手动实践一把吧。最后我们会发现优化过后,和google提供的sample是一模一样的了,也更能理解sample中的各个细节,都是有它的用意和必要性的。
Dagger2系列的学习,我觉得比提供一个依赖管理工具更具有价值的,是它让我们学习了如何从依赖的角度去审视我们的应用,敦促我们在应用的设计阶段规划出合理的架构。还有它通过factory和provider实现我们对象提供的层层解耦,现身说法地告诉我们设计模式的强大,以及如何应用它们。dagger-android通过模板来实现组件注入能力,也是非常的简约和精彩,实在值得多体会一下。最后,dagger2告诉我们一个终极的道理,我们人类总是拘泥于各种形式,而逻辑实现总是可以自由自在!
我们Dagger2系列的文章到此就结束了,有些地方肯定也没讲到或者没有讲好,希望能够对大家有所帮助。最后,祝大家和dagger2玩的愉快!