更清晰的Dagger2 + MVP 架构

来源:互联网 发布:人工智能语言有哪些 编辑:程序博客网 时间:2024/05/15 09:44

Dagger2 与 MVP

Dagger2是Google提供的依赖注入框架,依赖注入为Android中组件之间的解耦提供了很好的解决方案。Dagger2已经在越来越多的开源项目中被使用,其已经发展成为未来的一个技术趋势。
MVP设计模式大家也非常熟悉,其概念不在此处赘述。MVP的实现方式非常多样,和Dagger2相结合正式本文要论述和讨论的。

MVP模式将原来一个Activity可以解决的问题,分成了M、V、P三部分,这在项目中会产品大量的类,如果利用Dagger2来进行这些类的有效管理,是本文思考的问题。本文将从Dagger2基础论述,MVP的实现方式以及Dagger2和MVP的结合,三个方面来讨论,希望对大家有用。

Dagger2 基础

Dagger2主要基于注解来实现,刚接触Dagger2的时候都会有这样的困惑:

  • Inject,Component,Module,Provides,Scope,Qualifier是什么鬼?
  • Dagger2如何应用到项目中?

Inject
Inject,即注入,该注解标示地方表示需要通过DI框架来注入实例。Inject有三种方式,分别是Constructor injection、Fields injection、Methods injection。申明了Inject之后,会从注入框架中去查找需要注入的类实例,然后注入进来,也就是通过Component去查找。这三种注入的表现形式如下:

public class SolutionCreatePresenterImpl implements SolutionCreatePresenter {...  @Inject  public SolutionCreatePresenterImpl(Activity activity, StudioApiService apiService, RxBus rxBus, DataCenter dataCenter) {  this.activity = activity;   this.apiService = apiService;   this.rxBus = rxBus;   ... }}
public class SolutionCreateActivity extends BasePresenterActivity implements SolutionCreateView { @Inject SolutionCreatePresenter solutionCreatePresenter; @Inject RxBus rxBus; @Inject DataCenter dataCenter; @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState  {  super.onCreate(savedInstanceState);  component().inject(this);  ... }}
public class LoginActivityPresenter {   private LoginActivity loginActivity;   @Inject  public LoginActivityPresenter(LoginActivity loginActivity) {    this.loginActivity = loginActivity;  }  @Inject  public void enableWatches(Watches watches) {    watches.register(this);  //Watches instance required fully constructed LoginActivityPresenter  }}

Component
Component 要解决的问题就是Inject的实例从哪里来,所以它承担的就是一个连接器的作用。Component需要引用到目标类的实例,Component会查找目标类中用Inject注解标注的属性,查找到相应的属性后会接着查找该属性对应的用Inject标注的构造函数(这时候就发生联系了),剩下的工作就是初始化该属性的实例并把实例进行赋值。

Module
Module 是类实例提供的工厂模式,Module里面的方法基本都是创建类实例的方法。这样,Dagger2中就有2个维度可以创建类实例:

  • 通过用Inject注解标注的构造函数来创建(以下简称Inject维度)
  • 通过工厂模式的Module来创建(以下简称Module维度)

Provides
Provides 用在Module中,声明方法可以返回dependencies,也就是方法可以提供被注入的类实例。

@Modulepublic class NetApiModule {  @Provides  @Singleton  StudioApiService provideStudioApiService(RestApi restApi) {    return restApi.retrofitStudio(GlobalConfig.STUDIO_API_BASE_URL).create(StudioApiService.class);  }  @Provides  @Singleton  RxBus provideRxBus() {    return RxBus.getInstance();  }}

如下所以,NetApiModule提供了StudioAPIService、RxBus类的依赖实例。

Scope
Dagger2中Scope关心的问题就是类实例的生命周期,@ApplicationScope 是希望类实例和Application一样,也就是唯一单例;@ActivityScope则希望类实例和Activity生命周期一致。如此,Scope就有“local singletons” 的概念了。举个例子:AppComponent 用 @Singleton 进行标识,由于AppComponent只在Application中创建一次,这就保证了AppComponent所引用的ApplicationModule 中用 @Singleton 标识的类都是单例;同样的,ActivityComponent 用 @ActivityScope进行标识,每次创建新的Activity都会创建一个ActivityComponent,这就是的ActivityComponent所引用的ActivityModule中用@ActivityScope 标识的类都具有和Activity一样的生命周期。
在后面的讲解中,读者可以去体会Scope标识后的,类实例和Component的相互依赖关系。

Qualifier
如果类实例创建有多种相同的方式,就需要通过标签tag来区分之,并在注入的时候通过标签来区分。

Dagger2如何应用到项目中
Dagger2的使用首先要摆脱传统的类创建模式,即摒弃各种new的使用方式,要有类仓库的概念,所有类的引用采用Inject来注入。其次,要组织好Component,Component是连接器,提供了不同的类注入,有为Application提供的,有为Activity提供的,按照个人经验来讲,Component的划分有一下规则:

  • 要有全局Component, 负责管理整个app的全局类实例, 这些类基本都是单例模式,一般都用@Singleton标识
  • 每个页面对应一个Component,即每个Activity 和 Fragment,谷歌官方Dagger2示例是采用这种方式,这种方式有个不好地方,Component太多
  • 将页面依赖的类抽出,公用一个Component,有特殊需要的时候继承该公有Component
  • 不同类型的Component及Module,采用Scope进行区分

参考链接:
Miroslaw Stanek: http://frogermcs.github.io/dependency-injection-with-dagger-2-the-api/
Google: https://github.com/google/dagger

MVP 实现探讨

MVP的实现方式很多,这里介绍两种:

  1. 以Activity和Fragment作为View(视图层),Presenter管理业务逻辑;
  2. 使用Activity和Fragment作为presenters,View单独抽出处理视图相关。

使用Activity和Fragment作为View

这种方式是比较传统的实现方式,也比较符合我们的习惯,因为之前都是在Activity里面处理View相关,改用MVP模式也只是也Activity里面的业务逻辑抽成Presenter。这里要提的是Google官方MVP也是采用这种方式。
下面说一下我的实现方式,将Presenter和View都抽出一个基类接口,每个Activity和Fragment都分别定义对应的Presenter和View接口,并集成基类接口。具体实现如下:

基类接口View

public interface LoadDataView {  /**  * Show a view with a progress_upload bar indicating a loading process.  */  void showLoading();  /**  * Hide a loading view.  */  void hideLoading();  /**  * Show a retry view in case of an error when retrieving data.  */  void showRetry();  /**  * Hide a retry view shown if there was an error when retrieving data.  */  void hideRetry();  /**  * Show an error message  *  * @param message A string representing an error.  */  void showError(String message);  /**  * Get a {@link Context}.  */  Context context();}

基类接口Presenter

public interface Presenter<T> {  /**  * Method that control the lifecycle of the view. It should be called in the view's  * (Activity or Fragment) onStart() method.  */  void start();  /**  * Method that control the lifecycle of the view. It should be called in the view's  * (Activity or Fragment) onDestroy() method.  */  void destroy();  void setView(T view);}

我们以本文之前用到的SolutionCreatePresenter来讲述具体页面的Presenter定义。

// 接口定义public interface SolutionCreatePresenter extends    Presenter<SolutionCreateView>, View.OnClickListener, View.OnFocusChangeListener {  Solution getSolution();  void setSolution(Solution solution);}// 接口实现public class SolutionCreatePresenterImpl implements SolutionCreatePresenter { ...  @Inject  public SolutionCreatePresenterImpl(Activity activity, StudioApiService apiService, RxBus rxBus, DataCenter dataCenter) {    this.activity = activity;    this.apiService = apiService;    this.rxBus = rxBus;    ...  }  @Override  public void setView(SolutionCreateView view) {   this.solutionCreateView = view;  }  // 继续添加其他 override 方法}

SolutionCreateActivity 见本文前面的代码,需要implements SoluionCreateView, 并且Inject SolutionPresenter.

使用Activity和Fragment作为Presenters
为何要采用这种方式呢,基于两点来考虑:

  1. Activity Fragment本身就有生命周期的管理,这种管理类似于业务逻辑,所以要归为Presenter;
  2. Activity Fragment生命周期变化时,会带来业务逻辑的变化,直接作为Presenter,可以避免业务逻辑的复杂。

个中意味,读者自己体验体验,当自己有需求时,可以根据这两种方式自由变换。

参考链接:
一种在android中实现MVP模式的新思路:
https://github.com/hehonghui/android-tech-frontier/tree/master/androidweekly/一种在android中实现MVP模式的新思路
Google Samples:
https://github.com/googlesamples/android-architecture

Dagger2 与 MVP 的结合

这一部分,重点介绍Component 和 Module的设计,目的在于更好适应MVP模式。
基本思路:

  1. 全局Component通过AppComponent进行管理,大多设置单例模式;
  2. 将Activity和Fragment Component中通用的抽出,为BaseViewComponent;
  3. 上层PresenterComponent继承BaseViewComponet,DataBandingComponent只能继承android.databinding.DataBindingComponent,但可以将BaseViewModule 包含进来。其他Component使用时可以继承BaseViewComponent。

架构图如下:


dagger-mvp.jpeg

核心代码如下:

AppComponent

@Singleton@Component(modules = {AppModule.class, AppManageModule.class})public interface AppComponent {  void inject(DajiaApplication app);}

AppModule

@Module(includes = NetApiModule.class)public class AppModule {  private final Application application;  public AppModule(Application app) {    application = app;  }  @Provides  @Singleton  Application provideApplication() {    return application;  }  @Provides  @Singleton  Context provideContext() {    return application;  }}

BaseViewComponent

@Scope@Retention(RUNTIME)public @interface PerView { }
@PerView@Component(dependencies = AppComponent.class, modules = BaseViewModule.class)public interface BaseViewComponent {  Activity activity();  void inject(AbstractActivity activity);  void inject(BaseFragment fragment);}

BaseViewModule

@Modulepublic class BaseViewModule {  private final Activity activity;  public BaseViewModule(Activity activity) {    this.activity = activity;  }  @Provides  @PerView  Activity provideActivity() {    return this.activity;  }}

PresenterComponent

@PerView@Component(dependencies = AppComponent.class, modules = {    BaseViewModule.class, PresenterModule.class})public interface PresenterComponent extends BaseViewComponent { void inject(SolutionCreateActivity activity);}

PresenterModule

@Modulepublic class PresenterModule { @Provides  @PerView  SolutionCreatePresenter provideSolutionCreatePresenter(SolutionCreatePresenterImpl presenter) {    return presenter;  }}

此外,可以将Activity和Fragment中公用的东西抽出 AbstractActivity、BaseActivity、BasePresenterActiviy以及BaseFragment,并在这些类中初始化对应的Component。以AbstractActivity 和 BasePresenterActiviy为例:

public class AbstractActivity extends AppCompatActivity {  private BaseViewComponent mBaseViewComponent;  public BaseViewComponent component() {    if (mBaseViewComponent == null) {      mBaseViewComponent = DaggerBaseViewComponent.builder()          .appComponent(DajiaApplication.getInstance().component())          .baseViewModule(new BaseViewModule(this))          .build();    }    return mBaseViewComponent;  }}
public class BasePresenterActivity extends BaseActivity {  private PresenterComponent presenterComponent;  public PresenterComponent component() {    if (presenterComponent == null) {      presenterComponent = DaggerPresenterComponent.builder()          .appComponent(DajiaApplication.getInstance().component())          .baseViewModule(new BaseViewModule(this))          .presenterModule(new PresenterModule()).build();    }    return presenterComponent;  }}

如此,在使用MVP时,遵循如下步骤:

  1. 创建新的Presenter 继承 BasePresenter;
  2. 实现该接口,PresenterImpl;
  3. 在PresenterModule中提供PresenterImpl的注入方法;
  4. 创建新的View,继承BaseView;
  5. 创建新的页面Activity或者Frament,implements View;
  6. PresenterComponent添加inject方法,在新的页面中inject Presenter。

结语

本文想要系统的描述Dagger2和MVP的架构思想,但是文笔有限,行文中还是感觉有很多描述不到位的地方,希望读者在阅读时把握核心思想,而不局限于具体的实现步骤。

原创粉丝点击