以前根據(jù)參考各路大神的博客苍糠,以及自己對(duì)mvp的理解穷当,早早就寫了這種不成熟的結(jié)合模式(舊版),歷時(shí)半年饺著,他們終于進(jìn)化了椰苟!
一、基礎(chǔ)介紹
1碌更、什么是mvp
mvp是android開發(fā)架構(gòu)之一裕偿,MVP每個(gè)字母分別代表Model、View和Presenter痛单。
①model負(fù)責(zé)處理網(wǎng)絡(luò)數(shù)據(jù)的處理
②presenter是model和view的橋梁嘿棘,負(fù)責(zé)與兩端的通信
③view是視圖層
2、為什么有mvp
mvp的誕生得益于mvc旭绒,mvc確實(shí)貢獻(xiàn)也不小而且也歷經(jīng)風(fēng)雨鸟妙,但mvc的耦合是在太嚴(yán)重,因此mvp就誕生了挥吵。在mvp模式下重父,model和view是不直接進(jìn)行交互的,而是要通過presenter作為橋梁忽匈,這樣一來房午,各層的分工就更加明確,
①model不需要管ui長(zhǎng)什么樣丹允,只要把對(duì)應(yīng)的數(shù)據(jù)給到p即可郭厌,至于p是直接給v還是處理了再給v袋倔,怎么給,都是p的事情折柠。
②而p這里宾娜,p不管model用的post還是get,參數(shù)格式是什么扇售,p只需要向m發(fā)起請(qǐng)求前塔,并在拿到結(jié)果后,把結(jié)果回調(diào)給v即可承冰,不需要管v是要彈出對(duì)話框還是吐司還是跳轉(zhuǎn)頁面华弓。
③v只需要把用戶的動(dòng)作或輸入的內(nèi)容給到p,等待p回應(yīng)即可巷懈!
3该抒、這個(gè)mvp和其他有些不太一樣
這個(gè)mvp主要還是表現(xiàn)在p有寫與眾不同,p層拿到v的實(shí)例后顶燕,通過動(dòng)態(tài)代理得到視圖實(shí)例凑保,view方法的執(zhí)行又是交給InvocationHandler來處理的,這樣可以有效避免view不存在時(shí)涌攻,還執(zhí)行view方法欧引。P中推薦傳入activity實(shí)例,其實(shí)要的是applicat和activity的name恳谎,暫時(shí)還沒想到更好的方法芝此,就這樣用著先。如果傳入的參數(shù)不是activity或者沒傳參數(shù)因痛,記得要在頁面退出的該回收的地方調(diào)用p的detachView方法婚苹。
二、實(shí)現(xiàn)步驟
1鸵膏、基于哪些基礎(chǔ)膊升?
首先一定有個(gè)大前提,就是所有接口返回的數(shù)據(jù)格式谭企,都是一樣的廓译,比如說現(xiàn)在我的接口返回?cái)?shù)據(jù)格式是這樣的
{
"code": 200,
"message": "提交成功"
}
又或者是這樣的
{
"code": 200,
"message": "",
"data": {
"userName": "張三",
"headImage": "http://192.168.3.11/file/user/hf6d4g88a.jpg",
"sex": 1,
"bir": "2020-01-01"
}
}
他們都有些共同點(diǎn),如code和message债查,那么data就是泛型了非区!所以定義的響應(yīng)體接收的類為
public class CallResult<T> {
public int code;
public String message;
public T data;
}
2、封裝Retrofit
考慮到有時(shí)候需要在請(qǐng)求的header中加各種數(shù)據(jù)盹廷,比如說appVersion等征绸,并且上傳文件和普通的接口超時(shí)時(shí)間一般是不同的,因此就有了這種封裝
2.1開始封裝
import android.text.TextUtils;
import android.util.Log;
import com.example.mvp.OnHttpResultListener;
import com.example.mvp.retrofit2.convert.MyConverterFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient.Builder;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
/**
* @author Administrator
*/
public class Retrofit2Manager {
/**
* 默認(rèn)的請(qǐng)求時(shí)間
*/
private long timeOut = 20000L;
/**
* 監(jiān)聽請(qǐng)求過程
*/
private OnHttpResultListener onHttpResultListener;
/**
* 服務(wù)器地址
*/
private final String baseUrl;
/**
* 請(qǐng)求頭
*/
private final Map<String, String> map = new HashMap<>();
/**
* 自定義攔截器
*/
private final List<Class<? extends Interceptor>> interceptors = new ArrayList<>();
/**
* 靜態(tài)方法,入口
*
* @param baseUrl 路徑
* @return this
*/
public static Retrofit2Manager with(String baseUrl) {
return new Retrofit2Manager(baseUrl);
}
/**
* 私有構(gòu)造方法
*
* @param baseUrl 服務(wù)器路徑
*/
private Retrofit2Manager(String baseUrl) {
this.baseUrl = baseUrl;
}
/**
* 超時(shí)時(shí)間
*
* @param timeOut timeOut
* @return this
*/
public Retrofit2Manager setTimeOut(long timeOut) {
this.timeOut = timeOut;
return this;
}
/**
* 監(jiān)聽請(qǐng)求過程
*
* @param onHttpResultListener onHttpResultListener
* @return this
*/
public Retrofit2Manager setOnHttpResultListener(OnHttpResultListener onHttpResultListener) {
this.onHttpResultListener = onHttpResultListener;
return this;
}
/**
* 添加自定義請(qǐng)求頭
*
* @param key key
* @param value value
* @return this
*/
public Retrofit2Manager addHeadres(String key, String value) {
if (TextUtils.isEmpty(key)) {
return this;
}
if (TextUtils.isEmpty(value)) {
value = "";
}
map.put(key, value);
return this;
}
public Retrofit2Manager add(Class<? extends Interceptor> mClass) {
interceptors.add(mClass);
return this;
}
/**
* 返回retrofit2的實(shí)例
*
* @return retrofit2
*/
public Retrofit retrofit() {
Builder okBuilder = new Builder();
okBuilder.readTimeout(this.timeOut, TimeUnit.MILLISECONDS);
okBuilder.writeTimeout(this.timeOut, TimeUnit.MILLISECONDS);
okBuilder.connectTimeout(this.timeOut, TimeUnit.MILLISECONDS);
okBuilder.addInterceptor(new LogInterceptor(map, onHttpResultListener));
try {
for (Class<? extends Interceptor> mClass : interceptors) {
okBuilder.addInterceptor(mClass.newInstance());
}
} catch (Exception e) {
Log.e("mvp[error]", e.getMessage());
e.printStackTrace();
}
return (new Retrofit.Builder()).client(okBuilder.build())
.baseUrl(this.baseUrl)
//自定義解析
.addConverterFactory(MyConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
}
2.2為什么要自定義解析歹垫?
當(dāng)服務(wù)器返回的數(shù)據(jù)為
{
"code": 200,
"message": "",
"data": [
{
"userName": "李四",
"headImage": "http://192.168.3.11/file/user/hf6d4g3243288a.jpg",
"sex": 1,
"bir": "2020-01-01"
},
{
"userName": "張三",
"headImage": "http://192.168.3.11/file/user/hf6d4g84348a.jpg",
"sex": 2,
"bir": "2020-01-17"
},
{
"userName": "王五",
"headImage": "http://192.168.3.11/file/user/hf6d4345436g88a.jpg",
"sex": 1,
"bir": "2020-03-01"
}
]
}
時(shí)剥汤,我們定義的實(shí)體類可以正常接收數(shù)據(jù)。如果接口返回的數(shù)據(jù)是
{
"code": 401,
"message": "登錄狀態(tài)已失效",
"data": null
}
的時(shí)候排惨,你會(huì)發(fā)現(xiàn)直接json轉(zhuǎn)換閃退,因?yàn)閚ull無法轉(zhuǎn)換成list碰凶,因此我們要自己定義解析工廠暮芭,以下是部分代碼
@Override
public T convert(@NonNull ResponseBody value) {
String str = "";
Object var3;
try {
if (value.contentLength() != 0L) {
str = value.source().readUtf8();
var3 = this.convert(str, this.type);
return (T) var3;
}
str = "{\"code\":90000,\"message\":\"服務(wù)器無響應(yīng)\"}";
var3 = this.convert(str, CallResult.class);
} catch (Exception var8) {
//當(dāng)轉(zhuǎn)換出現(xiàn)異常,就用Void進(jìn)行轉(zhuǎn)換
Object var4 = this.convert(str, CallResult.class);
return (T) var4;
} finally {
value.close();
}
return (T) var3;
}
3欲低、創(chuàng)建mvp的各種Base基類
3.1 model層BaseModel
OnHttpResultListener是自己定義的辕宏,用來接收接口參數(shù),對(duì)調(diào)試非常好用砾莱,上線版本可以忽略
import android.util.Log;
import com.example.mvp.OnHttpResultListener;
import com.example.mvp.retrofit2.Retrofit2Manager;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class BaseModel implements OnHttpResultListener {
protected <T> T createService(String ip, Class<T> mClass) {
return Retrofit2Manager.with(ip).setOnHttpResultListener(this).retrofit().create(mClass);
}
@Override
public void onResponse(String method, String requestUrl, String requestHeaders, String requestParams, int responseCode, String responseData) {
String sb = "\n【請(qǐng)求方法】:" + method +
"\n【請(qǐng)求路徑】:" + requestUrl +
"\n【請(qǐng)求頭】:" + requestHeaders +
"\n【請(qǐng)求參數(shù)】:" + requestParams +
"\n【返回參數(shù)】:" + responseData;
Log.d("exccd(mvp-http)", sb);
}
/**
* 發(fā)起請(qǐng)求瑞筐,并且在ui線程執(zhí)行回調(diào)
*
* @param observable observable
* @param <T> 泛型
*/
protected <T> Observable<T> callBackOnUi(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* 發(fā)起請(qǐng)求,并且在新的子線程執(zhí)行回調(diào)
*
* @param observable observable
* @param <T> 泛型
*/
protected <T> Observable<T> callBackOnThread(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread());
}
}
3.2 視圖層IBaseView<T>
為了讓ui實(shí)現(xiàn)這個(gè)回調(diào)更加簡(jiǎn)單腊瑟,我這里將服務(wù)器返回的message和其他所有可能有的提示都通過s字段來返回了聚假,因此不管是“登錄狀態(tài)已失效”還是“網(wǎng)絡(luò)連接異常”闰非,都是在s回調(diào)的膘格。根據(jù)項(xiàng)目不用,有需要的可以拆分财松。
/**
* ui回調(diào)
*
* @param <T> 泛型
*/
public interface IBaseView<T> {
/**
* ui回調(diào)
*
* @param b 是否請(qǐng)求成功
* @param i 類型
* @param s 描述
* @param data 泛型
*/
void onCallBack(boolean b, int i, String s, T data);
}
3.3 Presenter層
這一層最關(guān)鍵的是IBasePresenter和它的實(shí)現(xiàn)類BasePresenter
3.3.1 IBasePresenter
start方法是發(fā)起請(qǐng)求的入口瘪贱,使用時(shí)需要將model的方法傳進(jìn)去,然后跟視圖綁定起來辆毡。
/**
* presenter需要具備的基礎(chǔ)方法
*/
public interface IBasePresenter {
/**
* 開始發(fā)起請(qǐng)求
*
* @param observable model層返回的obs
* @param iBaseView 視圖菜秦,回調(diào)
* @param <T> 泛型
*/
<T> void start(Observable<CallResult<T>> observable, IBaseView<T> iBaseView);
/**
* 成功回調(diào)
*
* @param iBaseView 視圖、回調(diào)
* @param data 數(shù)據(jù)
* @param <T> 泛型
*/
<T> void viewCallBackSuccess(IBaseView<T> iBaseView, CallResult<T> data);
/**
* 錯(cuò)誤回調(diào)
*
* @param iBaseView 視圖舶掖、回調(diào)
* @param e 錯(cuò)誤信息
* @param <T> 泛型
*/
<T> void viewCallBackError(IBaseView<T> iBaseView, Throwable e);
/**
* 解綁
*/
void detachView();
}
3.3.2 BasePresenter
BasePresenter處理一些回調(diào)球昨,將一些非正常接口請(qǐng)求的結(jié)果轉(zhuǎn)換成中文(指定描述)在回調(diào)給view,這里的所有數(shù)據(jù)都是可以自己定義的访锻,另外如果在某種情況下需要彈窗退出登錄褪尝,建議您新建一個(gè)MyBasePresenter extend BasePresenter,然后重寫onTokenErrorCallBack()即可期犬,但判斷的邏輯需要更改一下河哑。
public abstract class BasePresenter<M> implements IBasePresenter {
/**
* 未授權(quán)登錄,登錄狀態(tài)已失效
*/
public static final int UNAUTHORIZED = 401;
/**
* 請(qǐng)求成功
*/
public static final int SUCCESS = 200;
/**
* 請(qǐng)求被禁止
*/
public static final int FORBIDDEN = 403;
/**
* 接口失效
*/
public static final int NOT_FOUND = 404;
/**
* 請(qǐng)求超時(shí)
*/
public static final int REQUEST_TIMEOUT = 408;
/**
* 服務(wù)器錯(cuò)誤
*/
public static final int INTERNAL_SERVER_ERROR = 500;
/**
* 錯(cuò)誤的網(wǎng)關(guān)
*/
public static final int BAD_GATEWAY = 502;
/**
* 服務(wù)器不可用
*/
public static final int SERVICE_UNAVAILABLE = 503;
/**
* 網(wǎng)絡(luò)超時(shí)
*/
public static final int GATEWAY_TIMEOUT = 504;
/**
* 在默認(rèn)線程回調(diào)
*/
private boolean callBackInLoop = false;
/**
* 是否已經(jīng)解綁了龟虎,避免重復(fù)解綁
*/
private boolean isDttached = false;
/**
* model層
*/
protected M module;
/**
* 視圖
*/
private final Map<Integer, IBaseView<?>> mapView = new HashMap<>();
/**
* 視圖引用
*/
private final Map<Integer, WeakReference<?>> mapReference = new HashMap<>();
/**
* 請(qǐng)求對(duì)象
*/
private final Map<Integer, Disposable> mapDisposables = new HashMap<>();
/**
* 主線程
*/
protected Handler handler;
/**
* 構(gòu)造方法
* 您需要手動(dòng){@link #detachView()}解綁
*/
public BasePresenter() {
onCreate(null);
}
/**
* 構(gòu)造方法
*
* @param activity activity的實(shí)例
*/
public BasePresenter(Activity activity) {
onCreate(activity);
}
/**
* 構(gòu)造方法
*
* @param context 如果這是個(gè)activity的實(shí)例璃谨,那么不需要手動(dòng){@link #detachView()}即可解綁,否則您需要調(diào)用他
*/
public BasePresenter(Context context) {
if (context instanceof Activity) {
onCreate((Activity) context);
} else {
onCreate(null);
}
}
/**
* 初始化方法
*/
private void onCreate(Activity activity) {
this.handler = new Handler(Looper.getMainLooper());
if (this.module == null) {
this.module = this.createModel();
}
if (activity != null) {
String acName = activity.getLocalClassName();
Application app = activity.getApplication();
Application.ActivityLifecycleCallbacks callbacks = new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
if (acName.equals(activity.getLocalClassName())) {
detachView();
app.unregisterActivityLifecycleCallbacks(this);
}
}
};
app.registerActivityLifecycleCallbacks(callbacks);
}
}
/**
* 綁定
*
* @param view 視圖
*/
@SuppressWarnings("all")
private <T, V extends IBaseView<T>> void attachView(V view) {
if (view != null) {
WeakReference<V> weakReference = new WeakReference<V>(view);
mapReference.put(view.hashCode(), weakReference);
ClassLoader classLoader = view.getClass().getClassLoader();
Class<?>[] interfaces = view.getClass().getInterfaces();
InvocationHandler invocationHandler = new MvpViewHandler((IBaseView) weakReference.get());
IBaseView<T> v = (V) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
mapView.put(view.hashCode(), v);
}
}
/**
* 是否在默認(rèn)線程回調(diào)
*
* @param callBackInLoop 如果是false,回調(diào)會(huì)在ui線程處理佳吞,否則就是在發(fā)起默認(rèn)的線程回調(diào)
*/
public void setCallBackInLoop(boolean callBackInLoop) {
this.callBackInLoop = callBackInLoop;
}
/**
* 頁面是否已經(jīng)不存在了
*
* @param iBaseView 視圖
* @param <T> 泛型
* @return true存在拱雏,則回調(diào),否則忽略
*/
protected <T> boolean isViewAttached(IBaseView<T> iBaseView) {
if (iBaseView == null) {
return false;
}
int key = iBaseView.hashCode();
IBaseView<?> view = mapView.get(key);
WeakReference<?> weakReference = mapReference.get(key);
return view != null && weakReference != null && weakReference.get() != null;
}
/**
* 創(chuàng)建module
*
* @return M
*/
protected abstract M createModel();
/**
* 請(qǐng)求是否成功
*
* @param data 響應(yīng)體
* @return 成功true底扳,失敗false
*/
protected <T> boolean isSuccess(CallResult<T> data) {
return data != null && data.code == SUCCESS;
}
/**
* 開始發(fā)起請(qǐng)求
*
* @param observable model層返回的obs
* @param baseView 視圖铸抑、回調(diào)
* @param <T> 泛型
*/
@Override
public <T> void start(Observable<CallResult<T>> observable, IBaseView<T> baseView) {
attachView(baseView);
mapDisposables.put(baseView.hashCode(), observable
.subscribe(data -> viewCallBackSuccess(baseView, data), e -> viewCallBackError(baseView, e)));
}
/**
* 成功回調(diào)
*
* @param view 視圖、回調(diào)
* @param data 數(shù)據(jù)
* @param <T> 泛型
*/
@Override
public <T> void viewCallBackSuccess(IBaseView<T> view, CallResult<T> data) {
if (callBackInLoop) {
_viewCallBackSuccess(view, data);
} else {
if (Looper.myLooper() == Looper.getMainLooper()) {
_viewCallBackSuccess(view, data);
} else {
handler.post(() -> _viewCallBackSuccess(view, data));
}
}
}
/**
* 錯(cuò)誤回調(diào)
*
* @param view 視圖衷模、回調(diào)
* @param e 錯(cuò)誤信息
* @param <T> 泛型
*/
@Override
public <T> void viewCallBackError(IBaseView<T> view, Throwable e) {
if (callBackInLoop) {
_viewCallBackError(view, e);
} else {
if (Looper.myLooper() == Looper.getMainLooper()) {
_viewCallBackError(view, e);
} else {
handler.post(() -> _viewCallBackError(view, e));
}
}
}
/**
* 解綁
*/
@Override
public void detachView() {
if (isDttached) {
return;
}
isDttached = true;
// this.module = null;
this.handler.removeCallbacksAndMessages(null);
for (WeakReference<?> weakReference : mapReference.values()) {
if (weakReference != null) {
weakReference.clear();
}
}
mapReference.clear();
mapView.clear();
try {
for (Disposable disposable : mapDisposables.values()) {
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
}
} catch (Exception e) {
Log.e("mvp[error]", e.getMessage());
}
}
/**
* 統(tǒng)一執(zhí)行成功回調(diào)鹊汛,看{@link #viewCallBackSuccess}
*/
private <T> void _viewCallBackSuccess(IBaseView<T> view, CallResult<T> data) {
if (data.code == UNAUTHORIZED) {
onTokenErrorCallBack(data.message);
}
if (isViewAttached(view)) {
view.onCallBack(data.code == SUCCESS, data.code, data.message, data.data);
}
}
/**
* 統(tǒng)一執(zhí)行錯(cuò)誤回調(diào),看{@link #viewCallBackError}
*/
private <T> void _viewCallBackError(IBaseView<T> view, Throwable e) {
if (isViewAttached(view)) {
try {
if (e instanceof HttpException) {
HttpException httpException = (HttpException) e;
switch (httpException.code()) {
case UNAUTHORIZED:
callBackError(view, "登錄驗(yàn)證已過期");
onTokenErrorCallBack("登錄驗(yàn)證已過期");
break;
case INTERNAL_SERVER_ERROR:
callBackError(view, "服務(wù)器錯(cuò)誤");
break;
case FORBIDDEN:
case NOT_FOUND:
callBackError(view, "無效的請(qǐng)求");
break;
case REQUEST_TIMEOUT:
case GATEWAY_TIMEOUT:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
default:
callBackError(view, httpException.getMessage());
break;
}
} else if (e instanceof ConnectException) {
callBackError(view, "網(wǎng)絡(luò)連接異常阱冶,請(qǐng)檢查您的網(wǎng)絡(luò)狀態(tài)");
} else if (e instanceof SocketTimeoutException) {
callBackError(view, "網(wǎng)絡(luò)連接超時(shí)刁憋,請(qǐng)檢查您的網(wǎng)絡(luò)狀態(tài),稍后重試");
} else if (e instanceof UnknownHostException) {
callBackError(view, "網(wǎng)絡(luò)異常木蹬,請(qǐng)檢查您的網(wǎng)絡(luò)狀態(tài)");
} else if (e instanceof JSONException
|| e instanceof ParseException) {
callBackError(view, "數(shù)據(jù)解析錯(cuò)誤");
} else if (e instanceof SSLHandshakeException) {
callBackError(view, "證書驗(yàn)證失敗");
} else if (e instanceof RuntimeException) {
callBackError(view, "運(yùn)行時(shí)異常");
} else {
callBackError(view, e.toString());
}
} catch (Exception e1) {
Log.e("mvp[error]", e.getMessage());
}
}
}
/**
* {@link #_viewCallBackError}
*/
private <T> void callBackError(IBaseView<T> view, String message) {
view.onCallBack(false, 9000, message, null);
Log.e("excce", "UI回調(diào)錯(cuò)誤信息:" + message);
}
/**
* 返回一個(gè)value類型為Object的哈希表
*
* @param initSize 大小
* @return Map
*/
protected Map<String, Object> createMap(int initSize) {
return new HashMap<>(initSize);
}
/**
* 返回一個(gè)value類型為Integer的哈希表
*
* @param initSize 大小
* @return Map
*/
protected Map<String, Integer> createMapInt(int initSize) {
return new HashMap<>(initSize);
}
/**
* 返回一個(gè)value類型為String的哈希表
*
* @param initSize 大小
* @return Map
*/
protected Map<String, String> createMapStr(int initSize) {
return new HashMap<>(initSize);
}
/**
* 登錄狀態(tài)失效至耻,需要回到登錄頁
*
* @param message message
*/
protected void onTokenErrorCallBack(String message) {
}
/**
* 動(dòng)態(tài)代理
*
* @param <T> 泛型
*/
private class MvpViewHandler<T> implements InvocationHandler {
private final IBaseView<T> mvpView;
MvpViewHandler(IBaseView<T> mvpView) {
this.mvpView = mvpView;
}
@Override
@SuppressWarnings("SuspiciousInvocationHandlerImplementation")
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (isViewAttached(mvpView)) {
return method.invoke(this.mvpView, args);
} else {
Log.d("excci", "頁面已關(guān)閉,不執(zhí)行view層方法镊叁!");
return null;
}
}
}
}
4尘颓、開始使用
經(jīng)過一系列漫長(zhǎng)又復(fù)雜的封裝,終于可以開始使用了意系,這里就以登錄接口和獲取用戶信息接口為例泥耀,展示兩個(gè)不同模塊的使用方法
4.1 根據(jù)接口文檔編寫公共模塊的IModelCom
這其實(shí)就是大家熟悉的Service
/**
* 公共方法模塊
*/
public interface IModelCom {
/**
* 登錄
*
* @param map phone,passWord
*/
@POST("api/user/login")
Observable<CallResult<LoginResponseBean>> login(@Body Map<String, Object> map);
/**
* 注冊(cè)
*
* @param map phone蛔添,code痰催,passWord
*/
@POST("api/user/register")
Observable<CallResult<Void>> register(@Body Map<String, Object> map);
/**
* 忘記密碼
*
* @param map phone,code迎瞧,newPassWord
*/
@POST("api/user/forget")
Observable<CallResult<Void>> forgetPwd(@Body Map<String, Object> map);
}
另一個(gè)IModelUserCenter的寫法差不多的夸溶,忽略。
4.2來看看實(shí)現(xiàn)類
是否在主線程回調(diào)凶硅,看自己咯
public class ModelCom extends BaseModel implements IModelCom {
private static final class IHolder {
static final ModelCom i = new ModelCom();
}
public static ModelCom getInstance() {
return IHolder.i;
}
private final IModelCom api;
private ModelCom() {
api = createService(UrlUtils.IP, IModelCom.class);
}
@Override
public Observable<CallResult<LoginResponseBean>> login(Map<String, Object> map) {
return callBackOnUi(api.login(map));
}
@Override
public Observable<CallResult<Void>> register(Map<String, Object> map) {
return callBackOnUi(api.register(map));
}
@Override
public Observable<CallResult<Void>> forgetPwd(Map<String, Object> map) {
return callBackOnUi(api.forgetPwd(map));
}
}
4.3 契約類
就目前看來缝裁,契約類只定義了P層,原因是m層已經(jīng)是retrofit了足绅,而v層又只有回調(diào)參數(shù)捷绑。
/**
* 公共模塊契約類
*/
public interface IContractCom {
/**
* 公共p層
* {@link com.example.mvpdemo.mvp.presenter.PresenterCom}
*/
interface IPresenterCom extends IBasePresenter {
/**
* 登錄
*
* @param phone 手機(jī)號(hào)
* @param passWord 密碼
*/
void login(String phone, String passWord, IBaseView<LoginResponseBean> view);
/**
* 注冊(cè)
*
* @param phone 手機(jī)號(hào)
* @param passWord 密碼
* @param code 驗(yàn)證碼
* @param view 回調(diào)
*/
void register(String phone, String passWord, String code, IBaseView<Void> view);
/**
* 忘記密碼
*
* @param phone 手機(jī)號(hào)
* @param code 驗(yàn)證碼
* @param newPassWord 新密碼
* @param view 回調(diào)
*/
void forGetPassWord(String phone, String code, String newPassWord, IBaseView<Void> view);
}
}
4.4 P的實(shí)現(xiàn)
/**
* 公共P
*/
public class PresenterCom extends BasePresenter<IModelCom> implements IContractCom.IPresenterCom {
public PresenterCom(Activity activity) {
super(activity);
}
@Override
public void login(String phone, String passWord, IBaseView<LoginResponseBean> view) {
Map<String, Object> map = createMap(2);
map.put("phone", phone);
map.put("passWord", passWord);
start(module.login(map).map(resp -> {
if (isSuccess(resp)) {
//如果登錄成功了,則保存token氢妈,用戶名等信息
Log.i("loginResult", resp.data.token);
Log.i("loginResult", resp.data.userName);
Log.i("loginResult", String.valueOf(resp.data.roleId));
}
return resp;
}), view);
}
@Override
public void register(String phone, String passWord, String code, IBaseView<Void> view) {
Map<String, Object> map = createMap(3);
map.put("phone", phone);
map.put("passWord", passWord);
map.put("code", code);
start(module.register(map), view);
}
@Override
public void forGetPassWord(String phone, String code, String newPassWord, IBaseView<Void> view) {
Map<String, Object> map = createMap(3);
map.put("phone", phone);
map.put("code", code);
map.put("newPassWord", newPassWord);
start(module.forgetPwd(map), view);
}
@Override
public IModelCom createModel() {
return ModelCom.getInstance();
}
}
4.5 頁面的使用
這是登錄頁的調(diào)用方法
public class LoginActivity extends AppCompatActivity {
private ActivityLoginBinding view;
private IContractCom.IPresenterCom presenterCom;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = ActivityLoginBinding.inflate(getLayoutInflater());
setContentView(view.getRoot());
presenterCom = new PresenterCom(this);
view.btnLogin.setOnClickListener(v -> {
String phone = view.edtPhone.getText().toString();
String pwd = view.edtPwd.getText().toString();
if (TextUtils.isEmpty(phone) || TextUtils.isEmpty(pwd)) {
return;
}
presenterCom.login(phone, pwd, (b, i, s, data) -> {
if (b) {
Toast.makeText(this, "登錄成功", Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, MainActivity.class));
finish();
} else {
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
});
});
}
}
這是首頁的調(diào)用方法
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding view;
private IContractUser.IPresenterUser presenterUser;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(view.getRoot());
//在這種使用方法下粹污,不需要手動(dòng)解綁
presenterUser = new PresenterUser(this);
presenterUser.getUserInfo((b, i, s, data) -> {
if (b) {
String str = "用戶名:" + data.userName + "\n生日:" + data.bir;
view.tvMsg.setText(str);
} else {
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
});
}
}
三、其他說明
3.1 demo下載地址
3.2 我有一個(gè)想法
一般修改手機(jī)號(hào)首量、登錄和注冊(cè)壮吩,都會(huì)用到獲取驗(yàn)證碼的功能进苍,而這個(gè)功能并不需要在修改手機(jī)號(hào)的模塊和登錄注冊(cè)模塊都寫一次實(shí)現(xiàn)邏輯,要是有需要鸭叙,直接將獲取驗(yàn)證碼弄成單獨(dú)的模塊可能會(huì)更好
public interface IContractPhoneCode {
interface IPresenterPhoneCode extends IBasePresenter {
void getCode(String phone);
}
}
如在登錄頁使用的時(shí)候觉啊,就這樣用
public class LoginActivity extends AppCompatActivity {
private ActivityLoginBinding view;
private IContractCom.IPresenterCom presenterCom;
private IContractPhoneCode.IPresenterPhoneCode presenterPhoneCode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = ActivityLoginBinding.inflate(getLayoutInflater());
setContentView(view.getRoot());
presenterCom = new PresenterCom(this);
presenterPhoneCode = new PresenterPhoneCode(this);
view.btnLogin.setOnClickListener(v -> {
String phone = view.edtPhone.getText().toString();
String pwd = view.edtPwd.getText().toString();
if (TextUtils.isEmpty(phone) || TextUtils.isEmpty(pwd)) {
return;
}
presenterCom.login(phone, pwd, (b, i, s, data) -> {
if (b) {
Toast.makeText(this, "登錄成功", Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, MainActivity.class));
finish();
} else {
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
});
});
view.btnGetCode.setOnClickListener(v->{
String phone = view.edtPhone.getText().toString();
presenterPhoneCode.getCode(phone,(b,i,s,d)->{
});
});
}
}
3.3 說明
知識(shí)無邊無際,文中如有不足之處沈贝,還望海涵杠人,若能提出您的高見,我將不勝感激宋下。