MVP是个好东西,可是最近项目一直用的是mvc模式,先马克下之前鼓捣过的mvp框架,过年回家再用它重构下代码。
先上依赖库####
compile 'io.reactivex:rxandroid:1.2.1'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
compile 'com.android.support:design:24.2.1'
compile 'com.android.support:recyclerview-v7:24.2.1'
compile 'com.android.support:cardview-v7:24.2.1'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.github.chrisbanes.photoview:library:1.2.3'
建议写死依赖的版本号,而不要使用“+”,避免版本升级带了一些奇葩的问题。
依赖retrolambda####
在app.build依赖
apply plugin: 'me.tatarka.retrolambda'
再加上
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
然后在整个项目的build文件上面加入:
classpath 'me.tatarka:gradle-retrolambda:3.2.5'
OK,到这里我们的环境搭建就完成了
首先我们看下项目的目录结构:
BaseContract(基本的文件类,我们可以在里面写上view层,model层,Presenter层的interface)
/**
* Created by Ly on 2016/11/2.
*/
public class BaseContract {
}
BaseModel(基本的model层,所有耗时操作应该写在model层中)
/**
* Created by Ly on 2016/11/2.
*/
public interface BaseModel {
}
BaseView层(写入跟用户交互的方法集合类,比如showTosast,showDialog)
/**
* Created by Ly on 2016/11/2.
*/
public interface BaseView {
void TsShow(String msg);
}
看下我们的基本baseActivity.java:
package com.Ly.BaseJustTalk.base;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.MenuItem;
import android.widget.Toast;
import com.Ly.BaseJustTalk.R;
import butterknife.ButterKnife;
/**
* Created by Ly on 2017/1/12.
*/
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity {
protected T mPresenter;
private SwipeRefreshLayout mRefreshLayout;
private AppBarLayout mAppBar;
private Toolbar mToolbar;
private ProgressDialog mProgressBar;
protected Context mContext;
private boolean mIsRequestDataRefresh = false;
private static final String EXTRA_KEY = "extra";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dogetExtra();
mContext = this;
// 允许为空,不是所有的页面都要实现这个模式
if (createPresenter() != null) {
mPresenter = createPresenter();
mPresenter.attachView((V) this);
}
setContentView(provideContentViewId());
ButterKnife.bind(this);
mAppBar = findViewById(R.id.app_bar_layout);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar != null && mAppBar != null) {
setSupportActionBar(mToolbar); //把Toolbar当做ActionBar给设置
if (canBack()) {
ActionBar actionBar = getSupportActionBar();
if (null != actionBar) {
//设置ActionBar一个返回箭头,主界面没有,次级界面有
actionBar.setDisplayHomeAsUpEnabled(true);
}
if (Build.VERSION.SDK_INT >= 21) {
//Z轴浮动
mAppBar.setElevation(10.6F);
}
}
}
if (isSetRefresh()) {
setupSwipeRefresh();
}
}
public static void doStartActivity(Context context, Bundle bundle, Class<?> clz) {
Intent intent = new Intent(context, clz);
if (bundle != null) {
intent.putExtra(EXTRA_KEY, bundle);
}
context.startActivity(intent);
}
protected abstract void dogetExtra();
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 此时android.R.id.home即为返回箭头
if (item.getItemId() == android.R.id.home) {
onBackPressed();
finish();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.detachView();
}
}
/**
* 生成下拉刷新
*/
private void setupSwipeRefresh() {
mRefreshLayout = findViewById(R.id.swipe_refresh);
if (null != mRefreshLayout) {
mRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary);
mRefreshLayout.setProgressViewOffset(true,
0,
(int) TypedValue.applyDimension
(TypedValue.COMPLEX_UNIT_DIP, 24, getResources()
.getDisplayMetrics()));
}
}
/**
* 设置刷新
*
* @param requestDataRefresh
*/
public void setRefresh(boolean requestDataRefresh) {
if (mRefreshLayout == null) {
return;
}
if (!requestDataRefresh) {
mIsRequestDataRefresh = false;
mRefreshLayout.postDelayed(() -> {
if (mRefreshLayout != null) {
mRefreshLayout.setRefreshing(false);
}
}, 1000);
} else {
mRefreshLayout.setRefreshing(true);
}
}
/**
* 数据刷新
*/
public void requestDataRefresh() {
mIsRequestDataRefresh = true;
}
/**
* 判断当前页面是否可以返回,
* 主界面不可以返回 子界面可以放回
*
* @return
*/
public boolean canBack() {
return false;
}
/**
* 判断子Activity是否需要上下拉刷新功能
*
* @return
*/
public Boolean isSetRefresh() {
return false;
}
/**
* 创建P
*
* @return
*/
protected abstract T createPresenter();
/**
* 引入布局文件
*
* @return
*/
protected abstract int provideContentViewId();
protected void ShowTs(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
protected void ShowDialog() {
ShowDialog(null);
}
protected void ShowDialog(String msg) {
if (TextUtils.isEmpty(msg)) {
msg = getString(R.string.loading);
}
mProgressBar = ProgressDialog.show(this, null, msg);
}
protected void DissDialog() {
if (mProgressBar != null && mProgressBar.isShowing()) {
mProgressBar.dismiss();
}
}
}
相对应的BaseFragment.java 代码如下:
package com.Ly.BaseJustTalk.base;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.Ly.BaseJustTalk.R;
import butterknife.ButterKnife;
/**
* Created by Ly on 2011/1/12.
*/
public abstract class BaseFragment<V, T extends BasePresenter<V>> extends Fragment {
protected Context mContext;
protected T mPresenter;
private boolean mIsRequestDataRefresh = false;
private SwipeRefreshLayout mRefreshLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity();
mPresenter = createPresenter();
mPresenter.attachView((V) this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(createViewLayoutId(), container, false);
ButterKnife.bind(this, rootView);
initView(rootView);
if (isSetRefresh()) {
setupSwipeRefresh(rootView);
}
return rootView;
}
@Override
public void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}
private void setupSwipeRefresh(View view) {
mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
if (mRefreshLayout != null) {
mRefreshLayout.setColorSchemeResources(R.color.refresh_progress_1,
R.color.refresh_progress_2, R.color.refresh_progress_3);
mRefreshLayout.setProgressViewOffset(true, 0, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
mRefreshLayout.setOnRefreshListener(this::requestDataRefresh);
}
}
public void requestDataRefresh() {
mIsRequestDataRefresh = true;
}
public void setRefresh(boolean requestDataRefresh) {
if (mRefreshLayout == null) {
return;
}
if (!requestDataRefresh) {
mIsRequestDataRefresh = false;
mRefreshLayout.postDelayed(() -> {
if (mRefreshLayout != null) {
mRefreshLayout.setRefreshing(false);
}
}, 1000);
} else {
mRefreshLayout.setRefreshing(true);
}
}
protected abstract T createPresenter();
protected abstract int createViewLayoutId();
protected void initView(View rootView) {
}
public Boolean isSetRefresh() {
return true;
}
}
Ok,直接看实例代码来理解,就用登录界面的吧。
其中LoginActiviy为用户能看到的界面;
Contract是mvp的协议层;Model为数据处理层;Presenter可以理解为桥梁,负责沟通连接M层和V层。
在我的理解里的MVP是这个意思:我们可以看到,这个类持有了View和Model两个模块,在方法体里面,我们调用了model的方法去做耗时,在结果方法体里面我们调用了view的方法去修改UI,同时presenter这个模块又被view持有了,view可以在声明周期里面去调用特定的方法,view和presenter相互沟通,view和model完全隔离,presenter调控model,presenter沟通全局。
看下我们的登录界面:
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.5"
android:gravity="center"
android:text="@string/text_welcome"
android:textSize="25sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginLeft="@dimen/margin_left"
android:layout_marginRight="@dimen/margin_right"
android:layout_weight="3"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:id="@+id/til_username"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/et_username"
style="@style/edit_style"
android:inputType="text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/text_username" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/til_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin6">
<EditText
android:id="@+id/et_password"
style="@style/edit_style"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/text_password" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/bt_login"
style="@style/button_style"
android:layout_width="match_parent"
android:layout_height="@dimen/button_height"
android:layout_marginTop="@dimen/margin20"
android:text="@string/text_login_reg" />
</LinearLayout>
</LinearLayout>
LoginContract.java
package com.Ly.BaseJustTalk.ui.activity.Login;
import com.Ly.BaseJustTalk.base.BaseModel;
import com.Ly.BaseJustTalk.base.BaseView;
/**
* Created by Ly on 2017/1/16.
*/
public class LoginContract {
interface LoginView extends BaseView {
String getUserName();
String getUserPass();
// userName错误后进行提示
void setUserNameErr(String errMsg);
// 密码错误后进行提示
void setPassErr(String errMsg);
void doJustalkLogin();
void doLoginFial();
}
interface LoginModel extends BaseModel {
/**
* @param userName
* @param Pass
* @return 0 合法账户密码 给予执行下一步
* -1 用户名为空
* -2 用户密码为空
* -3 用户名不合法
* -4 用户密码不合法
*/
int isRightUserNamePass(String userName, String Pass);
/**
* 进行justalk的登录
*
* @param userName
* @param pass
* @return
*/
boolean doJustalkLogin(String userName, String pass);
}
interface LoginPresenter {
// 验证用户输入的信息
void doVerificationInfo();
// 验证正确后进行登录
void doJustalkLogin();
}
}
LoginModel.java
package com.Ly.BaseJustTalk.ui.activity.Login;
import android.text.TextUtils;
import android.util.Log;
import com.Ly.BaseJustTalk.R;
import com.Ly.BaseJustTalk.application.LyApplication;
import com.justalk.cloud.juslogin.LoginDelegate;
/**
* Created by Administrator on 2017/1/16.
*/
public class LoginModel implements LoginContract.LoginModel {
/**
* 验证账号合法性
*
* @param userName
* @param Pass
* @return 0 合法账户密码 给予执行下一步
* -1 用户名为空
* -2 用户密码为空
* -3 用户名不合法
* -4 用户密码不合法
*/
@Override
public int isRightUserNamePass(String userName, String Pass) {
if (TextUtils.isEmpty(userName)) {
return -1;
} else if (TextUtils.isEmpty(Pass)) {
return -2;
} else if (userName.length() < 4 || userName.length() > 12) {
return -3;
} else if (Pass.length() < 6 || Pass.length() > 12) {
return -4;
} else
return 0;
}
@Override
public boolean doJustalkLogin( String userName, String pass) {
if (LoginDelegate.getInitState() == LoginDelegate.InitStat.MTC_INIT_FAIL) {
return false;
}
String server = LyApplication.getInstance().getString(R.string.JusTalkCloud_network_address);
String network = null;
if (!server.startsWith("sudp:")) {
network = server;
server = "sarc:arc@AccessEntry:99";
Log.e("testtest", server);
}
if (LoginDelegate.login(userName, pass, server, network)) {
return true;
}
return false;
}
}
LoginPresenter.java
package com.Ly.BaseJustTalk.ui.activity.Login;
import com.Ly.BaseJustTalk.base.BasePresenter;
/**
* Created by Ly on 2017/1/16.
*/
public class LoginPresenter extends BasePresenter<LoginContract.LoginView> implements
LoginContract.LoginPresenter {
private LoginContract.LoginView loginView;
private LoginContract.LoginModel loginModel;
public LoginPresenter(LoginContract.LoginView loginView) {
this.loginView = loginView;
loginModel = new LoginModel();
}
@Override
public void doVerificationInfo() {
int resultcode = this.loginModel.isRightUserNamePass(this.loginView.getUserName(), this.loginView.getUserPass());
switch (resultcode) {
case -1:
this.loginView.setUserNameErr("请输入用户名");
break;
case -2:
this.loginView.setPassErr("请输入用户密码");
break;
case -3:
this.loginView.setUserNameErr("请检查用户名格式或长度");
break;
case -4:
this.loginView.setPassErr("请检查密码格式或长度");
break;
case 0:
this.loginView.setUserNameErr(null);
this.loginView.setPassErr(null);
this.loginView.doJustalkLogin();
break;
default:
this.loginView.TsShow("未知错误");
break;
}
}
@Override
public void doJustalkLogin() {
boolean isLoginSuccess = this.loginModel.doJustalkLogin(this.loginView.getUserName(), this.loginView.getUserPass());
if (!isLoginSuccess) {
this.loginView.doLoginFial();
}
}
}
LoginActivity.java
package com.Ly.BaseJustTalk.ui.activity.Login;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import com.Ly.BaseJustTalk.R;
import com.Ly.BaseJustTalk.base.BaseActivity;
import com.Ly.BaseJustTalk.ui.activity.MainActivity;
import com.Ly.BaseJustTalk.utils.Signer;
import com.justalk.cloud.juslogin.LoginDelegate;
import com.justalk.cloud.lemon.MtcCli;
import com.justalk.cloud.lemon.MtcCliConstants;
import butterknife.Bind;
import butterknife.OnClick;
/**
* Created by Ly on 2017/1/16.
*/
public class LoginActivity extends BaseActivity<LoginContract.LoginView, LoginPresenter>
implements LoginContract.LoginView, LoginDelegate.Callback {
@Bind(R.id.et_username)
EditText etUsername;
@Bind(R.id.til_username)
TextInputLayout tilUsername;
@Bind(R.id.et_password)
EditText etPassword;
@Bind(R.id.til_password)
TextInputLayout tilPassword;
@Bind(R.id.bt_login)
Button btLogin;
private LoginContract.LoginPresenter loginPresenter = new LoginPresenter(this);
@OnClick(R.id.bt_login)
void toLogin() {
loginPresenter.doVerificationInfo();
}
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter(this);
}
@Override
protected int provideContentViewId() {
return R.layout.activity_login;
}
@Override
public void TsShow(String msg) {
ShowTs(msg);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LoginDelegate.setCallback(this);
}
@Override
protected void dogetExtra() {
}
@Override
public String getUserName() {
return etUsername.getText().toString().trim();
}
@Override
public String getUserPass() {
return etPassword.getText().toString().trim();
}
@Override
public void setUserNameErr(String errMsg) {
tilUsername.setError(errMsg);
}
@Override
public void setPassErr(String errMsg) {
tilPassword.setError(errMsg);
}
@Override
public void doJustalkLogin() {
ShowDialog();
if (MtcCli.Mtc_CliGetState() != MtcCliConstants.EN_MTC_CLI_STATE_LOGINED) {
this.loginPresenter.doJustalkLogin();
} else {
MainActivity.doStartActivity(mContext, null, MainActivity.class);
}
}
@Override
public void doLoginFial() {
ShowTs(getString(R.string.tips_login_fail));
}
@Override
public void mtcLoginOk() {
MainActivity.doStartActivity(mContext, null, MainActivity.class);
}
@Override
public void mtcLoginDidFail() {
ShowTs(getString(R.string.tips_login_fail_justalk));
}
@Override
public void mtcLogoutOk() {
}
@Override
public void mtcLogouted() {
}
@Override
public void mtcAuthRequire(String s, String s1) {
Log.e("LHT", "mtcAuthRequire: " + s + "----" + s1);
String key =
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
"tRPBKBjUTEQwaKEjPy8YnX1bUODPB+7hto8KeGJbnCdCcnJdLxPFE7ld1skKIyPi" +
"YkyHj73JqA41ntHML2LNqw5Mhs1pewE4QLCu6icIUNtH8+bL53EhVnfdzwIDAQAB" +
"AoGAA6i6c5xHEGfKzoDHQJgiC5X9ZFAtES5AG3IMJmtF9flQyeoeDzRit+/FwNXi" +
"M1CKohnvLAJTvPs/8TBp5us4rabQ5Hnp+ylr7I2IbYIP2LV6TKkiTq/fBOJBxZiw" +
"qs0tjXxRZnC2IWqoCt/ciE4DXQIYVV3gYMRcKae5KZ3F2LECQQDqL4Sd2xyG0FsW" +
"cKwrlFRQ1cfFrSF/jmm2onkCZgDfq0R5aIGewpbTciLj8rf/Zq0XgAmCa3qQYo6M" +
"7G0OgIXTAkEAxbIC2xJocvPfEFbUd+hEWDFl/3LtQWZSHVLx9SXLXWSRY4/3dyRM" +
"6L78eQ2yWIVF4pxJrIHTbJqhxItlVM/elQJBAJ3jRZ0L8hKufQsHEf0btzD8wQB0" +
"doZCZOF+bumADgy+sp7MJ7/636dVZ1KZ/RWTixWx/DdS8UJRQFygtfI2EoMCQHky" +
"4tFPfb1LiStJMES6nnu6/R8YZB++DQVxPmjeXMjKyN9S+ZGPLZ9axwmnvfjK68c7" +
"rWcWyHlCa35FP0A5l+kCQB5cEu5Au1RkY9XfUodKmFhlCvdY8Ig0JgZ8DC6m+A31" +
"o4xrCoGHiPldKdCo0I7gQ4WMvoVNHCQyNv5qcw9t7uk=";
String code = Signer.signWithKey(key, s, s1, 3600);
LoginDelegate.promptAuthCode(code);
}
@Override
protected void onDestroy() {
super.onDestroy();
LoginDelegate.logout();
}
}
更新 这个框架里面的循环嵌套的方法
final String account = "111_111_1111", password = "1234556";
HttpRequest.getApiService().doRegister(account, password).concatMap(new Function<RegisterBean, Flowable<LoginUserBean>>() {
@Override
public Flowable<LoginUserBean> apply(RegisterBean registerBean) throws Exception {
if (registerBean.getStatus().equals(ErrCodeMessage.statusSuc)) {
XLog.e(registerBean.getMessage());
return HttpRequest.getApiService().doLogin(account, password);
} else {
return Flowable.empty();
}
}
}).compose(XApi.<LoginUserBean>getScheduler()).compose(XApi.<LoginUserBean>getApiTransformer())
.compose(this.<LoginUserBean>bindToLifecycle()).subscribe(new ApiSubscriber<LoginUserBean>() {
@Override
protected void onFail(NetError error) {
XLog.e("xxxxxxxxxxxx" + error.getMessage());
}
@Override
public void onNext(LoginUserBean loginUserBean) {
XLog.e("xxxxxxxxxxxx" + loginUserBean.toString());
}
});
如果注册失败的时候 返回 return Flowable.empty(); 一个空的