Android MVP

时间:2020-02-23 14:29:05  来源:igfitidea点击:

在本教程中,我们将讨论android MVP原理,并基于此原理开发应用程序。
从一开始,我们就通过在Activity中添加所有业务逻辑来开发应用程序。
在深入探讨新原理之前,让我们分析一下当前方法的弊端。

Android MVP

使用我们当前的方法,MainActivity类包含我们应用程序的所有实现逻辑。
我们一直在Activity类中使用从Retrofit回调到数据模型(SharedPref,POJO类)的各种内容。

最终,我们的活动成为了上帝的职业,并在可维护性,可读性,可伸缩性和重构已经肿的代码方面引起了问题。

由于实现逻辑与Android API紧密结合,因此单元测试变得越来越困难。
这是MVP(模型视图演示器)派上用场的地方。
它使我们能够编写干净,灵活的代码库,同时又可以轻松切换代码的任何部分,而无须麻烦。

模型视图演示者

Model View Presenter将我们的应用程序分为三层,即Model,View和Presenter。

  • 模型:处理我们应用程序的数据部分

  • 演示者:它充当连接模型和视图的桥梁。

  • 视图:负责按照演示者的指示使用相关数据布置视图

注意:View从不直接与Model通信。

Android MVP准则

  • Activity,Fragment和CustomView充当应用程序的View部分。

  • 演示者负责侦听用户交互(在View上)和模型更新(数据库,API),以及更新Model和View。

  • 通常,视图和演示者之间是一对一的关系。
    一个Presenter类一次管理一个View。

  • 需要定义和实现接口以在View-Presenter和Presenter-Model之间进行通信。

  • 演示者负责处理所有后台任务。
    在presenter类中必须避免使用Android SDK类。

  • View和Model类不能相互引用。

涵盖了MVP体系结构的理论之后,现在就构建一个Android MVP应用。

Android MVP示例应用程序项目结构

android mvp项目包含3个接口文件(也称为合同)。
Impl文件是实现接口的位置。

我们将创建一个活动应用程序,该应用程序将显示ArrayList中存在的引号列表中的随机引号。
我们将看到演示者如何设法使应用程序的业务逻辑远离活动类。

注意:Interactor是为从数据库,Web服务或者任何其他数据源中获取数据而构建的类。

Android MVP应用程式程式码

下面给出了" activity_main.xml"布局的代码。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context="com.theitroad.hellomvp.MainActivity">

  <TextView
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:text="Implementing MVP Pattern in this Application."
      app:layout_constraintBottom_toBottomOf="parent"
      android:padding="8dp"
      android:gravity="center"
      android:textAppearance="?android:attr/textAppearanceSearchResultTitle"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      android:id="@+id/textView" 

  <Button
      android:id="@+id/button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="GET NEXT QUOTE"
      android:layout_margin="@android:dimen/notification_large_icon_height"
      app:layout_constraintTop_toBottomOf="@+id/textView"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent" 

  <ProgressBar
      android:id="@+id/progressBar"
      style="?android:attr/progressBarStyleLarge"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      android:visibility="gone"
      app:layout_constraintBottom_toBottomOf="parent"

</android.support.constraint.ConstraintLayout>

下面给出了GetQuoteInteractor接口的代码。

package com.theitroad.hellomvp;

public interface GetQuoteInteractor {

  interface OnFinishedListener {
      void onFinished(String string);
  }

  void getNextQuote(OnFinishedListener listener);
}

它包含一个嵌套接口onFinishedListener。
我们将在下面的" GetQuoteInteractorImpl.java"中使用处理程序。
处理程序完成后,将触发上述" onFinished"方法。

package com.theitroad.hellomvp;

import android.os.Handler;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class GetQuoteInteractorImpl implements GetQuoteInteractor {

  private List<String> arrayList = Arrays.asList(
          "Be yourself. everyone else is already taken.",
          "A room without books is like a body without a soul.",
          "You only live once, but if you do it right, once is enough.",
          "Be the change that you wish to see in the world.",
          "If you tell the truth, you don't have to remember anything."
  );

  @Override
  public void getNextQuote(final OnFinishedListener listener) {
      new Handler().postDelayed(new Runnable() {
          @Override
          public void run() {
              listener.onFinished(getRandomString());
          }
      }, 1200);
  }

  private String getRandomString() {

      Random random = new Random();
      int index = random.nextInt(arrayList.size());

      return arrayList.get(index);
  }
}

MainView.java接口定义如下。

package com.theitroad.hellomvp;

public interface MainView {

  void showProgress();

  void hideProgress();

  void setQuote(String string);
}

当从GetQuoteInteractorImpl类获取下一个随机报价时,将使用showProgress()和hideProgress()来显示和隐藏progressBar。

setQuote()将在textView上设置随机字符串。

下面给出了MainPresenter.java接口的代码。

package com.theitroad.hellomvp;

public interface MainPresenter {

  void onButtonClick();

  void onDestroy();
}

MainPresenterImpl.java类在下面定义。

package com.theitroad.hellomvp;

public class MainPresenterImpl implements MainPresenter, GetQuoteInteractor.OnFinishedListener {

  private MainView mainView;
  private GetQuoteInteractor getQuoteInteractor;

  public MainPresenterImpl(MainView mainView, GetQuoteInteractor getQuoteInteractor) {
      this.mainView = mainView;
      this.getQuoteInteractor = getQuoteInteractor;
  }

  @Override
  public void onButtonClick() {
      if (mainView != null) {
          mainView.showProgress();
      }

      getQuoteInteractor.getNextQuote(this);
  }

  @Override
  public void onDestroy() {
      mainView = null;
  }

  @Override
  public void onFinished(String string) {
      if (mainView != null) {
          mainView.setQuote(string);
          mainView.hideProgress();
      }
  }
}

上面的类从GetQuoteInteractor实现Presenter和嵌套接口。
此外,它实例化了MainView和GetQuoteInteractor接口(分别为View和Model)。

单击按钮时,将在MainActivity类中触发onButtonClick方法,并在获取下一个随机引用时显示progressBar

onDestroy()方法将在MainActivity的生命周期方法onDestroy()中调用。

当处理程序在GetQuoteInteractorImpl中完成时,将调用onFinished()方法。
它返回的字符串将使用MainView的实例显示在TextView中。

下面给出了实现MainView接口的MainActivity.java的代码。

package com.theitroad.hellomvp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import static android.view.View.GONE;

public class MainActivity extends AppCompatActivity implements MainView {

  private TextView textView;
  private Button button;
  private ProgressBar progressBar;
  MainPresenter presenter;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      textView = (TextView) findViewById(R.id.textView);
      button = (Button) findViewById(R.id.button);
      progressBar = (ProgressBar) findViewById(R.id.progressBar);
      presenter = new MainPresenterImpl(this, new GetQuoteInteractorImpl());

      button.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              presenter.onButtonClick();
          }
      });
  }

  @Override
  protected void onResume() {
      super.onResume();
  }

  @Override
  protected void onDestroy() {
      super.onDestroy();
      presenter.onDestroy();
  }

  @Override
  public void showProgress() {
      progressBar.setVisibility(View.VISIBLE);
      textView.setVisibility(View.INVISIBLE);
  }

  @Override
  public void hideProgress() {
      progressBar.setVisibility(GONE);
      textView.setVisibility(View.VISIBLE);
  }

  @Override
  public void setQuote(String string) {
      textView.setText(string);
  }
}

showProgress()和hideProgress()方法还显示和隐藏textView,以防止当前文本和progressBar重叠。

注意:上面的类包含的内容现在没有业务逻辑,仅负责根据Presenter层触发的更改来更新UI。

上面的android MVP应用程序的输出如下。

注意:Google建议为Model View and Presenter保留一个合同界面文件。

因此,让我们将上面定义的接口合并为一个。

现在,项目结构如下所示:

下面给出了MainContract.java接口的代码。

package com.theitroad.hellomvp;

public interface MainContract {

  interface MainView {
      void showProgress();

      void hideProgress();

      void setQuote(String string);
  }

  interface GetQuoteInteractor {
      interface OnFinishedListener {
          void onFinished(String string);
      }

      void getNextQuote(OnFinishedListener onFinishedListener);
  }

  interface Presenter {
      void onButtonClick();

      void onDestroy();
  }
}