Android MVP(二)BaseMVP 基础框架设计

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主威威喵  |  博客主页https://blog.csdn.net/smile_running

 

MVP 架构系列文章:

Android MVP 架构(一)MVP 架构介绍与实战运用

Android MVP 架构(二)MVP 之 BaseMVP 基础框架设计

Android MVP 架构(三)MVP 内存泄漏分析与动态代理

Android MVP 架构(四)MVP 泛型 Model 的配置

Android MVP 架构(五)MVP 多个 Presenter 依赖注入

Android MVP 架构(六)MVP 之 BaseFragment 的封装

Android MVP 架构(七)MVP 之代理模式消除重复代码(结束)

源码地址:

github下载:MVPDemo

csdn 下载:MVPDemo

    上篇文章(Andoid MVP架构(一)MVP软件架构介绍与实战运用),我们讲述了如何构建一个基于 MVP 的 HelloWord 级别的程序,让我们粗浅的了解了 MVC 与 MVP 之间的优缺点和联系,以及写了一个具有代表性的 MVP 的小 demo,通过这个 demo 我们可以发现很多问题,比如,还是有代码的重复。如果你没看过上一篇文章,强烈建议你先过一遍,否则这篇文章你可能会觉得我不知所云,且看以下的代码示例:

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

        mPresenter = new MainPresenter(this);
        mPresenter.handlerData();
    }

    先来看看这一版本的分包结构,相比之前 v1 的版本就多了一个 basemvp 包,然这个包下的代码却很关键。

    我们的 View 层要持有 Presenter 层的引用,就必须要先实例化 P 层的对象,然后才能调用 P 层的相关方法,否则就会造成空指针异常。这里的 new MainPresenter() 是一个具体实现类,因为不管哪个 View 层,都是强制性要求实例化 P 层,为了防止我们忘了实例化,所以在此把实例化方法抽象到父类中,实现它的子类必须实现抽象方法,也就避免了我们忘记。

    还有不同的 Presenter 层,自然而然它的具体实现类就不同了,所以应该考虑用到泛型,这里要明白谁在引用 Presenter,就是谁要传入泛型。显然,我们的 IBaseView 的实现类 BaseActivity 就得传入一个 Presenter 的泛型参数,那先来看一看 IBaseView 接口如何写: 

View 层,新建 IBaseView 接口: 

package com.test.mvp.mvpdemo.mvp.v2.basemvp;

import android.content.Context;

public interface IBaseView {

    Context getContext();
}

    很简单,获取一个上下文对象,我们在 Activity 中或多或少的用到 Context,所以就干脆写个在这里写统一了,为了避免我们在匿名内部类中要传入 MainActivity.this 这样的麻烦事。既然有了 IBaseView 的接口,我们就该考虑写它的具体实现类,因为这里的 View 层就是 Activity,所以我们就得写一个 BaseActivity 基类,既然是基类,就要声明为抽象类,把共有的方法提取到基类中,这里使用到的就是模板模式

View 层,新建 BaseActivity 基类:

package com.test.mvp.mvpdemo.mvp.v2.basemvp;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public abstract class BaseActivity<P extends IBasePresenter> extends AppCompatActivity implements IBaseView {

    protected P mPresenter;

    protected abstract void initLayout(@Nullable Bundle savedInstanceState);

    protected abstract void initViews();

    protected abstract void initData();

    protected abstract P setPresenter();

    protected <T extends View> T $(@IdRes int viewId) {
        return findViewById(viewId);
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        initLayout(savedInstanceState);

        /**
         * 实例化和绑定 P 层
         */
        mPresenter = setPresenter();
        mPresenter.attech(this);

        initViews();
        initData();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        /**
         * 解绑,避免内存泄漏
         */
        mPresenter.detech();
        mPresenter = null;
    }

    @Override
    public Context getContext() {
        return this;
    }
}

    从上面的代码中,我把 new Presenter() 实现类写成了一个泛型,泛型应该是 Java 的基础和基本操作,我就不多说了。这里的抽象方法 setPresenter() 就相当于 new Presenter() 一样,子类继承 BaseActivity 时,只需要返回一个具体的 Presenter() 实例就行了,这样 View 层就持有了 Presenter 的引用。

    然后看 presenter.attech(this) 这个方法,目的是传入一个 IBaseView 的接口类型,将 View 层的引用给了 Presenter 层,这样就形成了关联,就可以互相调用对象的方法了。

    这里注意,一定要在 onDestory() 时释放 P 层引用,否则可能会造成内存泄漏,这个我们之后再说。然后我们来看看 IBasePresenter 接口的方法:

Presenter 层,新建 IBasePresenter 接口:

package com.test.mvp.mvpdemo.mvp.v2.basemvp;

public interface IBasePresenter<V extends IBaseView> {

    void attech(V view);

    void detech();
}

    这里也很简单,就是做了绑定 View 和解绑 View 的操作。那我们的 BasePresenter 实现类就需要持有 BaseView 的引用,所以必须要传入一个泛型的 View 层接口,具体实现代码如下。

Presenter 层,新建 BasePresenter 基类:

package com.test.mvp.mvpdemo.mvp.v2.basemvp;

public abstract class BasePresenter<V extends IBaseView> implements IBasePresenter {
    protected V mView;

    @SuppressWarnings("unchecked")
    @Override
    public void attech(IBaseView view) {
        mView = (V) view;
    }

    @Override
    public void detech() {
        mView = null;
    }
}

    结束了这两个基类的封装,我们的 BaseMVP 差不多就可以形成了。剩下的就是要修改 MainActivity 和 MainPresenter 的操作了,这里的 Model 层是无需修改的,具体业务逻辑也是不用修改的。所以,我们在之前 v1 版本的 MVP 架构的基础之上做一些简单的修改。(请看我的上一篇文章:Andoid MVP架构(一)MVP软件架构介绍与实战运用

    首先,我们需要修改的一个地方,就是契约类,这个类中的业务逻辑处理都不需要修改,还记得我们之前写过的那两个 IBaseView 与 IBasePresenter 吗?这时候就派上用场了。来看代码:

修改 MainContract 契约类:

package com.test.mvp.mvpdemo.mvp.v2;

import com.test.mvp.mvpdemo.mvp.v2.basemvp.IBasePresenter;
import com.test.mvp.mvpdemo.mvp.v2.basemvp.IBaseView;

import okhttp3.Callback;

/**
 * 契约接口,可以很直观的看到 M、V、P 层接口中提供的方法
 */
public interface MainContract {
    interface IMainModel {
        void requestBaidu(Callback callback);
    }

    interface IMainView extends IBaseView{
        void showDialog();

        void succes(String content);
    }

    interface IMainPresenter extends IBasePresenter {
        void handlerData();
    }
}

    为了支持泛型,这里的 IMainView 和 IMainPresenter 就需要继承它们各自的基类接口了。

    其次,MainActivity 不再继承 AppCompatActivity 了,要继承我们刚刚写好的基类:BaseActivity,其中修改的代码如下:

View 层,修改 MainActivity 实现类:

package com.test.mvp.mvpdemo.mvp.v2.view;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.widget.TextView;
import android.widget.Toast;

import com.test.mvp.mvpdemo.R;
import com.test.mvp.mvpdemo.mvp.v2.MainContract;
import com.test.mvp.mvpdemo.mvp.v2.basemvp.BaseActivity;
import com.test.mvp.mvpdemo.mvp.v2.presenter.MainPresenter;

/**
 * MVP 的写法,Version 2: 使用模板方法模式 + 泛型 封装 mvp 基类
 *
 * @author 神探丶威威猫
 * @blog https://blog.csdn.net/smile_running
 * @warning 点个赞哦,评个论哦
 */
public class MainActivity extends BaseActivity<MainContract.IMainPresenter> implements MainContract.IMainView {

    private TextView tv;

    @Override
    protected void initLayout(@Nullable Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void initViews() {
        tv = $(R.id.tv);
    }

    @Override
    protected void initData() {
        mPresenter.handlerData();
    }

    @Override
    protected MainContract.IMainPresenter setPresenter() {
        return new MainPresenter();
    }

    @Override
    public void showDialog() {
        ProgressDialog dialog = new ProgressDialog(getContext());
        dialog.show();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                dialog.dismiss();
            }
        }, 1500);
    }

    @Override
    public void succes(String content) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getContext(), "" + content, Toast.LENGTH_SHORT).show();
                tv.setText(content);
            }
        });
    }
}

    就这么简单,只要稍微的修改就可以了。然后我们看看 MainPresenter 实现类的代码,为了持有 View 层的引用,这里就需要传入泛型的接口了,代码修改成如下:

Presenter 层,修改 MainPresenter 实现类:

package com.test.mvp.mvpdemo.mvp.v2.presenter;

import android.util.Log;

import com.test.mvp.mvpdemo.mvp.v2.MainContract;
import com.test.mvp.mvpdemo.mvp.v2.basemvp.BasePresenter;
import com.test.mvp.mvpdemo.mvp.v2.basemvp.IBaseView;
import com.test.mvp.mvpdemo.mvp.v2.model.DataModel;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

/**
 * presenter 层,承担业务逻辑处理,数据源处理等
 */
public class MainPresenter extends BasePresenter<MainContract.IMainView> implements MainContract.IMainPresenter {

    private MainContract.IMainModel mModel;

    @Override
    public void attech(IBaseView view) {
        super.attech(view);
        mModel = new DataModel();
    }

    @Override
    public void handlerData() {
        if (mView != null) {
            mView.showDialog();
        }
        /**
         * 发起请求,获得回调数据
         */
        mModel.requestBaidu(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String content = response.body().string();
                if (mView != null) {
                    mView.succes(content);
                }
            }
        });
    }

    @Override
    public void detech() {
        super.detech();
        /**
         * 释放内存、关闭网络请求、关闭线程等操作
         */
        Log.d("==========", "detech: 解除绑定,释放内存");
    }
}

    终于修改完了一个基础框架,是不是觉得非常有成就感。不过呢,这个框架还会有一些隐患,我会在下一篇的文章中去解决这个隐患:那就是内存泄漏与动态代理的问题。

    那么,这篇文章到此为止,我们就把新的一个版本的 MVP 架构给修改完了,其实运用的都是一些基础知识,比如模板模式、抽象类和抽象方法、泛型、继承、接口等的使用,每一个都是 Java 的基础知识,是不是很简单啊。如果你可以基于上一篇的 MVP 版本,自己手写一个泛型框架,那说明你掌握的基础知识很不错。不然,还是多推敲推敲别人的写法,多想为什么要这样写?这样才能有效的提高自己的水平。

©️2020 CSDN 皮肤主题: 成长之路 设计师:Amelia_0503 返回首页