第一篇讲解了无参情况两种注入方式和有参情况的两种注入方式。
第二篇讲解了注入迷失怎么解决,局部单例和全局单例模式的写法,以及Component依赖和继承的写法。
这一篇是Dagger2的融合篇,目标让读者能学会把Dagger2融合到mvp框架开发中。
在APP的开发中,有非常多的类都有依赖对象,总不能每一个目标类都配一个Component吧。应该以什么样的原则来划分Component呢,Component应该划分为多小的粒度呢,一般会遵循如下的组织原则:
- 创建一个全局的Component(ApplicationComponent), 在application中对其进行实例化,一般会在这个component中用来管理APP中的全局类实例。
- 对于每个页面创建一个Component,一个Activity页面定义一个Component,一个Fragment定义一个Component,使这些component继承自applicationComponent。
一创建全局的Component
全局的实例都有哪些呢?联网的需要单例,SP需要单例,还有全局的Application对象,那么就开始写吧。
创建全局module:
@Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
public static final int DEFAULT_TIMEOUT = 30;
@Singleton
@Provides
public API provideAPI(Retrofit retrofit) {
return retrofit.create(API.class);
}
@Provides
public Retrofit provideRetrofit(OkHttpClient client) {
return new Retrofit.Builder()
.baseUrl("baseurl")
.addConverterFactory(GsonConverterFactory.create())//可以添加自定义解析器和默认的解析器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加响应式编程的适配器
.client(client)
.build();
}
@Provides
public OkHttpClient provideOkHttpClient() {
return new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)//设置连接超时时间
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
}
@Singleton
@Provides
public SharedPreferences provideSharedPreferences() {
return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
}
@Singleton
@Provides
MyApplication provideApplication() {
return application;
}
}这里你应该能看懂,就是第一篇讲解的有参数的第三方注入写法。唯一要讲解的就是 @Singleton注解,他是Scope的默认实现,大家习惯写全局单例用这个注解,而不是去自定义一个注解。
然后就是AppComponent接口:
@Singleton
@Component(modules = AppModule.class)
public interface AppCompoent {
//当这个Component被别的Component依赖时,必须提供下边方法,不写代表不对依赖Component暴漏对象
API provideAPI();
SharedPreferences provideSharedPreferences();
MyApplication provideApplication();
}这里有两点要强调:
- 当这个Component被别的Component依赖时,必须提供下边方法,不写代表不对依赖Component暴漏对象
- Component的注解Singleton要和module的一致,要不编译不通过。
然后就是再Application中获取这个全局的对象。
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ComponentHolder.setAppComponent(DaggerAppCompoent.builder().appModule(new AppModule(this)).build());
}
}然后把获取到的对象保存到另外一个类里边,这个类的代码如下:
public class ComponentHolder {
private static AppCompoent myAppComponent;
public static void setAppComponent(AppCompoent component) {
myAppComponent = component;
}
public static AppCompoent getAppComponent() {
return myAppComponent;
}
}然后我们就完成一个一个全局的Component。
二.mvp中P对象的V对象注入
在MVP模式中,V层持有P的对象,P层持有V的对象,而且,我们会把P对象和V对象抽取到父类。
如下代码:
public abstract class BaseActivity<P extends BasePersenter> extends AppCompatActivity {
@Inject
public P mPersenter;
@Override
protected void onDestroy() {
super.onDestroy();
if (mPersenter != null) {
mPersenter.detach();
}
}}public class BasePresenterImpl<V extends BaseView> implements BasePersenter {
public V mView;
public BasePresenterImpl(V View) {
this.mView = View;
}
@Override
public void detach() {
this.mView = null;//释放View,防止内存泄露
}继承BaseActivity的子类HomeActivity的module就要去提供对应的Persenter对象。我们可以在Persenter的构造方法中使用注解他的构造方法,然后通过module提供对应的View对象,而module中的View对象就通过module的构造方法从子类传递过来。这样就完成了p对象的注入,同时view对象也就注入到P中去了(通过构造方法传递进去的)。
代码如下:
@Module
public class HomeModule {
HomeContact.view view;
public HomeModule(HomeContact.view view) {
this.view = view;
}
@Provides
public HomeContact.view provideHomeContactView() {
return view;
}
}@ActivityScope
@Component(modules = HomeModule.class,
dependencies = AppCompoent.class)
public interface HomeComponent {
void inject(MainActivity activity);
}public class HomePersenter extends BasePresenterImpl<HomeContact.view> {
@Inject
public HomePersenter(HomeContact.view View) {
super(View);
}
}注意:这里的ActivityScope注解是自定义的注解,这里如果不标记这个注解,会编译错误,因为依赖的Component有作用域注解,这里就要提供一个和依赖作用域不同的注解。
三P层中获取请求网络实例
我们请求网络的实力写的是全局单例模式,而HomeComponent依赖与全局Component,所以我们请求网络的实例可以注入到Activity中,但是对应的P层却注入不到请求网络对象。但是我们p层拥有View对象,我们就可以让View提供一个返回请求网络对象的方法。这样就可以获取到请求的网络的实例。代码如下:
public interface BaseView {
API getAPI();
}对应的BaseActivity中添加如下代码:
public abstract class BaseActivity<P extends BasePersenter> extends AppCompatActivity implements BaseView {
@Inject
public P mPersenter;
@Inject
public API api;
@Override
public API getAPI() {
return api;
}
......
}到此我们就把Dagger2简单的融合到mvp中,并且可以简单使用。但是我们还会面临一些细节需要去探讨怎么解决。
四简单页面没有P层
我们在上边探讨了正常有P层关于Dagger2的融合,并在基类中注入了api和Persenter对象,但是实际开发中,有的页面没有P层,但是我们却注入了P层,我们应该怎么解决这个问题呢?
去创建一个EmptyPersenter类,并把构造方法注入,代码如下:
public class EmptyPersenter implements BasePersenter {
@Inject
public EmptyPersenter() {
}
......
}这样我们就可以在没有P层的时候去泛型一个空的对象。
同时我们基类还有APi对象的注入,那么就需要我们去创建一个HotComponent去依赖全局的Component,否则会报错。
@ActivityScope
@Component(modules = HotModule.class, dependencies = AppCompoent.class)
public interface HotComponent {
void inject(HotActivity activity);
}同时还可以注入这个简单页面内,其他实例对象的注入。
五页面对象全部用注入赋值
假如我们新建一个FindActivity继承BaseActivity,让这个页面展示一个列表,让adapter和layoutmanager对象全部用注入赋值。
@Module
public class FindModule {
private FindContact.View view;
public FindModule(FindContact.View view) {
this.view = view;
}
@Provides
public FindContact.View provideFindContactView() {
return view;
}
@Provides
RecyclerView.LayoutManager provideLayoutManager() {
return new LinearLayoutManager(view.getActivity(), LinearLayoutManager.VERTICAL, false);
}
@Provides
FindAdapter provideAdapter() {
return new FindAdapter(null);
}
}注意:这里的view.getActivity(),实现思想和p中获取请求对象的思路是一样,不做过多的说明。
对应的页面代码:
public class FindActivity extends BaseActivity<FindPersenter> implements FindContact.View {
private RecyclerView recyclerView;
@Inject
RecyclerView.LayoutManager linearLayoutManager;
@Inject
FindAdapter adapter;
@Override
protected void inject() {
DaggerFindComonent.builder().appCompoent(ComponentHolder.getAppComponent()).findModule(new FindModule(this)).build().inject(this);
}
@Override
public int getLayout() {
return R.layout.activity_find;
}
@Override
protected void initView(Bundle savedInstanceState) {
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
@Override
protected void initListener() {
}
@Override
public void getSerivceData() {
mPersenter.getData();
}
@Override
public void setData(List<String> list) {
adapter.setNewData(list);
}
//对module类提供的上下文
@Override
public Context getActivity() {
return this;
}
}这样就把所有对象通过依赖注入的思想去赋值。
以上就是dagger2融合到mvp中的用法。这里说一下笔者自己的看法:dagger2的目的是为了解耦类之间的依赖关系而单生了,但是在小的项目中使用Dagger2会让开发者感觉很多余,原因就是因为项目小,逻辑简单。导致感觉繁琐。就比如上边的例子,连adapter和LayoutManager对象都用注入去赋值,有种去你邻居家里 十米远, 你却要开车的感觉。
读者有没有发现我们在每个页面都都写入了注入的代码,而并不是抽取到父类,这就是Dagger2在Android中使用的缺陷。为了解决这个问题,又出来了Dagger-android框架。至于Dagger-Android笔者并不打算开文章讲解用法,因为在实际项目中我并不打算用Dagger2,或者Dagger-Android去开发项目。并且在kotlin项目中更加显得Dagger2无用~后续可能会对Kotlin的依赖注入框架Kodein做一下研究。
最后本文的Demo地址:github.com/XiFanYin/Da…