简单版MVP

作者: 夜沐下的星雨 | 来源:发表于2020-07-23 08:21 被阅读0次

MVP概述
当将架构改为MVP以后,Presenter的出现,将Actvity视为View层,Presenter负责完成View层与Model层的交互。每一层都是一个接口,每一层都有它自己的实现类。现在是这样的:

View 对应于Activity,负责View的绘制以及与用户交互

Model 处理数据和实体模型

Presenter 负责完成View于Model间的交互,是V和M层交互的桥梁。

以登录为案例简单讲解:

1.创建Contract
2.创建callBack
3.ApiSercice
4.bean类
5.创建mode
6.utils工具类 网络请求hashMap 的公有参数
7.P 层
8.Acitiviy
9.fragment

1.创建Contract

public interface PasswordLoginContract {
        public interface ILoginView{
            void onLoginSuccess(User user);
            void onLoginFail(String mag);
            void onNetError();
            void onInputFail(String mag);
            void showLoading();
            void closeLoading();
        }
        public interface ILoginPresenter{
            void login(String userCount,String password);
            void bindView(ILoginView view);
            void unBindView();
        }
        public interface ILoginMode{
            void login(HashMap<String,String> params, IBaseCallBack<User> callBack);
        }
}

2.创建callBack

//基类callback
public interface IBaseCallBack<T> {
    void onSuccess(T t);
    void onFail(String error);
}

3.ApiSercice

a.ApiService:

//请求
public interface ApiService {
    String BASE_URL = "https://www.badu.com";

    /**
     * 密码登录
     * @param params
     * @return
     */
    @POST("/api/user/login")
    @FormUrlEncoded
    Observable<HttpResult<User>> login(@FieldMap HashMap<String,String> params);
}

b.DataService

//相当于HttpManger    retrofit 的封装 和 日志拦截器
public class DataService {

    private static final long TIME_OUT = 20000;


    public static volatile ApiService mService;

    public static ApiService getService() {
        if (mService == null) {
            synchronized (DataService.class) {
                if (mService == null) {
                    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
                    /**
                     * 注意,如果有大文件下载,或者 response 里面的body 很大,要么不加HttpLoggingInterceptor 拦截器
                     * 如果非要加,日志级别不能是 BODY,否则容易内存溢出。
                     */
                    if (BuildConfig.DEBUG) {
                        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
                    } else {
                        logging.setLevel(HttpLoggingInterceptor.Level.NONE);
                    }

                    OkHttpClient.Builder builder = new OkHttpClient.Builder();
                    builder.addInterceptor(logging);
                    builder.connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
                            .writeTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
                            .readTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
                            .build();
//
                    Retrofit mRetrofit = new Retrofit.Builder()
                            .client(builder.build())
                            .addConverterFactory(GsonConverterFactory.create()) // 帮我们把json 窜转为 entity 对象
                            .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // 结合 rxjava 使用
                            .baseUrl(ApiService.BASE_URL)
                            .build();
                    mService = mRetrofit.create(ApiService.class);

                }

            }
        }

        return mService;
    }
}

4.Bean类:

a.公有Bean 中的参数


public class HttpResult<T> {
    /**
     * 'code': '1',
     * 'message': '成功提示',
     * 'data':
     */
    private int code;
    private String message;
    private  T data;
    public HttpResult(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

b.bean


public class User {
    private Token token;
    private UserInfo user_info;

    public Token getToken() {
        return token;
    }

    public void setToken(Token token) {
        this.token = token;
    }

    public UserInfo getUser_info() {
        return user_info;
    }

    public void setUser_info(UserInfo user_info) {
        this.user_info = user_info;
    }

    public class Token {

        /**
         * * 'token': {//token信息
         * * 'value': '⽤户登录成功之后的身份标识',
         * * 'expire_time': 'token过期时间',
         * * },
         */

        private String value;
        private long expire_time;


        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public long getExpire_time() {
            return expire_time;
        }

        public void setExpire_time(long expire_time) {
            this.expire_time = expire_time;
        }
    }


    public class UserInfo {


        /**
         * 'head_url': '头像',
         * * 'nickname': '昵称',
         * * 'mobile': '⼿机号',
         * * 'qq_bind': 'qq是否绑定,1绑定,0未绑定',
         * * 'qq_openid': '扣扣的openid',
         * * 'qq_unionid': '值为空,可以忽略',
         * * 'sina_bind': '新浪是否绑定,1绑定,0未绑定',
         * * 'sina_openid': '新浪的uid',
         * * 'sina_unionid': '值为空,可以忽略',
         * * 'wechat_bind': '微信是否绑定,1绑定,0未绑定',
         * * 'wechat_openid': '微信的openid',
         * * 'wechat_openid': '微信的unionid',
         * * 'notice_count': '未读消息数量',
         * * 'my_integral': '我的积分',
         * * 'check_in_status': '签到状态:0未签到,1已签到',
         */

        private String head_url;
        private String nickname;
        private String mobile;
        private String qq_openid;
        private String qq_unionid;
        private String sina_openid;
        private String sina_unionid;
        private String wechat_openid;
        private String wechat_unionid;


        private int qq_bind;
        private int sina_bind;
        private int wechat_bind;
        private int notice_count;
        private int my_integral;
        private int check_in_status;


        public String getHead_url() {
            return head_url;
        }

        public void setHead_url(String head_url) {
            this.head_url = head_url;
        }

        public String getNickname() {
            return nickname;
        }

        public void setNickname(String nickname) {
            this.nickname = nickname;
        }

        public String getMobile() {
            return mobile;
        }

        public void setMobile(String mobile) {
            this.mobile = mobile;
        }

        public String getQq_openid() {
            return qq_openid;
        }

        public void setQq_openid(String qq_openid) {
            this.qq_openid = qq_openid;
        }

        public String getQq_unionid() {
            return qq_unionid;
        }

        public void setQq_unionid(String qq_unionid) {
            this.qq_unionid = qq_unionid;
        }

        public String getSina_openid() {
            return sina_openid;
        }

        public void setSina_openid(String sina_openid) {
            this.sina_openid = sina_openid;
        }

        public String getSina_unionid() {
            return sina_unionid;
        }

        public void setSina_unionid(String sina_unionid) {
            this.sina_unionid = sina_unionid;
        }

        public String getWechat_openid() {
            return wechat_openid;
        }

        public void setWechat_openid(String wechat_openid) {
            this.wechat_openid = wechat_openid;
        }

        public String getWechat_unionid() {
            return wechat_unionid;
        }

        public void setWechat_unionid(String wechat_unionid) {
            this.wechat_unionid = wechat_unionid;
        }

        public int getQq_bind() {
            return qq_bind;
        }

        public void setQq_bind(int qq_bind) {
            this.qq_bind = qq_bind;
        }

        public int getSina_bind() {
            return sina_bind;
        }

        public void setSina_bind(int sina_bind) {
            this.sina_bind = sina_bind;
        }

        public int getWechat_bind() {
            return wechat_bind;
        }

        public void setWechat_bind(int wechat_bind) {
            this.wechat_bind = wechat_bind;
        }

        public int getNotice_count() {
            return notice_count;
        }

        public void setNotice_count(int notice_count) {
            this.notice_count = notice_count;
        }

        public int getMy_integral() {
            return my_integral;
        }

        public void setMy_integral(int my_integral) {
            this.my_integral = my_integral;
        }

        public int getCheck_in_status() {
            return check_in_status;
        }

        public void setCheck_in_status(int check_in_status) {
            this.check_in_status = check_in_status;
        }
    }
}

5.创建mode

a.mode 的抽取:

/相当于model 的基类
//将<?> 全部改成T
//mode 抽取
public abstract class BaseRepository {
        //doObserver 三个参数
    public <T> void doObserver(@NonNull Observable<HttpResult<T>> observable, @NonNull Consumer<T> consumer, IBaseCallBack<T> callBack) {
        observable.map(new Function<HttpResult<T>,T>() {
            @Override
            public T apply(HttpResult<T> result) throws Throwable {
                if (result.getCode() == 1) {
                    if (result.getData() != null) {
                        return result.getData();
                    } else {
                        throw new Exception("服务器异常");
                    }
                } else {
                    throw new Exception("服务器异常");
                }
            }
        }).doOnNext(consumer)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<T>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {

                    }

                    @Override
                    public void onNext(@NonNull T t) {
                    callBack.onSuccess(t);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                    callBack.onFail(e.getMessage());
                    }

                    @Override
                    public void onComplete() {

                    }
                });

    }

    public <T> void doObserver(Observable<HttpResult<T>> observable,IBaseCallBack<T> callBack){
        doObserver(observable, new Consumer<T>() {
            @Override
            public void accept(T t) throws Throwable {

            }
        },callBack);

    }
}

b.mode层


public class UserRepository extends BaseRepository implements PasswordLoginContract.ILoginMode {

    @Override
    public void login(HashMap<String, String> params, IBaseCallBack<User> callBack) {
        doObserver(DataService.getService().login(params),callBack);
    }
}

6.utils 网络请求hashMap 的公有参数

a.Constrant

public interface Constrant {
    String BASE_URL = "https://www.seetao.com";
    String VALUE_FROM = "android";
    String VALUE_LANG = "zh";


    interface  RequestKey{

        String KEY_FROM = "from";
        String KEY_LANG = "lang";
        String KEY_TIMESTAMP = "timestamp";
        String KEY_NONCE = "nonce";
        String KEY_SIGNATURE = "signature";


        String KEY_USER_COUNT = "username";
        String KEY_USER_PASSWORD = "password";
    }
}

b.ParamsUtils

/*
*公有参数
 **/
public class ParamsUtils {

    private static String SHA1_KEY = "K;9)Bq|ScMF1h=Vp5uA-G87d(_fi[aP,.w^{vQ:W";


    public static HashMap<String,String> getCommonParams(){

        HashMap<String,String> hashMap = new HashMap();
        //来源信息
        hashMap.put(KEY_FROM, VALUE_FROM);
        //语言信息
        hashMap.put(KEY_LANG, VALUE_LANG);


        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonce = String.valueOf((int) ((Math.random() * 9 + 1) * 100000));

        //随机数
        hashMap.put(KEY_NONCE, nonce);
        //时间戳
        hashMap.put(KEY_TIMESTAMP, timestamp);
        //签名结果串
        hashMap.put(KEY_SIGNATURE,getSHA1(timestamp, nonce));


        return hashMap;
    }



    public static  String getSHA1(String timestamp, String nonce) {
        try {
            String[] array = new String[]{SHA1_KEY, timestamp, nonce};
            StringBuffer sb = new StringBuffer();
            // 字符串排序
            Arrays.sort(array);
            for (int i = 0; i < 3; i++) {
                sb.append(array[i]);
            }
            String str = sb.toString();
            // SHA1签名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "";
    }
}

c.appUtil

public class AppLoginUtils {
    public static boolean isValidUserCount(String count){
        //判断手机号是否正确   如果长度==11就返回true
        if(!TextUtils.isEmpty(count)){
            if(count.length() == 11){
                return true;
            }
        }
        return false;
    }
    //判断密码长度大于等于6 大于就返回true
    public static boolean isValidUserPassword(String password){
        if(!TextUtils.isEmpty(password)){
            if(password.length() >= 6){
                return true;
            }
        }
        return false;
    }
}

7.P 层:


public class PasswordLoginPresenter implements PasswordLoginContract.ILoginPresenter {

    private PasswordLoginContract.ILoginMode mode;
    private PasswordLoginContract.ILoginView view;

    public PasswordLoginPresenter() {
        mode= new UserRepository();
    }


    @Override
    public void login(String userCount, String password) {
      /*  if (isOnInternet()){
            view.onNetError();
            return;
        }*/
        if (!AppLoginUtils.isValidUserCount(userCount)){
            view.onInputFail("手机号格式不对,手机号长度为11位");
            return;
        }
        if (!AppLoginUtils.isValidUserPassword(password)){
            view.onInputFail("密码格式不对,密码长度必须大于等于6");
            return;
        }
        HashMap<String, String> commonParams = ParamsUtils.getCommonParams();
        commonParams.put(KEY_USER_COUNT,userCount);
        commonParams.put(KEY_USER_PASSWORD,password);
        view.showLoading();
        mode.login(commonParams, new IBaseCallBack<User>() {
            @Override
            public void onSuccess(User user) {
                view.closeLoading();
                view.onLoginSuccess(user);
            }

            @Override
            public void onFail(String error) {
                view.closeLoading();
                view.onLoginFail(error);
            }
        });
    }
    //判断是否有网
    private boolean isOnInternet(){
        return true;
    }
    @Override
    public void bindView(PasswordLoginContract.ILoginView view) {
           this.view=view;
    }

    @Override
    public void unBindView() {
            this.view=null;
    }

}

8.Activity:


//手机号  密码 登录
public class PasswordLoginActivity extends AppCompatActivity  implements PasswordLoginContract.ILoginView{

    private ActivityLoginPasswordBinding binding;
    private PasswordLoginPresenter passwordLoginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityLoginPasswordBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        passwordLoginPresenter = new PasswordLoginPresenter();
        passwordLoginPresenter.bindView(this);
        binding.authPasswordLoginBtnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                initLogin();
            }
        });
    }
    private void initLogin() {
        String count = binding.authPasswordLoginEdtCount.getText().toString().trim();
        String password = binding.authPasswordLoginEdtPassword.getText().toString().trim();
        passwordLoginPresenter.login(count,password);

    }

    @Override
    public void onLoginSuccess(User user) {
        //得到网络数据
        Toast.makeText(this,"USER:"+user.getUser_info().getNickname(),Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onLoginFail(String mag) {
        //网络数据的错误
        Toast.makeText(this,mag,Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNetError() {
        Toast.makeText(this,"没有网络,请检查网络",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onInputFail(String mag) {
        //判断账号密码规范
        Toast.makeText(this,mag,Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showLoading() {
        Toast.makeText(this,"显示加载动画",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void closeLoading() {
        Toast.makeText(this,"关闭加载动画",Toast.LENGTH_SHORT).show();
    }

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

9.fragment:


public class PasswordLoginFragment extends Fragment implements PasswordLoginContract.ILoginView {

    private FragmentLoginPasswordBinding binding;
    private PasswordLoginPresenter passwordLoginPresenter;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        passwordLoginPresenter = new PasswordLoginPresenter();
        passwordLoginPresenter.bindView(this);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = FragmentLoginPasswordBinding.inflate(getLayoutInflater(), container, false);


        binding.authPasswordLoginBtnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                login();
            }
        });
        return binding.getRoot();
    }

    private void login() {
        String count = binding.authPasswordLoginEdtCount.getText().toString().trim();
        String password = binding.authPasswordLoginEdtPassword.getText().toString().trim();
        passwordLoginPresenter.login(count,password);
    }
    @Override
    public void onLoginSuccess(User user) {
        //得到网络数据
        Toast.makeText(getActivity(),"USER:"+user.getUser_info().getNickname(),Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onLoginFail(String mag) {
        Toast.makeText(getActivity(),mag,Toast.LENGTH_SHORT).show();

    }

    @Override
    public void onNetError() {
        Toast.makeText(getActivity(),"没有网络,请检查网络",Toast.LENGTH_SHORT).show();


    }

    @Override
    public void onInputFail(String mag) {
        Toast.makeText(getActivity(),mag,Toast.LENGTH_SHORT).show();

    }

    @Override
    public void showLoading() {
        Toast.makeText(getActivity(),"显示加载动画",Toast.LENGTH_SHORT).show();

    }

    @Override
    public void closeLoading() {
        Toast.makeText(getActivity(),"关闭加载动画",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        passwordLoginPresenter.unBindView();
    }
}

Acitivity 和Fragment 是通过Binding 获取控件:

出现的错误:

由于最近才使用的Rxjava3,而retrofit2要与Rxjava 3相结合使用就必须添加依赖:
implementation ‘ com.squareup.retrofit2:adapter-rxjava3:2.9.0 ’
而不是:
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

相关文章

网友评论

    本文标题:简单版MVP

    本文链接:https://www.haomeiwen.com/subject/pvqclktx.html