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'
网友评论