MVP架构笔记之-DI框架dagger2
dagger2在mvp架构开发中主要起到了解耦的作用,我的上一篇文章MVP架构笔记之初探--mvp原理写的是一个最为基础的MVP架构,其实啊,这个架构是耦合的一个架构,我们的view层持有一个MvpPresenter()对象实例;我们的P层构造函数里面又新建了一个m层的对象实例,这样view依赖presenter,presenter又依赖model就是一个紧耦合的架构。
现在在我们用dagger2对上面的mvp架构进行改进,在这之前先对dagger2进行了解,首先dagger2的官网
https://google.github.io/dagger/ 以及dagger2的github开源地址 https://github.com/google/dagger,dagger2几个新属性的价绍:
@Inject | 可以注解成员变量和构造函数,如上面的view层持有的p的对象变量和p的构造函数 |
@Module | 可用来注解无法由我们注解构造函数的第三方类、系统类、接口类,如retrofit,okhttp,gson等 |
@Provides | 注解修饰的方法,一般用于@Module注解类中的方法,负责提供具体类型的依赖 |
@Componet |
负责修饰接口或抽象类,提供inject抽象方法等待相应的页面oncreate时实现注入,提供
modules=xxmodule.class链接module,实现@Inject 和@Module中间链接桥的作用
|
还有一个非必需但很重要的,
@scope :作用域,dagger2给我们提供了一种@Singleton单例,其他的类型需要我们自己定义,比如@ApplicationScope,@ActivityScope,@FragmentScope等,定义方式可以如下:
@Scope
@Retention(RUNTIME)
public @interface ActivityScope {}
需要了解更多scope原理请参考文档:http://www.cnblogs.com/tiantianbyconan/p/5095426.html
接下来,先来看一下我们改造过后的项目代码,首先我们看构造函数完成注入的方式activity中:
public class MainActivity extends AppCompatActivity implements MvpContract.View {
@Inject
TestPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerMainActivityComponent.create().inject(this);
setContentView(R.layout.activity_main);
presenter.toString();
}
@Override
public void showData(String data) {
Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
}
@Override
public void showload() {
}
}
public class TestPresenter {
String temp;
@Inject
public TestPresenter() {
temp = "测试数据";
}
public String toString() {
return temp;
}
}
@Component
public interface MainActivityComponent {
void inject(MainActivity activity);
}
为了测试方便,我新建了一个presenter,因为我在presenter中暂时还不想拿model和view对象,如上,我们发现activity的代码变得简单了许多,其中最关键的步骤就是我们开始注解的这一步
DaggerMainActivityComponent.create().inject(this);
这一步事真正实现presenter对象的实例化的,不然你可以把这行代码去掉,看你的presenter会不会报空指针异常,当然我们并没有写DaggerMainAcitivityComponent这类,这个类是我们的链接桥MainActivitiyComponent运行时生成的类,所以我们需要先把工程build一下才能调用这个类。注意:上述案例是在项目的mvpdagger2分支
接下来,我们来看一下当包含module时得实现方式,先看看我们得工程目录,我对代码进行了分包:
相比于前一个案例,确实复杂了不少,但分得却很明确,framework包下主要定义一些抽象得接口和公用得东西;di包下主要定义了module和component,前面我们也定义了component 的;mvp下对主要功能进行分层,契约类、M层、P层和v层,整个架构的思想如下图:
可以看到我们得契约类和先前得不太一样了,主要作用是对model和view层进行管理,即我们需要在presenter中处理的类对象,契约类如下:
public interface MainContract {
//对于经常使用的关于UI的方法可以定义到IView中,如显示隐藏进度条,和显示文字消息
interface View extends IView {
void showData(String string);
}
//Model层定义接口,外部只需关心Model返回的数据,无需关心内部细节,即是否使用缓存
interface Model extends IModel {
String getData();
}
}
接下来实现model层:
@ActivityScope
public class MainModel implements MainContract.Model {
@Inject
public MainModel() {
//获取网络数据或者缓存数据
}
@Override
public void onDestroy() {
}
@Override
public String getData() {
return "来自model层得数据";
}
}
可以看到我们对构造函数进行了注解,这里要写的原因是和我们module里面的写法有关系,如下:
@Module
public class MainModule {
private MainContract.View view;
/**
* 构建MainModule时,将View的实现类传进来,这样就可以提供View的实现类给presenter
*
* @param view
*/
public MainModule(MainContract.View view) {
this.view = view;
}
@ActivityScope
@Provides
MainContract.View provideMainView() {
return this.view;
}
@ActivityScope
@Provides
MainContract.Model provideMainModel(MainModel model) {
return model;
}
// @ActivityScope
// @Provides
// MainContract.Model provideMainModel() {
// return new MainModel();
// }
}
我们写的函数需要给函数提供值,所以注解了构造函数,当然如果换成下面不提供值,直接新建对象的话就不需要提供数据的来源了,自然也不需要@inject model 的构造函数;你可以跑一跑程序试试,两种是一样的效果。在如上两个类中我们都看到了有一个自定义注解@ActivityScope ,表示只针对当前的Activity 有效,限制作用域范围,注解在类上表示类,注解在方法上表示方法,把modlue都写好了就该我们的连接器component登场了,如下:
@ActivityScope
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
这里是一个接口,暴露了抽象方法给外部,由MainModule提供数据,等着页面来注解,同样在activity里面进行数据的注入操作,如下:
@Inject
MainPresenter mainPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
mainPresenter.getData();
}
@Override
public void showData(String string) {
Log.e("yy", string);
}
至此,我们就完成了整个主流程的操作。还有一个默认的@scope操作符@Singleton我忘记介绍了,其实和@ActivityScope类似,可以修饰相应的类和方法,如下:
@Singleton
@Provides
MainContract.Model provideMainModel(MainModel model) {
return model;
}
表示单例model,最后运行一下我们的程序结果如下:
代码在项目的mvpdagger2module分支,通过这两个案例我们对dagger2的使用有了一个初步的了解,其实dagger2还有更强大的用法,dagger.android,这个我也还没掌握。哈哈,大家可以看下引入规则,dagger2:
implementation 'com.google.dagger:dagger:2.14.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
dagger2.android 需要内外添加:
implementation 'com.google.dagger:dagger-android:2.14.1'
implementation 'com.google.dagger:dagger-android-support:2.14.1'
// if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.14.1'
最后,给出代码的git地址:
https://github.com/yangyong915/MvpManager-yy
如果你觉得好,一定记得STAR哦。
推荐阅读