今天記錄一下自己的使用的分層框架买乃,也是現(xiàn)在的主流框架MVP+Rxjava锈死,好處就不多說了珍剑,直接主題吧掸宛。
首先對照圖片來總體看一下mvp是怎么工作的
步驟一:view調(diào)用presenter的方法,通知presenter自己需要哪些數(shù)據(jù)
步驟二:presenter調(diào)用model的方法獲取數(shù)據(jù)
步驟三:model獲取到數(shù)據(jù)以后返回給presenter
步驟四:presenter獲取到數(shù)據(jù)以后再傳遞給view進行顯示
上面的流程就可以清晰地看出presenter就是一個中間人的角色招拙,它與隔離了view和model唧瘾,已達到解耦的目的措译,view只負責(zé)顯示數(shù)據(jù),model只負責(zé)獲取數(shù)據(jù)(網(wǎng)絡(luò)或是本地)
具體如何實現(xiàn)請看我的github地址github
下面就根據(jù)github的內(nèi)容講解
代碼的結(jié)構(gòu)以功能模塊來劃分饰序,我這里是登錄模塊领虹,還是比較清晰地。
base
base包里面的內(nèi)容集中抽象了各層的基礎(chǔ)操作
一求豫、view
View也就是Activity(或是Fragment)
這里有一個BaseView是所有View的接口塌衰,定義了大部分view會有的操作,來看代碼:
public interface BaseView {
//顯示等待框
void showLoading();
//顯示錯誤提示
void showError();
}
接口里面只有兩個方法蝠嘉,一般的view都會有這兩個操作猾蒂,如果你有更多可以添加更多方法。
二是晨、model
public interface BaseModel {
}
model是一個空接口肚菠,因為每一個模塊使用到的數(shù)據(jù)會不一樣,具體的數(shù)據(jù)由他的實現(xiàn)類來確定罩缴,比如我們這里需要獲取網(wǎng)絡(luò)的User數(shù)據(jù)蚊逢,那么BaseModel的實現(xiàn)就應(yīng)該有g(shù)etUser()這樣的方法。
三箫章、presenter
public abstract class BasePresenter<M, V> {
public M model;
V view;
public WeakReference<V> mViewRef;
public void attachModelView(M pModel, V pView) {
this.model = pModel;
mViewRef = new WeakReference<>(pView);
}
public boolean isAttach() {
return mViewRef != null && mViewRef.get() != null;
}
public V getView() {
if (isAttach()) {
return mViewRef.get();
} else {
return null;
}
}
public void onDettach() {
if (null != mViewRef) {
mViewRef.clear();
mViewRef = null;
}
}
}
presenter的內(nèi)容比較多烙荷,畢竟他是model和view的中間人,所以它要同時持有這兩個引用
這里使用了兩個泛型M檬寂、V终抽,BasePresenter并不知道m(xù)odel和view的具體類型,里面提供了一個初始化Model和view的方法桶至。
到了這里base寫好了昼伴,下面就來到了我們的Login模塊
登錄的邏輯很簡單,輸入用戶名和密碼镣屹,顯示登錄結(jié)果(失敗或是成功)圃郊,成功會返回User對象
LoginContract里面定義了兩個接口和一個抽象類,你也可以分開了寫女蜈,但是就會造成類太多開起來亂持舆。
public interface LoginContract {
interface LoginView extends BaseView{
//在LoginPresenter中調(diào)用這個方法顯示數(shù)據(jù)
void setUser(TestData<User> user);
}
interface LoginModel extends BaseModel{
//獲取數(shù)據(jù)庫的數(shù)據(jù)
void getUser(String name , String password, MVPListener listener);
}
abstract class LoginPresenter extends BasePresenter<LoginModel,LoginView>{
//調(diào)用LoginModel的getUser方法獲取數(shù)據(jù),傳遞給LoginView顯示伪窖,起到中間人的作用
abstract void getUser(String name,String password);
}
}
這里面的方法都是Login模塊需要用到的數(shù)據(jù)LoginPresenter的getUser會調(diào)用LoginModel的getUser逸寓,最后調(diào)用LoginView的setUser,也就是剛開始的流程圖的實現(xiàn)覆山。
好了竹伸,寫了那么多接口終于到了他們的實現(xiàn)類的時候了
首先看看LoginModel
public class LoginModel implements LoginContract.LoginModel {
Retrofit retrofit;
@Override
public void getUser(String name, String password, final MVPListener mvpListener) {
//做一些網(wǎng)絡(luò)請求
initHttp();
retrofit.create(ApiService.class).reLogin(name, password).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<TestData<User>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(TestData<User> value) {
//回調(diào)給presenter
mvpListener.onSuccess(value);
}
@Override
public void onError(Throwable e) {
mvpListener.onError();
}
@Override
public void onComplete() {
}
});
}
public void initHttp() {
retrofit = new Retrofit.Builder().baseUrl("http://wnd.agri114.cn/wndms/")
.client(new OkHttpClient())
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().setDateFormat("yyyy-MM-dd").create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
}
LoginModel只負責(zé)獲取數(shù)據(jù),所以方法也簡單只有一個getUser()汹买,這里面用到了Retrofit和Rxjava佩伤,不會的請自行查閱(這個現(xiàn)在必須會)聊倔,當(dāng)然了我們這里只是講解MVP的實現(xiàn)晦毙,具體的網(wǎng)絡(luò)請求使用什么框架不重要生巡。
這里面還有一個回調(diào)接口MVPListener 是用來將獲取到的數(shù)據(jù)返回給presenter的,也就是流程圖的步驟三见妒。
public interface MVPListener<T> {
void onSuccess(T login);
void onError();
}
兩個方法孤荣,不多解釋。這里的地址是可以使用的须揣,大家免費測試盐股,用戶名是18805174084,密碼111111
再來看看LoginPresenter
public class LoginPresenter extends LoginContract.LoginPresenter {
@Override
void getUser(String name,String password) {
final LoginContract.LoginView view = getView();
view.showLoading();
model.getUser(name, password, new MVPListener() {
@Override
public void onSuccess(Object login) {
if(login instanceof TestData){
view.setUser((TestData<User>) login);
}
}
@Override
public void onError() {
view.showError();
}
});
}
}
方法也是非常的簡單耻卡,就是調(diào)用Model的getUser方法讓view顯示(注意presenter持有model和view的引用疯汁,具體請看BasePresenter)
最后看看view
我們的activity基本都會有一個baseactivity,這里我在baseactivity里面增加一些內(nèi)容
public abstract class BaseActivity<T extends BasePresenter, M extends BaseModel> extends AppCompatActivity {
public T mPresenter;
public M mModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
mPresenter = CreatUtil.getT(this, 0);
mModel = CreatUtil.getT(this,1);
mPresenter.attachModelView(mModel,this);
initView();
}
public abstract void initView();
public abstract int getLayoutId();
}
這里就是mvp工作開始的地方卵酪,BaseActivity<T extends BasePresenter, M extends BaseModel>定義了兩個泛型并且在onCreate方法中初始化他們幌蚊,初始化使用反射原理
public static <T> T getT(Object o, int i) {
try {
return ((Class<T>) ((ParameterizedType) (o.getClass().getGenericSuperclass())).getActualTypeArguments()[i]).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
然后初始化presenter的Model和View,至于initView()和getLayoutId()由子類實現(xiàn)溃卡。
基類定義好之后就到了LoginActivity
public class LoginActivity extends BaseActivity<LoginPresenter, LoginModel> implements LoginContract.LoginView {
AutoCompleteTextView name;
EditText password;
Button mButton;
@Override
public void showLoading() {
Toast.makeText(this, "showLoading", Toast.LENGTH_LONG).show();
}
@Override
public void showError() {
Toast.makeText(this, "Error", Toast.LENGTH_LONG).show();
}
@Override
public void setUser(TestData<User> user) {
if (user != null) {
Toast.makeText(this, user.getUser().getTel()+"", Toast.LENGTH_LONG).show();
}
}
//由父類調(diào)用
@Override
public void initView() {
name = (AutoCompleteTextView) findViewById(R.id.email);
password = (EditText) findViewById(R.id.password);
mButton = (Button) findViewById(R.id.email_sign_in_button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPresenter.getUser(name.getText().toString(), password.getText().toString());
}
});
}
@Override
public int getLayoutId() {
return R.layout.activity_login;
}
}
繼承了BaseActivity并且給Model和View設(shè)置了確定的類型溢豆,因為是view所以要實現(xiàn)LoginView接口,里面的方法都很簡單瘸羡,
注:這里講的是MVP的工作流程漩仙,所以對于代碼的安全檢查并沒有做
其他的模塊照著login模塊就可以了
最后是一張接口和類的關(guān)系圖
在登錄模塊我把兩個接口和一個抽象類放在了一起,其他模塊建議也這樣做