Android MVP Framework Learning

Keywords: Android Java xml github

What is the MVP framework

Android has been in development for several years, and has recently come into contact with the MVP model for Android development, which is the abbreviation Model, View, Presenter.If someone who has experience in project development has more and more project functions and more complex logic, the code will be written more and more chaotically, and it will be difficult to see if they are chaotic (I have deep experience in doing projects).Because Android's previous development model was similar to the MVC framework, XML layout was View layer, data entity was Model layer, Activity was Controller layer, when there was more logic in an activity, it was easy to get over a thousand lines of code in the activity, and a lot of code for logic and View interaction was in the activity.Such a development model causes View and Controller to be tightly coupled, not only writing code and looking confused, but also very inconvenient for later maintenance and expansion.The launch of MVP development mode not only effectively solves the problem of coupling between View and Controller, but also the Activity is treated as View thoroughly. The interaction between model and View is accomplished by interface between Presenter and View, the code level is also clear, and later expansion and modification are also convenient.

Raise a chestnut

It's useless to say that, for example, it's the easiest to understand. Here's a Demo I wrote while learning the MVP framework. I hope I wrote it correctly (hopefully God pointed it out if I made a mistake)
This project is named Test MVP. Its main function is to download files, have a download button on the page, and a progress bar. The following is the catalog structure of the project:

The code for the layout file below:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.test.testmvp.MainActivity">

    <Button
        android:id="@+id/download_btn"
        android:onClick="handleDownloadBtnClick"
        android:text="CLICK TO DOWNLOAD"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ProgressBar
        android:layout_below="@+id/download_btn"
        android:layout_marginTop="20dp"
        android:visibility="gone"
        android:id="@+id/progress_bar"
        android:max="100"
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

The layout file is simple, so there's no need to explain it.
In the view package, it is mainly an interface file IDownloadView.java for View, with the following code:

package com.test.testmvp.view;

/**
 * Created by yubo on 2017/3/26.
 * view Layer's Abstract interface, which requires Activity to implement (so Activity is treated as a view)
 */

public interface IDownloadView {

    void initViews();
    void showProgressBar();
    void hideProgressBar();
    void updateProgressBar(int progress);
    void showToast(String msg);

}

This View's interface file defines a series of ways to operate on View. Since Activity is treated as View completely in MVP mode, our Activity needs to implement this IDownloadView interface. For the specific implementation of Activity, put the code behind it.
Following is the presenter package. Since this Demo is mainly a download logic, the IDownloadPresenter interface is mainly a download method with the following code:

package com.test.testmvp.presenter;

import com.test.testmvp.listener.OnDownloadListener;

import java.io.File;

/**
 * Created by yubo on 2017/3/26.
 */

public interface IDownloadPresenter {

    //Logic for downloading files, implemented by implementation classes, and listener for monitoring the download process
    void startDownload(String urlStr, File saveFile, OnDownloadListener listener);

}

The startDownload method of the downloaded IDownloadPresenter interface has three parameters: the first is the download address, the second is the download file, the third is the download listener, and the OnDownloadListener code is as follows:

package com.test.testmvp.listener;

/**
 * Created by yubo on 2017/3/26.
 * Download monitor to handle different stages of download
 */

public interface OnDownloadListener {

    void onStartDownload();
    void onProgressUpdate(int progress);
    void onDownloadSuccess();
    void onDownloadError(Exception e);

}

It is mainly to monitor the four processes of starting download, download progress, download completion, download error.With the IDownloadPresenter interface, you also need an implementation class, DownloadPresenterImpl, which has the following code:

package com.test.testmvp.presenter;

import android.app.Activity;
import android.content.Context;

import com.test.testmvp.listener.OnDownloadListener;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

/**
 * Created by yubo on 2017/3/26.
 */

public class DownloadPresenterImpl implements IDownloadPresenter {

    private Context mContext;

    public DownloadPresenterImpl(Context context) {
        this.mContext = context;
    }

    @Override
    public void startDownload(final String urlStr, final File saveFile, final OnDownloadListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("OnDownloadListener should not be null!");
        }
        listener.onStartDownload();
        new Thread(new Runnable() {
            @Override
            public void run() {
                InputStream inputStream = null;
                FileOutputStream fileOutputStream = null;
                try {
                    URL url = new URL(urlStr);
                    URLConnection conn = url.openConnection();
                    inputStream = conn.getInputStream();
                    fileOutputStream = new FileOutputStream(saveFile);
                    int totalSize = inputStream.available();
                    int hasDownload = 0;
                    int hasRead = 0;
                    byte[] buf = new byte[512];
                    while ((hasRead = inputStream.read(buf)) > 0) {
                        fileOutputStream.write(buf, 0, hasRead);
                        hasDownload += hasRead;
                        final int progress = (int) (hasDownload * 100.0f / totalSize);
                        listener.onProgressUpdate(progress);
                        ((Activity)mContext).runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                listener.onProgressUpdate(progress);
                            }
                        });
                    }
                    ((Activity)mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            listener.onDownloadSuccess();
                        }
                    });
                } catch (final MalformedURLException e) {
                    e.printStackTrace();
                    ((Activity)mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            listener.onDownloadError(e);
                        }
                    });
                } catch (final IOException e) {
                    e.printStackTrace();
                    ((Activity)mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            listener.onDownloadError(e);
                        }
                    });
                } finally {
                    if (inputStream != null) {
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }).start();
    }

}

The decoupling of logic and views is done by Presenter (if you use a previous MVC-like development model, all the code in Presenter is written into Activity).There is also a DownloadFileModel class that simply encapsulates the downloaded file with the following code:

package com.test.testmvp.model;

import java.io.File;
import java.io.IOException;

/**
 * Created by yubo on 2017/3/26.
 */

public class DownloadFileModel {

    private String fileName;
    private String fileSavePath;
    private File file;

    public DownloadFileModel(String fileName, String fileSavePath) {
        this.fileName = fileName;
        this.fileSavePath = fileSavePath;
        this.file = new File(fileSavePath + File.separator + fileName);
        if (this.file.exists()) {
            this.file.delete();
        }
        try {
            this.file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public File getFile() {
        return this.file;
    }

    public String getFilePath() {
        return this.file.getAbsolutePath();
    }

}

The following is the code for MainActivity:

package com.test.testmvp;

import android.os.Environment;
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.Toast;

import com.test.testmvp.listener.OnDownloadListener;
import com.test.testmvp.model.DownloadFileModel;
import com.test.testmvp.presenter.DownloadPresenterImpl;
import com.test.testmvp.view.IDownloadView;

public class MainActivity extends AppCompatActivity implements IDownloadView {

    //File download address, may be invalid
    private static final String DOWNLOAD_FILE_URL = "http://do.xiazaiba.com/route.php?ct=stat&ac=stat&g=aHR0cDovL2FwcHMud2FuZG91amlhLmNvbS9yZWRpcmVjdD9zaWduYXR1cmU9YzkxNGQ2OSZ1cmw9aHR0cCUzQSUyRiUyRmRvd25sb2FkLmVvZW1hcmtldC5jb20lMkZhcHAlM0ZjaGFubmVsX2lkJTNEMTAwJTI2Y2xpZW50X2lkJTI2aWQlM0QyNDQwMDMmcG49Y29tLnVzb2Z0LmFwcC51aSZtZDU9OTI4MWIxZjU1YmM1YzExNmYyMThmZjYwY2FhZWEwMmImYXBraWQ9MTQyODY1ODgmdmM9MTEmc2l6ZT0zNDE0MTEw";
    //Downloaded File Name
    private static final String DOWNLOAD_FILE_NAME = "test.apk";
    //Downloaded File Storage Path
    private static final String DOWNLOAD_FILE_SAVE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();

    //Progress bar control
    private ProgressBar mProgressBar;

    //Processing downloaded Packager
    private DownloadPresenterImpl downloadPresenter;
    //Downloaded File Model
    private DownloadFileModel downloadFileModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        downloadFileModel = new DownloadFileModel(DOWNLOAD_FILE_NAME, DOWNLOAD_FILE_SAVE_PATH);
        downloadPresenter = new DownloadPresenterImpl(this);
    }

    @Override
    public void initViews() {
        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
    }

    //Handle Download Button Click Event
    public void handleDownloadBtnClick(View view) {
        downloadPresenter.startDownload(DOWNLOAD_FILE_URL, downloadFileModel.getFile(), new OnDownloadListener() {
            @Override
            public void onStartDownload() {
                showProgressBar();
            }

            @Override
            public void onProgressUpdate(int progress) {
                updateProgressBar(progress);
            }

            @Override
            public void onDownloadSuccess() {
                hideProgressBar();
                showToast("download success, path: " + downloadFileModel.getFilePath());
            }

            @Override
            public void onDownloadError(Exception e) {
                hideProgressBar();
                showToast(e.getMessage());
            }
        });
    }

    @Override
    public void showProgressBar() {
        mProgressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgressBar() {
        mProgressBar.setVisibility(View.GONE);
    }

    @Override
    public void updateProgressBar(int progress) {
        mProgressBar.setProgress(progress);
    }

    @Override
    public void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

You can see that Activity implements the IDownloadView interface and implements the methods in the interface. Activity holds a reference to Presenter and a reference to Model. Activity is viewed as View. When a button hits an event, it calls the startDownload method of Presenter to process business logic. Updates to the view areIn the callback interface of the startDownload method, the method calling the IDownloadView interface accomplishes this without direct interaction between logic and view, which achieves the purpose of low coupling.
OK, this is the end of the whole learning notes. I hope my understanding is correct and I also learned by referring to the blog god's blog post. Here's the blog god's article:
http://blog.csdn.net/lmj623565791/article/details/46596109

The code in this learning note has been uploaded to github, [click here to view the code] ( https://github.com/yubo725/Android-MVP-Demo)

Posted by ponsho on Mon, 15 Jul 2019 10:37:37 -0700