IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> android中使用VIPER-clean架构 -> 正文阅读

[移动开发]android中使用VIPER-clean架构

简介

VIPER 是视图 (View),交互器 (Interactor),主持人 (Presenter),实体 (Entity) 以及路由 (Routing) 的首字母缩写,各个部分职责明确,遵循单一职责原则。
在这里插入图片描述

VIPER是基于The Clean Architecture的思想,改进而来的方案,可以参考Uncle Bob的《整洁架构之道》。随着项目需求的增加,可以使用更多的usecase(用例),不会增加单个类的代码量,使得代码看起来整洁,易于维护。
在这里插入图片描述

经验

如果一个页面,承载了太多的逻辑,比如: 音视频页面、派对房等,属于功能超多的页面,可以使用这个架构。如果是简单的页面,比如: 登录、注册或者列表展示的页面,使用常规的mvc架构,就足够了,代码也不会乱到哪里去。对于小团队来说,可以灵活点。就像Uncle Bob说的,不能嫁给框架。

定义基础类

  1. BaseView
public interface BaseView<T extends BasePresenter> {
    void setPresenter(T presenter);
}
  1. BaseInteractor
public interface BaseInteractor {
}
  1. BasePresenter
public interface BasePresenter {
}
  1. BaseRouter
public interface BaseRouter {
}
  1. ThreadUtils
public class ThreadUtils {
    public static void runOnUI(Runnable runnable){
        if (runnable==null){
            return;
        }
        if (Looper.myLooper()!=Looper.getMainLooper()){
            new Handler(Looper.getMainLooper()).post(runnable);
        }else{
            runnable.run();
        }
    }
}
  1. UICallBackWrapper
public class UICallBackWrapper<P> implements BaseUseCase.UseCaseCallback<P> {

    private final BaseUseCase.UseCaseCallback<P> mCallback;

    public UICallBackWrapper(BaseUseCase.UseCaseCallback<P> mCallback) {
        this.mCallback = mCallback;
    }

    @Override
    public void onSuccess(final P response) {
        ThreadUtils.runOnUI(new Runnable() {
            @Override
            public void run() {
                mCallback.onSuccess(response);
            }
        });
    }

    @Override
    public void onError(final String error) {
        ThreadUtils.runOnUI(new Runnable() {
            @Override
            public void run() {
                mCallback.onError(error);
            }
        });
    }
}
  1. BaseUseCase
public abstract class BaseUseCase<Q,P> {
    private Q mRequestValues;
    private UseCaseCallback<P> mUseCaseCallback;

    /**
     * 若包含多个请求数据,则需要封装传入
     * @param requestValues
     * @return
     */
    @CallSuper
    public BaseUseCase<Q,P> setRequestValues(Q requestValues){
        mRequestValues=requestValues;
        return this;
    }

    public Q getRequestValues(){
        return mRequestValues;
    }

    protected UseCaseCallback<P> getUseCaseCallback(){
        return mUseCaseCallback;
    }

    /**
     * 数据处理结束后的回调
     * @param useCaseCallback
     * @return
     */
    public BaseUseCase<Q,P> setUseCaseCallback(UseCaseCallback<P> useCaseCallback){
        mUseCaseCallback=new UICallBackWrapper<>(useCaseCallback);
        return this;
    }

    /**
     * 可在任意线程执行,回调时返回主线程
     */
    public void run(){
        executeUseCase(mRequestValues);
    }

    /**
     * 处理数据
     * @param requestValues
     */
    protected abstract void executeUseCase(Q requestValues);

    public interface RequestValues{}
    public interface ResponseValues{}
    public interface UseCaseCallback<P>{
        void onSuccess(P response);
        void onError(String error);
    }
}

练习

做一个简单的项目,右边是项目结构,如图:
在这里插入图片描述

  1. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/ll_demand_1"
        android:layout_margin="20dp"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20dp"
            android:textColor="@android:color/black"
            android:text="需求1: 1 + 1 =  "/>

        <TextView
            android:id="@+id/tv_result_1"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:textSize="20dp"
            android:textColor="@android:color/black"
            android:text="__"/>

        <Button
            android:id="@+id/btn_1"
            android:onClick="clickBtn1"
            android:layout_marginLeft="30dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="开始计算"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll_demand_2"
        android:layout_margin="20dp"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20dp"
            android:textColor="@android:color/black"
            android:text="需求2: 3 * 3 =  "/>

        <TextView
            android:id="@+id/tv_result_2"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:textSize="20dp"
            android:textColor="@android:color/black"
            android:text="__"/>

        <Button
            android:id="@+id/btn_2"
            android:onClick="clickBtn2"
            android:layout_marginLeft="30dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="开始计算"/>
    </LinearLayout>

    <LinearLayout
        android:layout_margin="20dp"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20dp"
            android:textColor="@android:color/black"
            android:text="需求3:100内 =  "/>

        <TextView
            android:id="@+id/tv_result_3"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:textSize="20dp"
            android:textColor="@android:color/black"
            android:text="__"/>

        <Button
            android:id="@+id/btn_3"
            android:onClick="clickBtn3"
            android:layout_marginLeft="30dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="开始随机"/>
    </LinearLayout>

    <Button
        android:id="@+id/btn_detail"
        android:onClick="clickBtnDetail"
        android:layout_marginLeft="30dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="跳转到详情"/>

</LinearLayout>
  1. MainActivity
public class MainActivity extends AppCompatActivity implements MContact.View {

    private TextView tvResult1;
    private TextView tvResult2;
    private TextView tvResult3;
    private MContact.Presenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        setPresenter(new MPresenter(new MInteractor(),this,new MRouter()));
    }

    private void initView(){
        tvResult1 =findViewById(R.id.tv_result_1);
        tvResult2 =findViewById(R.id.tv_result_2);
        tvResult3=findViewById(R.id.tv_result_3);
    }

    public void clickBtn1(View v){
        mPresenter.calculateFirst();
    }

    public void clickBtn2(View v){
        mPresenter.calculateSecond();
    }

    public void clickBtn3(View v){
        mPresenter.exeRandom();
    }

    public void clickBtnDetail(View v){
        mPresenter.openDetail();
    }

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

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

    @Override
    public void setFirstView(int result) {
        tvResult1.setText(String.valueOf(result));
    }

    @Override
    public void setSecondView(int result) {
        tvResult2.setText(String.valueOf(result));
    }

    @Override
    public void setThirdView(int result) {
        tvResult3.setText(String.valueOf(result));
    }

    @Override
    public void setPresenter(MContact.Presenter presenter) {
        this.mPresenter=presenter;
    }
}
  1. MContact
public class MContact {

    interface Presenter extends BasePresenter{
        /** 计算第一个*/
        void calculateFirst();
        /**计算第二个*/
        void calculateSecond();
        /**随机*/
        void exeRandom();
        /** 跳转到详情*/
        void openDetail();
    }

    interface View extends BaseView<Presenter>{
        Context getContext();
        void showToast(String msg);
        void setFirstView(int result);
        void setSecondView(int result);
        void setThirdView(int result);
    }

    interface Interactor extends BaseInteractor{
        FirstUseCase getFirstUseCase();
        SecondUseCase getSecondUseCase();
        ThirdUseCase getThirdUseCase();
    }

    interface Router extends BaseRouter{
        void openDetailActivity(Context context);
    }

}
  1. MInteractor
public class MInteractor implements MContact.Interactor {

    private FirstUseCase firstUseCase;
    private SecondUseCase secondUseCase;
    private ThirdUseCase thirdUseCase;

    @Override
    public FirstUseCase getFirstUseCase() {
        if (firstUseCase==null){
            synchronized(this){
                if (firstUseCase==null){
                    firstUseCase=new FirstUseCase();
                }
            }
        }
        return firstUseCase;
    }

    @Override
    public SecondUseCase getSecondUseCase() {
        if (secondUseCase==null){
            synchronized(this){
                if (secondUseCase==null){
                    secondUseCase=new SecondUseCase();
                }
            }
        }
        return secondUseCase;
    }

    @Override
    public ThirdUseCase getThirdUseCase() {
        if (thirdUseCase==null){
            synchronized(this){
                if (thirdUseCase==null){
                    thirdUseCase=new ThirdUseCase();
                }
            }
        }
        return thirdUseCase;
    }
}
  1. MPresenter
public class MPresenter implements MContact.Presenter {

    private MContact.Interactor mInteractor;
    private MContact.View mView;
    private MContact.Router mRouter;

    public MPresenter(MContact.Interactor mInteractor, MContact.View mView, MContact.Router mRouter) {
        this.mInteractor = mInteractor;
        this.mView = mView;
        this.mRouter = mRouter;
    }

    @Override
    public void calculateFirst() {
        mInteractor.getFirstUseCase().setRequestValues(new FirstUseCase.RequestValues(1,1))
                .setUseCaseCallback(new BaseUseCase.UseCaseCallback<FirstUseCase.ResponseValues>() {
                    @Override
                    public void onSuccess(FirstUseCase.ResponseValues response) {
                        //显示到屏幕上
                        mView.setFirstView(response.getResult());
                    }

                    @Override
                    public void onError(String error) {
                        mView.showToast(error);
                    }
                }).run();
    }

    @Override
    public void calculateSecond() {
        mView.showToast("延时计算...");
        mInteractor.getSecondUseCase().setRequestValues(new SecondUseCase.RequestValues(3,3))
                .setUseCaseCallback(new BaseUseCase.UseCaseCallback<SecondUseCase.ResponseValues>() {
                    @Override
                    public void onSuccess(SecondUseCase.ResponseValues response) {
                        mView.setSecondView(response.getResult());
                    }

                    @Override
                    public void onError(String error) {
                        mView.showToast(error);
                    }
                }).run();
    }

    @Override
    public void exeRandom() {
        mInteractor.getThirdUseCase().setRequestValues(new ThirdUseCase.RequestValues(100))
                .setCustomCallBack(new ThirdUseCase.CustomCallBack() {
                    @Override
                    public void ok(ThirdUseCase.ResponseValues result) {
                        mView.setThirdView(result.getResult());
                    }

                    @Override
                    public void fail(String err) {
                        mView.showToast(err);
                    }
                }).run();
    }

    @Override
    public void openDetail() {
        mRouter.openDetailActivity(mView.getContext());
    }


}
  1. MRouter
public class MRouter implements MContact.Router {
    @Override
    public void openDetailActivity(Context context) {
        Intent intent=new Intent(context,DetailActivity.class);
        context.startActivity(intent);
    }
}
  1. FirstUseCase
public class FirstUseCase extends BaseUseCase<FirstUseCase.RequestValues, FirstUseCase.ResponseValues> {

    @Override
    protected void executeUseCase(RequestValues requestValues) {
        //假设,不能大于1000,大的话报错
        int maxError=1000;
        if (requestValues.num1>maxError||requestValues.num2>maxError){
            getUseCaseCallback().onError("不能大于1000");
        }else{
            int result=requestValues.num1+requestValues.num2;
            getUseCaseCallback().onSuccess(new ResponseValues(result));
        }
    }

    public static class RequestValues implements BaseUseCase.RequestValues{
        private int num1;
        private int num2;

        public RequestValues(int num1, int num2) {
            this.num1 = num1;
            this.num2 = num2;
        }
    }
    public static class ResponseValues implements BaseUseCase.ResponseValues{
        private int result;

        public ResponseValues(int result) {
            this.result = result;
        }

        public int getResult() {
            return result;
        }
    }


}
  1. SecondUseCase
public class SecondUseCase extends BaseUseCase<SecondUseCase.RequestValues, SecondUseCase.ResponseValues> {

    @Override
    protected void executeUseCase(RequestValues requestValues) {
        //假设,不能大于1000,大的话报错
        int maxError=1000;
        if (requestValues.num1>maxError||requestValues.num2>maxError){
            getUseCaseCallback().onError("不能大于1000");
        }else{
            //假装耗时操作
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //等待2秒
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //开始计算
                    int result=requestValues.num1*requestValues.num2;
                    //这里会自动把线程切换到主线程,请看父类中的定义UICallBackWrapper
                    getUseCaseCallback().onSuccess(new ResponseValues(result));
                }
            }).start();
        }
    }

    public static class RequestValues implements BaseUseCase.RequestValues{
        private int num1;
        private int num2;

        public RequestValues(int num1, int num2) {
            this.num1 = num1;
            this.num2 = num2;
        }
    }
    public static class ResponseValues implements BaseUseCase.ResponseValues{
        private int result;

        public ResponseValues(int result) {
            this.result = result;
        }

        public int getResult() {
            return result;
        }
    }


}
  1. ThirdUseCase
/**
 * 如果BaseUseCase的回调方法满足不了需求,这里可以重新自定义回调方法
 */
public class ThirdUseCase extends BaseUseCase<ThirdUseCase.RequestValues, ThirdUseCase.ResponseValues> {

    @Override
    protected void executeUseCase(RequestValues requestValues) {
        //假设,不能大于1000,大的话报错
        int maxError=1000;
        if (requestValues.num1>maxError){
            //自定义回调
            getCustomCallBack().fail("不能大于1000");
        }else{
            int a = (int) (Math.random() * requestValues.num1);
            //自定义回调
            getCustomCallBack().ok(new ResponseValues(a));
        }
    }

    public static class RequestValues implements BaseUseCase.RequestValues{
        private int num1;

        public RequestValues(int num1) {
            this.num1 = num1;
        }
    }
    public static class ResponseValues implements BaseUseCase.ResponseValues{
        private int result;

        public ResponseValues(int result) {
            this.result = result;
        }

        public int getResult() {
            return result;
        }
    }

    @Override
    public ThirdUseCase setRequestValues(RequestValues requestValues) {
        super.setRequestValues(requestValues);
        return this;
    }

    /** 自定义回调方法*/
    public interface CustomCallBack{
        void ok(ResponseValues result);
        void fail(String err);
    }
    private CustomCallBack customCallBack;

    public ThirdUseCase setCustomCallBack(CustomCallBack callBack){
        this.customCallBack=callBack;
        return this;
    }

    private CustomCallBack getCustomCallBack(){
        return customCallBack;
    }

}

实践

每个页面有一个契约类*Contract,主要用来定义接口。优点:逻辑会比较清晰,便于维护。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-31 15:33:43  更:2021-08-31 15:36:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年10日历 -2024/10/23 15:27:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码