📜  带有示例的Android中的MVP(模型视图演示者)架构模式

📅  最后修改于: 2021-05-10 15:04:26             🧑  作者: Mango

在Android开发的初始阶段,学习者会以最终创建一个MainActivity类的方式编写代码,该类包含应用程序的所有实现逻辑(现实世界中的业务逻辑)。这种应用程序开发方法导致Android活动与UI和应用程序数据处理机制紧密相关。此外,这在维护和扩展此类移动应用程序方面造成了困难。为了避免在可维护性,可读性,可伸缩性和应用程序重构方面出现此类问题,开发人员更喜欢定义分隔良好的代码层。通过应用软件体系结构模式,人们可以组织应用程序的代码以分离关注点。 MVP(模型-视图-演示者)体系结构是最受欢迎的体系结构模式之一,可有效地组织项目。

MVP(模型-视图-演示者)作为传统MVC(模型-视图-控制器)体系结构模式的替代品出现。使用MVC作为软件体系结构,开发人员最终面临以下困难:

  • 大多数核心业务逻辑都驻留在Controller中。在应用程序的生命周期中,此文件会变得越来越大,并且变得难以维护代码。
  • 由于UI和数据访问机制紧密耦合,因此ControllerView层属于同一活动或片段。这会导致在更改应用程序功能时出现问题。
  • 由于要测试的大部分零件都需要Android SDK组件,因此很难对不同层进行单元测试。

MVP模式克服了MVC的这些挑战,并提供了一种构造项目代码的简便方法。 MVP之所以被广泛接受,是因为它提供了模块化,可测试性以及更干净和可维护的代码库。它由以下三个组件组成:

  • 型号:用于存储数据的层。它负责处理域逻辑(实际业务规则)以及与数据库和网络层的通信。
  • 查看: UI(用户界面)层。它提供数据的可视化并跟踪用户的操作,以便通知Presenter。
  • 演示者:从模型中获取数据并应用UI逻辑来决定要显示的内容。它管理视图的状态,并根据用户从视图中输入的通知执行操作。

Android中的MVP(模型-视图-演示者)架构模式

MVP架构的要点

  1. View-Presenter和Presenter-Model之间的通信是通过接口(也称为Contract)进行的
  2. 一个Presenter类一次管理一个View,即Presenter和View之间存在一对一的关系。
  3. Model和View类不了解彼此的存在。

MVP架构示例

为了展示MVP架构模式在项目上的实现,这里是一个单活动android应用程序的示例。通过从模型中随机选择,应用程序将在View(Activity)上显示一些字符串 Presenter类的作用是使应用程序的业务逻辑远离活动。以下是此android应用程序的完整分步实施。请注意,我们将同时使用JavaKotlin语言来实现该项目。

步骤1:创建一个新项目

  1. 单击文件,然后单击新建=>新建项目。
  2. 选择清空活动
  3. 选择语言作为Java/科特林
  4. 根据需要选择最小的SDK。

步骤2:修改String.xml文件

此文件中列出了活动中使用的所有字符串。

XML

    GfG | MVP Architecture
    Display Next Course
    MVP Architecture Pattern
    GeeksforGeeks Computer Science Online Courses
    Course Description


XML


  
    
    
  
    
    
  
    
    
  
    
    


Java
public interface Contract {
    interface View {
        // method to display progress bar
        // when next random course details
        // is being fetched
        void showProgress();
  
        // method to hide progress bar
        // when next random course details
        // is being fetched
        void hideProgress();
  
        // method to set random
        // text on the TextView
        void setString(String string);
    }
  
    interface Model {
  
        // nested interface to be
        interface OnFinishedListener {
            // function to be called
            // once the Handler of Model class
            // completes its execution
            void onFinished(String string);
        }
  
        void getNextCourse(Contract.Model.OnFinishedListener onFinishedListener);
    }
  
    interface Presenter {
  
        // method to be called when
        // the button is clicked
        void onButtonClick();
  
        // method to destroy
        // lifecycle of MainActivity
        void onDestroy();
    }
}


Kotlin
interface Contract {
    interface View {
        // method to display progress bar
        // when next random course details
        // is being fetched
        fun showProgress()
  
        // method to hide progress bar
        // when next random course details
        // is being fetched
        fun hideProgress()
  
        // method to set random
        // text on the TextView
        fun setString(string: String?)
    }
  
    interface Model {
        // nested interface to be
        interface OnFinishedListener {
            // function to be called
            // once the Handler of Model class
            // completes its execution
            fun onFinished(string: String?)
        }
  
        fun getNextCourse(onFinishedListener: OnFinishedListener?)
    }
  
    interface Presenter {
        // method to be called when
        // the button is clicked
        fun onButtonClick()
  
        // method to destroy
        // lifecycle of MainActivity
        fun onDestroy()
    }
}


Java
import android.os.Handler;
  
import java.util.Arrays;
import java.util.List;
import java.util.Random;
  
public class Model implements Contract.Model {
  
    // array list of strings from which
    // random strings will be selected
    // to display in the activity
    private List arrayList = Arrays.asList(
            "DSA Self Paced: Master the basics of Data Structures and Algorithms to solve complex problems efficiently. ",
            "Placement 100: This course will guide you for placement with theory,lecture videos, weekly assignments " +
                    "contests and doubt assistance.",
            "Amazon SDE Test Series: Test your skill & give the final touch to your preparation before applying for " +
                    "product based against like Amazon, Microsoft, etc.",
            "Complete Interview Preparation: Cover all the important concepts and topics required for the interviews. " +
                    "Get placement ready before the interviews begin",
            "Low Level Design for SDE 1 Interview: Learn Object-oriented Analysis and Design to prepare for " +
                    "SDE 1 Interviews in top companies"
    );
  
    @Override
    // this method will invoke when
    // user clicks on the button
    // and it will take a delay of
    // 1200 milliseconds to display next course detail
    public void getNextCourse(final OnFinishedListener listener) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                listener.onFinished(getRandomString());
            }
        }, 1200);
    }
  
    // method to select random
    // string from the list of strings
    private String getRandomString() {
        Random random = new Random();
        int index = random.nextInt(arrayList.size());
        return arrayList.get(index);
    }
}


Kotlin
import android.os.Handler
import java.util.*
  
  
class Model : Contract.Model {
    // array list of strings from which
    // random strings will be selected
    // to display in the activity
    private val arrayList =
        Arrays.asList(
            "DSA Self Paced: Master the basics of Data Structures and Algorithms to solve complex problems efficiently. ",
            "Placement 100: This course will guide you for placement with theory,lecture videos, weekly assignments " +
                    "contests and doubt assistance.",
            "Amazon SDE Test Series: Test your skill & give the final touch to your preparation before applying for " +
                    "product based against like Amazon, Microsoft, etc.",
            "Complete Interview Preparation: Cover all the important concepts and topics required for the interviews. " +
                    "Get placement ready before the interviews begin",
            "Low Level Design for SDE 1 Interview: Learn Object-oriented Analysis and Design to prepare for " +
                    "SDE 1 Interviews in top companies"
        )
  
    // this method will invoke when
    // user clicks on the button
    // and it will take a delay of
    // 1200 milliseconds to display next course detail
    override fun getNextCourse(onFinishedListener: Contract.Model.OnFinishedListener?) {
        Handler().postDelayed({ onFinishedListener!!.onFinished(getRandomString) }, 1200)
    }
  
  
    // method to select random
    // string from the list of strings
    private val getRandomString: String
        private get() {
            val random = Random()
            val index = random.nextInt(arrayList.size)
            return arrayList[index]
        }
}


Java
public class Presenter implements Contract.Presenter, Contract.Model.OnFinishedListener {
  
    // creating object of View Interface
    private Contract.View mainView;
  
    // creating object of Model Interface
    private Contract.Model model;
  
    // instantiating the objects of View and Model Interface
    public Presenter(Contract.View mainView, Contract.Model model) {
        this.mainView = mainView;
        this.model = model;
    }
  
    @Override
    // operations to be performed
    // on button click
    public void onButtonClick() {
        if (mainView != null) {
            mainView.showProgress();
        }
        model.getNextCourse(this);
    }
  
    @Override
    public void onDestroy() {
        mainView = null;
    }
  
    @Override
    // method to return the string
    // which will be displayed in the
    // Course Detail TextView
    public void onFinished(String string) {
        if (mainView != null) {
            mainView.setString(string);
            mainView.hideProgress();
        }
    }
}


Kotlin
// instantiating the objects of View and Model Interface
// creating object of View Interface
// creating object of Model Interface
class Presenter(
    private var mainView: Contract.View?,
    private val model: Contract.Model) : Contract.Presenter,
    Contract.Model.OnFinishedListener {
  
    // operations to be performed
    // on button click
    override fun onButtonClick() {
        if (mainView != null) {
            mainView!!.showProgress()
        }
        model.getNextCourse(this)
    }
  
    override fun onDestroy() {
        mainView = null
    }
  
    // method to return the string
    // which will be displayed in the
    // Course Detail TextView
    override fun onFinished(string: String?) {
        if (mainView != null) {
            mainView!!.setString(string)
            mainView!!.hideProgress()
        }
    }
  
}


Java
import androidx.appcompat.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 Contract.View {
  
    // creating object of TextView class
    private TextView textView;
  
    // creating object of Button class
    private Button button;
  
    // creating object of ProgressBar class
    private ProgressBar progressBar;
  
    // creating object of Presenter interface in Contract
    Contract.Presenter presenter;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
  
        // assigning ID of the TextView
        textView = findViewById(R.id.textView);
  
        // assigning ID of the Button
        button = findViewById(R.id.button);
  
        // assigning ID of the ProgressBar
        progressBar = findViewById(R.id.progressBar);
  
        // instantiating object of Presenter Interface
        presenter = new Presenter(this, new Model());
  
        // operations to be performed when
        // user clicks the button
        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
    // method to display the Course Detail TextView
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
        textView.setVisibility(View.INVISIBLE);
    }
  
    @Override
    // method to hide the Course Detail TextView
    public void hideProgress() {
        progressBar.setVisibility(GONE);
        textView.setVisibility(View.VISIBLE);
    }
  
    @Override
    // method to set random string
    // in the Course Detail TextView
    public void setString(String string) {
        textView.setText(string);
    }
}


Kotlin
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
  
  
class MainActivity : AppCompatActivity(), Contract.View {
    // creating object of TextView class
    private var textView: TextView? = null
  
    // creating object of Button class
    private var button: Button? = null
  
    // creating object of ProgressBar class
    private var progressBar: ProgressBar? = null
  
    // creating object of Presenter interface in Contract
    var presenter: Presenter? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        // assigning ID of the TextView
        textView = findViewById(R.id.textView)
  
        // assigning ID of the Button
        button = findViewById(R.id.button)
  
        // assigning ID of the ProgressBar
        progressBar = findViewById(R.id.progressBar)
  
        // instantiating object of Presenter Interface
        presenter = Presenter(this, Model())
  
        // operations to be performed when
        // user clicks the button
        this.button!!.setOnClickListener(View.OnClickListener { presenter!!.onButtonClick() })
    }
  
    override fun onResume() {
        super.onResume()
    }
  
    override fun onDestroy() {
        super.onDestroy()
        presenter!!.onDestroy()
    }
  
    // method to display the Course Detail TextView
    override fun showProgress() {
        progressBar!!.visibility = View.VISIBLE
        textView!!.visibility = View.INVISIBLE
    }
  
    // method to hide the Course Detail TextView
    override fun hideProgress() {
        progressBar!!.visibility = View.GONE
        textView!!.visibility = View.VISIBLE
    }
  
    // method to set random string
    // in the Course Detail TextView
    override fun setString(string: String?) {
        textView!!.text = string
    }
}


步骤3:使用activity_main.xml文件

打开activity_main.xml文件,并添加一个Button,一个TextView以显示该字符串以及一个Progress Bar,以使应用程序具有动态感。以下是用于设计适当活动布局的代码。

XML格式



  
    
    
  
    
    
  
    
    
  
    
    

步骤4:为模型,视图和演示者定义合同接口文件

为了在View-Presenter和Presenter-Model之间建立通信,需要一个接口。该接口类将包含所有抽象方法,这些方法稍后将在View,Model和Presenter类中定义。

Java

public interface Contract {
    interface View {
        // method to display progress bar
        // when next random course details
        // is being fetched
        void showProgress();
  
        // method to hide progress bar
        // when next random course details
        // is being fetched
        void hideProgress();
  
        // method to set random
        // text on the TextView
        void setString(String string);
    }
  
    interface Model {
  
        // nested interface to be
        interface OnFinishedListener {
            // function to be called
            // once the Handler of Model class
            // completes its execution
            void onFinished(String string);
        }
  
        void getNextCourse(Contract.Model.OnFinishedListener onFinishedListener);
    }
  
    interface Presenter {
  
        // method to be called when
        // the button is clicked
        void onButtonClick();
  
        // method to destroy
        // lifecycle of MainActivity
        void onDestroy();
    }
}

科特林

interface Contract {
    interface View {
        // method to display progress bar
        // when next random course details
        // is being fetched
        fun showProgress()
  
        // method to hide progress bar
        // when next random course details
        // is being fetched
        fun hideProgress()
  
        // method to set random
        // text on the TextView
        fun setString(string: String?)
    }
  
    interface Model {
        // nested interface to be
        interface OnFinishedListener {
            // function to be called
            // once the Handler of Model class
            // completes its execution
            fun onFinished(string: String?)
        }
  
        fun getNextCourse(onFinishedListener: OnFinishedListener?)
    }
  
    interface Presenter {
        // method to be called when
        // the button is clicked
        fun onButtonClick()
  
        // method to destroy
        // lifecycle of MainActivity
        fun onDestroy()
    }
}

步骤5:创建模型类

创建一个名为Model的新类,以分隔所有字符串数据和获取这些数据的方法。该类将不知道View类的存在。

Java

import android.os.Handler;
  
import java.util.Arrays;
import java.util.List;
import java.util.Random;
  
public class Model implements Contract.Model {
  
    // array list of strings from which
    // random strings will be selected
    // to display in the activity
    private List arrayList = Arrays.asList(
            "DSA Self Paced: Master the basics of Data Structures and Algorithms to solve complex problems efficiently. ",
            "Placement 100: This course will guide you for placement with theory,lecture videos, weekly assignments " +
                    "contests and doubt assistance.",
            "Amazon SDE Test Series: Test your skill & give the final touch to your preparation before applying for " +
                    "product based against like Amazon, Microsoft, etc.",
            "Complete Interview Preparation: Cover all the important concepts and topics required for the interviews. " +
                    "Get placement ready before the interviews begin",
            "Low Level Design for SDE 1 Interview: Learn Object-oriented Analysis and Design to prepare for " +
                    "SDE 1 Interviews in top companies"
    );
  
    @Override
    // this method will invoke when
    // user clicks on the button
    // and it will take a delay of
    // 1200 milliseconds to display next course detail
    public void getNextCourse(final OnFinishedListener listener) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                listener.onFinished(getRandomString());
            }
        }, 1200);
    }
  
    // method to select random
    // string from the list of strings
    private String getRandomString() {
        Random random = new Random();
        int index = random.nextInt(arrayList.size());
        return arrayList.get(index);
    }
}

科特林

import android.os.Handler
import java.util.*
  
  
class Model : Contract.Model {
    // array list of strings from which
    // random strings will be selected
    // to display in the activity
    private val arrayList =
        Arrays.asList(
            "DSA Self Paced: Master the basics of Data Structures and Algorithms to solve complex problems efficiently. ",
            "Placement 100: This course will guide you for placement with theory,lecture videos, weekly assignments " +
                    "contests and doubt assistance.",
            "Amazon SDE Test Series: Test your skill & give the final touch to your preparation before applying for " +
                    "product based against like Amazon, Microsoft, etc.",
            "Complete Interview Preparation: Cover all the important concepts and topics required for the interviews. " +
                    "Get placement ready before the interviews begin",
            "Low Level Design for SDE 1 Interview: Learn Object-oriented Analysis and Design to prepare for " +
                    "SDE 1 Interviews in top companies"
        )
  
    // this method will invoke when
    // user clicks on the button
    // and it will take a delay of
    // 1200 milliseconds to display next course detail
    override fun getNextCourse(onFinishedListener: Contract.Model.OnFinishedListener?) {
        Handler().postDelayed({ onFinishedListener!!.onFinished(getRandomString) }, 1200)
    }
  
  
    // method to select random
    // string from the list of strings
    private val getRandomString: String
        private get() {
            val random = Random()
            val index = random.nextInt(arrayList.size)
            return arrayList[index]
        }
}

步骤6:创建Presenter类

此类的方法包含核心业务逻辑,这些逻辑将决定要显示什么以及如何显示。它触发View类对UI进行必要的更改。

Java

public class Presenter implements Contract.Presenter, Contract.Model.OnFinishedListener {
  
    // creating object of View Interface
    private Contract.View mainView;
  
    // creating object of Model Interface
    private Contract.Model model;
  
    // instantiating the objects of View and Model Interface
    public Presenter(Contract.View mainView, Contract.Model model) {
        this.mainView = mainView;
        this.model = model;
    }
  
    @Override
    // operations to be performed
    // on button click
    public void onButtonClick() {
        if (mainView != null) {
            mainView.showProgress();
        }
        model.getNextCourse(this);
    }
  
    @Override
    public void onDestroy() {
        mainView = null;
    }
  
    @Override
    // method to return the string
    // which will be displayed in the
    // Course Detail TextView
    public void onFinished(String string) {
        if (mainView != null) {
            mainView.setString(string);
            mainView.hideProgress();
        }
    }
}

科特林

// instantiating the objects of View and Model Interface
// creating object of View Interface
// creating object of Model Interface
class Presenter(
    private var mainView: Contract.View?,
    private val model: Contract.Model) : Contract.Presenter,
    Contract.Model.OnFinishedListener {
  
    // operations to be performed
    // on button click
    override fun onButtonClick() {
        if (mainView != null) {
            mainView!!.showProgress()
        }
        model.getNextCourse(this)
    }
  
    override fun onDestroy() {
        mainView = null
    }
  
    // method to return the string
    // which will be displayed in the
    // Course Detail TextView
    override fun onFinished(string: String?) {
        if (mainView != null) {
            mainView!!.setString(string)
            mainView!!.hideProgress()
        }
    }
  
}

步骤7:在MainActivity文件中定义View的功能

View类负责根据Presenter层触发的更改来更新UI。 View将使用模型提供的数据,并在活动中进行适当的更改。

Java

import androidx.appcompat.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 Contract.View {
  
    // creating object of TextView class
    private TextView textView;
  
    // creating object of Button class
    private Button button;
  
    // creating object of ProgressBar class
    private ProgressBar progressBar;
  
    // creating object of Presenter interface in Contract
    Contract.Presenter presenter;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
  
        // assigning ID of the TextView
        textView = findViewById(R.id.textView);
  
        // assigning ID of the Button
        button = findViewById(R.id.button);
  
        // assigning ID of the ProgressBar
        progressBar = findViewById(R.id.progressBar);
  
        // instantiating object of Presenter Interface
        presenter = new Presenter(this, new Model());
  
        // operations to be performed when
        // user clicks the button
        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
    // method to display the Course Detail TextView
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
        textView.setVisibility(View.INVISIBLE);
    }
  
    @Override
    // method to hide the Course Detail TextView
    public void hideProgress() {
        progressBar.setVisibility(GONE);
        textView.setVisibility(View.VISIBLE);
    }
  
    @Override
    // method to set random string
    // in the Course Detail TextView
    public void setString(String string) {
        textView.setText(string);
    }
}

科特林

import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
  
  
class MainActivity : AppCompatActivity(), Contract.View {
    // creating object of TextView class
    private var textView: TextView? = null
  
    // creating object of Button class
    private var button: Button? = null
  
    // creating object of ProgressBar class
    private var progressBar: ProgressBar? = null
  
    // creating object of Presenter interface in Contract
    var presenter: Presenter? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        // assigning ID of the TextView
        textView = findViewById(R.id.textView)
  
        // assigning ID of the Button
        button = findViewById(R.id.button)
  
        // assigning ID of the ProgressBar
        progressBar = findViewById(R.id.progressBar)
  
        // instantiating object of Presenter Interface
        presenter = Presenter(this, Model())
  
        // operations to be performed when
        // user clicks the button
        this.button!!.setOnClickListener(View.OnClickListener { presenter!!.onButtonClick() })
    }
  
    override fun onResume() {
        super.onResume()
    }
  
    override fun onDestroy() {
        super.onDestroy()
        presenter!!.onDestroy()
    }
  
    // method to display the Course Detail TextView
    override fun showProgress() {
        progressBar!!.visibility = View.VISIBLE
        textView!!.visibility = View.INVISIBLE
    }
  
    // method to hide the Course Detail TextView
    override fun hideProgress() {
        progressBar!!.visibility = View.GONE
        textView!!.visibility = View.VISIBLE
    }
  
    // method to set random string
    // in the Course Detail TextView
    override fun setString(string: String?) {
        textView!!.text = string
    }
}

输出

  MVP架构的优势

  • android组件中没有概念关系
  • 由于应用程序的模型,视图和演示者层是分离的,因此易于代码维护和测试。

MVP架构的缺点

  • 如果开发人员不遵循单一职责原则来破坏代码,则Presenter层倾向于扩展为庞大的全知类。
想要一个节奏更快,更具竞争性的环境来学习Android的基础知识吗?
单击此处,前往由我们的专家精心策划的指南,以使您立即做好行业准备!