本文為菜鳥窩作者蔣志碧的連載皆刺。“從 0 開始開發(fā)一款直播 APP ”系列來聊聊時(shí)下最火的直播 APP耕突,如何完整的實(shí)現(xiàn)一個(gè)類"騰訊直播"的商業(yè)化項(xiàng)目
視頻地址:http://www.cniao5.com/course/10121
【從 0 開始開發(fā)一款直播 APP】5.1 MVP 完全解析 -- 實(shí)現(xiàn)直播登錄
【從 0 開始開發(fā)一款直播 APP】5.2 MVP 之 Fragment 交互實(shí)現(xiàn)滑動(dòng)導(dǎo)航
【從 0 開始開發(fā)一款直播 APP】5.3 MVC 完全解析 -- 實(shí)現(xiàn)直播登錄
MVP 概述
在無任何架構(gòu)模式下的開發(fā)時(shí)笤成,Activity 和 Model 之間的關(guān)系太緊密,做了所有的操作眷茁,不易維護(hù)炕泳,擴(kuò)展性差。如果所有邏輯都在 Activity 中實(shí)現(xiàn)上祈,代碼顯得臃腫不易維護(hù)和修改培遵。
MVP(Model-View-Presenter浙芙,模型-視圖-表示器)模式則是由 IBM 開發(fā)出來的一個(gè)針對 C++ 和 Java 的編程模型,大概出現(xiàn)于 2000 年籽腕,是 MVC 模式的一個(gè)變種嗡呼,主要用來隔離UI、UI 邏輯和業(yè)務(wù)邏輯节仿、數(shù)據(jù)晤锥。MVP 模式中不容許 View 直接訪問 Model,這是 MVP 和 MVC 之間最大的不同廊宪。View 中應(yīng)該只有 UI 邏輯,捕捉用戶輸入以及視圖的渲染女轿。這樣將其它復(fù)雜的邏輯抽離出來放到 Presenter 中去箭启,這樣就出現(xiàn)了MVP。簡單的說蛉迹,就是將 View 中的復(fù)雜工作抽取到 Presenter 中傅寡,降低了耦合度,便于維護(hù)和測試北救,也增強(qiáng)了復(fù)用性荐操。
Presenter 是 Model 和 View 之間的橋梁,為了讓結(jié)構(gòu)變得更加簡單珍策,View 并不能直接對 Model 進(jìn)行操作托启,這也是 MVP 與 MVC 最大的不同之處。
Model — 業(yè)務(wù)邏輯和數(shù)據(jù)模型攘宙。
Model 層主要負(fù)責(zé)
1屯耸、從網(wǎng)絡(luò),數(shù)據(jù)庫蹭劈,文件疗绣,傳感器,第三方等數(shù)據(jù)源讀寫數(shù)據(jù)铺韧。
2多矮、對外部的數(shù)據(jù)類型進(jìn)行解析轉(zhuǎn)換為 APP 內(nèi)部數(shù)據(jù)交由上層處理。
3哈打、對數(shù)據(jù)的臨時(shí)存儲,管理塔逃,協(xié)調(diào)上層數(shù)據(jù)請求。
View — View 的繪制和用戶交互
View 層主要負(fù)責(zé)
1前酿、提供 UI 交互
2患雏、在 Presenter 的控制下修改 UI
3、將業(yè)務(wù)事件交由 Presenter 處理
注意:View 層不存儲數(shù)據(jù)罢维,不與 Model 層交互
Presenter — View 和 Model 間的交互
Presenter 層主要負(fù)責(zé)
1淹仑、作為 View 和 Model 之間的紐帶丙挽,處理與用戶交互的邏輯實(shí)現(xiàn)。
2匀借、根據(jù)用戶在視圖中的行為更新模型的邏輯
3颜阐、負(fù)責(zé)從 View 視圖中取得數(shù)據(jù)發(fā)送給模型
MVP 優(yōu)點(diǎn)
1、降低耦合度吓肋,實(shí)現(xiàn)了Model和View真正的完全分離凳怨,可以修改 View 而不影響 Model
2、模塊職責(zé)劃分明顯是鬼,層次清晰
3肤舞、隱藏?cái)?shù)據(jù)
4、 Presenter 可以復(fù)用均蜜,一個(gè) Presenter 可以用于多個(gè) View李剖,而不需要更改 Presenter的邏輯(當(dāng)然是在 View 的改動(dòng)不影響業(yè)務(wù)邏輯的前提下)
5、利于測試驅(qū)動(dòng)開發(fā)囤耳。在使用 MVP 的項(xiàng)目中 Presenter 對 View 是通過接口進(jìn)行篙顺,在對 Presenter 進(jìn)行不依賴 UI 環(huán)境的單元測試的時(shí)候〕湓瘢可以通過 Mock 一個(gè) View 對象德玫,這個(gè)對象只需要實(shí)現(xiàn)了 View 的接口即可。然后依賴注入到 Presenter 中椎麦,單元測試的時(shí)候就可以完整的測試 Presenter 應(yīng)用邏輯的正確性宰僧。
6、View可以進(jìn)行組件化铃剔。在MVP當(dāng)中撒桨,View不依賴Model。這樣就可以讓View從特定的業(yè)務(wù)場景中脫離出來键兜,可以說View可以做到對業(yè)務(wù)完全無知凤类。它只需要提供一系列接口提供給上層操作。這樣就可以做到高度可復(fù)用的View組件普气。
7谜疤、提高代碼靈活性
MVP 缺點(diǎn)
1、Presenter中除了應(yīng)用邏輯以外现诀,還有大量的 View -> Model夷磕,Model -> View 的手動(dòng)同步邏輯,造成 Presenter 比較笨重仔沿,維護(hù)起來會(huì)比較困難坐桩。
2、由于對視圖的渲染放在了 Presenter 中封锉,所以視圖和 Presenter 的交互會(huì)過于頻繁绵跷。
3膘螟、如果 Presenter 過多地渲染了視圖,往往會(huì)使得它與特定的視圖的聯(lián)系過于緊密碾局。一旦視圖需要變更荆残,那么 Presenter 也需要變更了。
4净当、額外的代碼復(fù)雜度及學(xué)習(xí)成本内斯。
MVP 使用
MVP 的邏輯實(shí)現(xiàn)類圖,根據(jù) MVP 模型像啼,定義 P 層和 V 層接口以及基本方法以及 M 層相關(guān)方法俘闯。
使用 MVP 大致要做以下步驟
1、創(chuàng)建 IPresenter 接口忽冻,把所有業(yè)務(wù)邏輯接口都定義在內(nèi)备徐,并創(chuàng)建它的實(shí)現(xiàn)類 PresenterImpl。Ipresenter 持有 IView 的引用甚颂,調(diào)用 IView 中的方法。
2秀菱、創(chuàng)建 IView接口振诬,把所有視圖邏輯的接口都定義在內(nèi),創(chuàng)建其實(shí)現(xiàn)類 Activity / Fragment衍菱。
3赶么、IPresenter 間接持有 Model 的引用,但 Model 并不是必須有的脊串,但是一定會(huì)有 Presenter 和 View辫呻。
4、在上圖中可以看到琼锋,Activity 中包含了一個(gè) IPresenter放闺,而 PresenterImpl 里包含了一個(gè) IView并依賴于 Model。Activity 只保留了對 IPresenter 的調(diào)用缕坎,其它工作全部由 PresenterImpl 實(shí)現(xiàn)怖侦。
MVP 實(shí)現(xiàn)登錄
Presenter — LoginPresenter
1、定義 BasePresenter 接口谜叹,主要方法是 start() — presenter 開始處理數(shù)據(jù)匾寝,綁定 Presenter 。 finish() — 處理銷毀工作荷腊,界面結(jié)束時(shí)調(diào)用艳悔,取消綁定 Presenter 的方法。
2女仰、定義 ILoginPresenter 抽象類實(shí)現(xiàn) BasePresenter 接口猜年,定義登錄所需邏輯處理方法抡锈,與 ILoginView 關(guān)聯(lián)。
3码倦、定義 LoginPresenter 繼承 ILoginPresenter 抽象類企孩,實(shí)現(xiàn)抽象方法以及處理邏輯。
View — LoginActivity
1袁稽、定義 BaseView 接口勿璃,將通用方法封裝到里面。
2推汽、定義 ILoginView 接口补疑,定義登錄需要的通用方法。
3歹撒、創(chuàng)建 LoginActivity 實(shí)現(xiàn) ILoginView 接口莲组,并與 LoginPresenter 關(guān)聯(lián)。
Model — UserInfo
根據(jù)登錄請求數(shù)據(jù)定義 Model 所需字段以及 set() 和 get() 方法暖夭,注意:Model 要實(shí)現(xiàn)序列化接口锹杈。
根據(jù)相關(guān)類圖創(chuàng)建包和類。
對登錄不了解的請查看 【從 0 開始開發(fā)一款直播 APP】4.4 網(wǎng)絡(luò)封裝之 OkHttp -- 網(wǎng)絡(luò)請求實(shí)現(xiàn)直播登錄
在后面的代碼中會(huì)涉及到網(wǎng)絡(luò)的狀態(tài)判斷迈着,倒計(jì)時(shí)器的加載竭望,弱引用,ACache 緩存等裕菠,有興趣的可以看看咬清。
【從 0 開始開發(fā)一款直播 APP】6 緩存 ACache 源碼解析
【從 0 開始開發(fā)一款直播 APP】7 倒計(jì)時(shí)器 CountDownTimer 源碼解析
【從 0 開始開發(fā)一款直播 APP】8 弱引用 WeakReference
【從 0 開始開發(fā)一款直播 APP】9 網(wǎng)絡(luò)連接狀態(tài)詳解
Presenter 所有類實(shí)現(xiàn)
1、BasePresenter
public interface BasePresenter{
/**
* presenter 開始處理方法
*/
void start();
/**
* 處理一些銷毀工作奴潘,在界面結(jié)束時(shí)候調(diào)用
*/
void finish();
}
2旧烧、ILoginPresenter
public abstract class ILoginPresenter implements BasePresenter {
protected BaseView mBaseView;
public ILoginPresenter(BaseView baseView) {
mBaseView = baseView;
}
/**
* 檢查手機(jī)號驗(yàn)證碼是否合法
* @param phone
* @param verifyCode
* @return
*/
public abstract boolean checkPhoneLogin(String phone, String verifyCode);
/**
* 檢查用戶名密碼是否合法
* @param userName
* @param password
* @return
*/
public abstract boolean checkUserNameLogin(String userName, String password);
/**
* 手機(jī)號登錄
* @param phone
* @param verifyCode
*/
public abstract void phoneLogin(String phone, String verifyCode);
/**
* 用戶名登錄
* @param userName
* @param password
*/
public abstract void userNameLogin(String userName, String password);
/**
* 發(fā)送驗(yàn)證碼
* @param phoneNum
*/
public abstract void sendVerifyCode(String phoneNum);
}
3、LoginPresenter
實(shí)現(xiàn) ILoginPresenter 接口及其方法的邏輯
public class LoginPresenter extends ILoginPresenter {
private ILoginView mLoginView;
public LoginPresenter(ILoginView loginView) {
super(loginView);
mLoginView = loginView;
}
@Override
public void start() {
}
@Override
public void finish() {
//與 View 解綁
if (mLoginView!=null){
mLoginView = null;
}
}
@Override
public boolean checkPhoneLogin(String phone, String verifyCode) {
//判斷手機(jī)號是否合法
if (OtherUtils.isPhoneNumValid(phone)) {
//判斷驗(yàn)證碼是否正確
if (OtherUtils.isVerifyCodeValid(verifyCode)) {
//監(jiān)測網(wǎng)絡(luò)狀態(tài)画髓,包括網(wǎng)絡(luò)類型(Wi-Fi掘剪,2G,3G雀扶,4G)以及網(wǎng)絡(luò)是否連接正常杖小,設(shè)置網(wǎng)絡(luò)等
if (OtherUtils.checkNetWorkState(mLoginView.getContext())) {
return true;
} else {
mLoginView.showMsg("當(dāng)前無網(wǎng)絡(luò)連接!");
}
} else {
mLoginView.verifyCodeError("驗(yàn)證碼錯(cuò)誤愚墓!");
}
} else {
mLoginView.phoneError("手機(jī)格式錯(cuò)誤予权!");
}
mLoginView.dismissLoading();
return false;
}
@Override
public boolean checkUserNameLogin(String userName, String password) {
//判斷用戶名是否合法
if (OtherUtils.isUsernameVaild(userName)) {
//判斷密碼是否合法
if (OtherUtils.isPasswordValid(password)) {
//監(jiān)測網(wǎng)絡(luò)狀態(tài),包括網(wǎng)絡(luò)類型(Wi-Fi浪册,2G扫腺,3G,4G)以及網(wǎng)絡(luò)是否連接正常村象,設(shè)置網(wǎng)絡(luò)等
if (OtherUtils.checkNetWorkState(mLoginView.getContext())) {
return true;
} else {
mLoginView.showMsg("當(dāng)前無網(wǎng)絡(luò)連接笆环!");
}
} else {
mLoginView.passwordError("密碼過短攒至!");
}
} else {
mLoginView.usernameError("用戶名不符合規(guī)范!");
}
//取消loading進(jìn)度顯示
mLoginView.dismissLoading();
return false;
}
@Override
public void phoneLogin(final String phone, final String verifyCode) {
//判斷手機(jī)號和驗(yàn)證碼是否正確
if (checkPhoneLogin(phone, verifyCode)) {
final PhoneLoginRequest request = new PhoneLoginRequest(RequestComm.loginPhone, phone, verifyCode);
AsyncHttp.instance().postJson(request, new AsyncHttp.IHttpListener() {
@Override
public void onStart(int requestId) {
//顯示loading進(jìn)度
mLoginView.showLoading();
}
@Override
public void onSuccess(int requestId, Response response) {
if (response.getStatus() == RequestComm.SUCCESS) {
ACache.get(mLoginView.getContext()).put(CacheConstants.LOGIN_PHONE, phone);
mLoginView.loginSuccess();
} else {
mLoginView.loginFailed(response.getStatus(), response.getMsg());
}
mLoginView.dismissLoading();
}
@Override
public void onFailure(int requestId, int httpStatus, Throwable error) {
mLoginView.verifyCodeFailed("網(wǎng)絡(luò)異常");
mLoginView.dismissLoading();
}
});
}
}
@Override
public void userNameLogin(final String userName, final String password) {
//判斷用戶名是否合法
if (checkUserNameLogin(userName, password)) {
LoginRequest request = new LoginRequest(RequestComm.loginUsername, userName, password);
AsyncHttp.instance().postJson(request, new AsyncHttp.IHttpListener() {
@Override
public void onStart(int requestId) {
mLoginView.showLoading();
}
@Override
public void onSuccess(int requestId, Response response) {
if (response.getStatus() == RequestComm.SUCCESS) {
//請求數(shù)據(jù)成功
UserInfo info = (UserInfo) response.getData();
//將登陸數(shù)據(jù)存入緩存躁劣,這個(gè)在后面的文章會(huì)介紹
UserInfoCache.saveCache(mLoginView.getContext(), info);
ACache.get(mLoginView.getContext()).put(CacheConstants.LOGIN_USERNAME, userName);
ACache.get(mLoginView.getContext()).put(CacheConstants.LOGIN_PASSWORD, password);
mLoginView.loginSuccess();
} else {
mLoginView.loginFailed(response.getStatus(), response.getMsg());
mLoginView.dismissLoading();
}
}
@Override
public void onFailure(int requestId, int httpStatus, Throwable error) {
mLoginView.loginFailed(httpStatus, error.getMessage());
mLoginView.dismissLoading();
}
});
}
}
@Override
public void sendVerifyCode(String phoneNum) {
//判斷手機(jī)號是否合法
if (OtherUtils.isPhoneNumValid(phoneNum)) {
//監(jiān)測網(wǎng)絡(luò)狀態(tài)迫吐,包括網(wǎng)絡(luò)類型(Wi-Fi,2G账忘,3G志膀,4G)以及網(wǎng)絡(luò)是否連接正常,設(shè)置網(wǎng)絡(luò)等
if (OtherUtils.checkNetWorkState(mLoginView.getContext())) {
VerifyCodeRequest request = new VerifyCodeRequest(RequestComm.verifyCodeRequestId, phoneNum);
AsyncHttp.instance().postJson(request, new AsyncHttp.IHttpListener() {
@Override
public void onStart(int requestId) {
mLoginView.showLoading();
}
@Override
public void onSuccess(int requestId, Response response) {
if (response.getStatus() == RequestComm.SUCCESS) {
UserInfo info = (UserInfo) response.getData();
if (null != mLoginView){
//倒計(jì)時(shí)器 60 秒發(fā)送驗(yàn)證碼
mLoginView.verifyCodeSuccess(60,60);
}
}else {
mLoginView.verifyCodeFailed("獲取后臺驗(yàn)證碼失敱钋堋溉浙!");
}
mLoginView.dismissLoading();
}
@Override
public void onFailure(int requestId, int httpStatus, Throwable error) {
if (null != mLoginView){
mLoginView.verifyCodeFailed("獲取后臺驗(yàn)證碼失敗蒋荚!");
}
mLoginView.dismissLoading();
}
});
} else {
mLoginView.showMsg("當(dāng)前無網(wǎng)絡(luò)連接戳稽!");
}
} else {
mLoginView.phoneError("手機(jī)號碼不符合規(guī)范!");
}
}
}
View 所有類實(shí)現(xiàn)
1期升、BaseView
public interface BaseView {
/**
* 數(shù)據(jù)加載或耗時(shí)加載時(shí)界面顯示
*/
void showLoading();
/**
* 數(shù)據(jù)加載或耗時(shí)加載完成時(shí)界面顯示
*/
void dismissLoading();
/**
* 消息提示惊奇,如 Toast,Dialog等
*/
void showMsg(String msg);
void showMsg(int msgId);
/**
* 獲取Context
* @return
*/
Context getContext();
}
2播赁、ILoginView
在 LoginPresenter 中綁定了 ILoginView赊时,在 LoginPresenter 實(shí)現(xiàn)邏輯時(shí)調(diào)用的時(shí) ILoginView 中的方法。
public interface ILoginView extends BaseView {
/**
* 登錄成功
*/
void loginSuccess();
/**
* 登錄失敗
* @param status
* @param msg
*/
void loginFailed(int status, String msg);
/**
* 用戶名錯(cuò)誤
* @param errorMsg
*/
void usernameError(String errorMsg);
/**
*手機(jī)號錯(cuò)誤
* @param errorMsg
*/
void phoneError(String errorMsg);
/**
* 密碼錯(cuò)誤
* @param errorMsg
*/
void passwordError(String errorMsg);
/**
* 驗(yàn)證碼錯(cuò)誤
* @param errorMsg
*/
void verifyCodeError(String errorMsg);
/**
* 驗(yàn)證失敗
* @param errorMsg
*/
void verifyCodeFailed(String errorMsg);
/**
* 驗(yàn)證成功
* @param reaskDuration
* @param expireDuration
*/
void verifyCodeSuccess(int reaskDuration, int expireDuration);
}
3行拢、LoginActivity
之前的文章中有講過用戶登錄實(shí)現(xiàn),那時(shí)是硬實(shí)現(xiàn)诞吱,沒有邏輯分層舟奠,可以結(jié)合前面的文章一起學(xué)習(xí)。
public class LoginActivity extends BaseActivity implements View.OnClickListener, ILoginView {
//共用控件
private ProgressBar progressBar;
private EditText etPassword;
private EditText etLogin;
private Button btnLogin;
private Button btnPhoneLogin;
private TextInputLayout tilLogin, tilPassword;
private Button btnRegister;
//手機(jī)驗(yàn)證登陸控件
private TextView tvVerifyCode;
private boolean isPhoneLogin = false;
private LoginPresenter mLoginPresenter;
@Override
protected void setActionBar() {
}
@Override
protected void setListener() {
}
@Override
protected void initData() {
etLogin.setText(ACache.get(this).getAsString(CacheConstants.LOGIN_USERNAME));
etPassword.setText(ACache.get(this).getAsString(CacheConstants.LOGIN_PASSWORD));
}
@Override
protected void initView() {
mLoginPresenter = new LoginPresenter(this);
etLogin = obtainView(R.id.et_username);
etPassword = obtainView(R.id.et_password);
btnRegister = obtainView(R.id.btn_register);
btnPhoneLogin = obtainView(R.id.btn_phone_login);
btnLogin = obtainView(R.id.btn_login);
progressBar = obtainView(R.id.progressbar);
tilLogin = obtainView(til_login);
tilPassword = obtainView(R.id.til_password);
tvVerifyCode = obtainView(R.id.btn_verify_code);
userNameLoginViewInit();
}
@Override
protected int getLayoutId() {
return R.layout.activity_login;
}
/**
* 用戶名密碼登錄界面init
*/
public void userNameLoginViewInit() {
//用戶名登錄切換
userLoginTrans();
tvVerifyCode.setOnClickListener(this);
//注冊
btnRegister.setOnClickListener(this);
//手機(jī)號登錄
btnPhoneLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//手機(jī)號登錄
phoneLoginViewinit();
}
});
//用戶名登錄
btnLogin.setOnClickListener(this);
}
public void phoneLoginViewinit() {
phoneLoginTrans();
btnPhoneLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//轉(zhuǎn)換為用戶名登錄界面
userNameLoginViewInit();
}
});
btnLogin.setOnClickListener(this);
btnRegister.setOnClickListener(this);
tvVerifyCode.setOnClickListener(this);
}
private void phoneLoginTrans() {
isPhoneLogin = true;
tvVerifyCode.setVisibility(View.VISIBLE);
AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
alphaAnimation.setDuration(250);
tvVerifyCode.setAnimation(alphaAnimation);
//設(shè)定點(diǎn)擊優(yōu)先級于最前(避免被EditText遮擋的情況)
tvVerifyCode.bringToFront();
//設(shè)置輸入框輸入類型為 手機(jī)號
etLogin.setInputType(EditorInfo.TYPE_CLASS_PHONE);
etLogin.setText("");
etPassword.setText("");
//手機(jī)號登錄按鈕文字改為 用戶名登錄
btnPhoneLogin.setText("用戶名登錄");
tilLogin.setHint("手機(jī)號");
tilPassword.setHint("密碼");
}
private void userLoginTrans() {
isPhoneLogin = false;
tvVerifyCode.setVisibility(View.GONE);
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
alphaAnimation.setDuration(250);
tvVerifyCode.setAnimation(alphaAnimation);
etLogin.setInputType(EditorInfo.TYPE_CLASS_TEXT);
etLogin.setText("");
etPassword.setText("");
btnPhoneLogin.setText("手機(jī)號登錄");
tilLogin.setHint("用戶名");
tilPassword.setHint("密碼");
}
/**
* 手機(jī)登錄和用戶名登錄界面顯示或隱藏
* @param active
*/
public void showOnLoading(boolean active) {
if (active) {
progressBar.setVisibility(View.VISIBLE);
btnLogin.setVisibility(View.INVISIBLE);
etLogin.setEnabled(false);
etPassword.setEnabled(false);
btnPhoneLogin.setClickable(false);
btnRegister.setTextColor(getResources().getColor(R.color.colorTransparentGray));
btnPhoneLogin.setTextColor(getResources().getColor(R.color.colorTransparentGray));
btnRegister.setClickable(false);
} else {
progressBar.setVisibility(View.GONE);
btnLogin.setVisibility(View.VISIBLE);
etLogin.setEnabled(true);
etPassword.setEnabled(true);
btnPhoneLogin.setClickable(true);
btnRegister.setClickable(true);
btnRegister.setTextColor(getResources().getColor(R.color.white));
btnPhoneLogin.setTextColor(getResources().getColor(R.color.white));
}
}
public boolean checkPhoneLogin(String phone, String verifyCode) {
if (OtherUtils.isPhoneNumValid(phone)) {
if (OtherUtils.isVerifyCodeValid(verifyCode)) {
if (OtherUtils.checkNetWorkState(this)) {
return true;
} else {
ToastUtils.showShort(this, "當(dāng)前無網(wǎng)絡(luò)連接");
}
} else {
ToastUtils.showShort(this, "驗(yàn)證碼錯(cuò)誤");
}
} else {
ToastUtils.showShort(this, "手機(jī)格式錯(cuò)誤");
}
return false;
}
public boolean checkUserNameLogin(String userName, String password) {
if (OtherUtils.isUsernameVaild(userName)) {
if (OtherUtils.isPasswordValid(password)) {
if (OtherUtils.checkNetWorkState(this)) {
return true;
} else {
ToastUtils.showShort(this, "當(dāng)前無網(wǎng)絡(luò)連接");
}
} else {
ToastUtils.showShort(this, "密碼過短");
}
} else {
ToastUtils.showShort(this, "用戶名不符合規(guī)范");
}
return false;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_login:
if (isPhoneLogin) {
mLoginPresenter.phoneLogin(etLogin.getText().toString(), etPassword.getText().toString());
} else {
mLoginPresenter.userNameLogin(etLogin.getText().toString(), etPassword.getText().toString());
}
break;
case R.id.btn_verify_code:
mLoginPresenter.sendVerifyCode(etLogin.getText().toString());
break;
case R.id.btn_register:
break;
}
}
@Override
public void loginSuccess() {
dismissLoading();
invoke(this, MainActivity.class);
finish();
}
@Override
public void loginFailed(int status, String msg) {
dismissLoading();
showMsg("登錄失敺课:" + msg);
}
@Override
public void usernameError(String errorMsg) {
etLogin.setError(errorMsg);
}
@Override
public void phoneError(String errorMsg) {
etLogin.setError(errorMsg);
}
@Override
public void passwordError(String errorMsg) {
etPassword.setError(errorMsg);
}
@Override
public void verifyCodeError(String errorMsg) {
showMsg(errorMsg);
}
@Override
public void verifyCodeFailed(String errorMsg) {
showMsg(errorMsg);
}
@Override
public void verifyCodeSuccess(int reaskDuration, int expireDuration) {
showMsg("注冊短信下發(fā)沼瘫,驗(yàn)證碼 " + expireDuration / 60 + " 分鐘內(nèi)有效!");
OtherUtils.startTimer(new WeakReference<TextView>(tvVerifyCode), "驗(yàn)證碼", reaskDuration, 1);
}
@Override
public void showLoading() {
showOnLoading(true);
}
@Override
public void dismissLoading() {
showOnLoading(false);
}
@Override
public void showMsg(String msg) {
showToast(msg);
}
@Override
public void showMsg(int msgId) {
showMsg(msgId);
}
@Override
public Context getContext() {
return this;
}
}
Model 的定義
1咙俩、UserInfo 要實(shí)現(xiàn)序列化接口
public class UserInfo extends IDontObfuscate{
private String nickname;
private int sex;
private String headPic;
private String sigId;
private String userId;
private String sdkAppId;
private String sdkAccountType;
private String token;
public UserInfo() {
}
public UserInfo(String nickname, int sex, String headPic, String userId) {
this.nickname = nickname;
this.sex = sex;
this.headPic = headPic;
this.userId = userId;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getHeadPic() {
return headPic;
}
public void setHeadPic(String headPic) {
this.headPic = headPic;
}
public String getSigId() {
return sigId;
}
public void setSigId(String sigId) {
this.sigId = sigId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getSdkAppId() {
return sdkAppId;
}
public void setSdkAppId(String sdkAppId) {
this.sdkAppId = sdkAppId;
}
public String getSdkAccountType() {
return sdkAccountType;
}
public void setSdkAccountType(String sdkAccountType) {
this.sdkAccountType = sdkAccountType;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
2耿戚、UserInfoCache 也要實(shí)現(xiàn)序列化接口
賬號字段在后面的文章介紹,這里直接貼出來阿趁。
public class UserInfoCache extends IDontObfuscate{
public static void saveCache(Context context, UserInfo info){
ACache.get(context).put("user_id",info.getUserId());
ACache.get(context).put("nickname",info.getNickname());
ACache.get(context).put("head_pic",info.getHeadPic());
ACache.get(context).put("sig_id",info.getSigId());
ACache.get(context).put("token",info.getToken());
ACache.get(context).put("sdk_app_id",info.getSdkAppId());
ACache.get(context).put("adk_account_type",info.getSdkAccountType());
ACache.get(context).put("sex",info.getSex());
if (info.getSdkAppId() != null && TextUtils.isDigitsOnly(info.getSdkAccountType())){
Constants.IMSDK_ACCOUNT_TYPE = Integer.parseInt(info.getSdkAccountType());
}
}
public static String getUserId(Context context){
return ACache.get(context).getAsString("user_id");
}
public static String getNickname(Context context){
return ACache.get(context).getAsString("nickname");
}
public static String getHeadPic(Context context){
return ACache.get(context).getAsString("head_pic");
}
public static String getSigId(Context context){
return ACache.get(context).getAsString("sig_id");
}
public static String getToken(Context context){
return ACache.get(context).getAsString("token");
}
public static String getSdkAccountType(Context context){
return ACache.get(context).getAsString("adk_account_type");
}
public static String getSdkAppId(Context context){
return ACache.get(context).getAsString("sex");
}
public static String getSex(Context context){
return ACache.get(context).getAsString("sdk_app_id");
}
public static void clearCache(Context context){
ACache.get(context).remove("user_id");
ACache.get(context).remove("nickname");
ACache.get(context).remove("head_pic");
ACache.get(context).remove("sig_id");
ACache.get(context).remove("token");
ACache.get(context).remove("adk_account_type");
ACache.get(context).remove("sdk_app_id");
ACache.get(context).remove("sex");
}
}
登錄實(shí)現(xiàn)運(yùn)行效果
參考:
http://www.reibang.com/p/9a6845b26856
http://www.reibang.com/p/f6252719b3af
140套Android優(yōu)秀開源項(xiàng)目源碼膜蛔,領(lǐng)取地址:http://mp.weixin.qq.com/s/afPGHqfdiApALZqHsXbw-A