今天記錄一下自己的使用的分層框架,也是現在的主流框架MVP+Rxjava,好處就不多說了,直接主題吧。
首先對照圖片來總體看一下mvp是怎么工作的
步驟一:view調用presenter的方法,通知presenter自己需要哪些數據
步驟二:presenter調用model的方法獲取數據
步驟三:model獲取到數據以后返回給presenter
步驟四:presenter獲取到數據以后再傳遞給view進行顯示
上面的流程就可以清晰地看出presenter就是一個中間人的角色,它與隔離了view和model,已達到解耦的目的,view只負責顯示數據,model只負責獲取數據(網絡或是本地)
具體如何實現請看我的github地址github
下面就根據github的內容講解
代碼的結構以功能模塊來劃分,我這里是登錄模塊,還是比較清晰地。
base
base包里面的內容集中抽象了各層的基礎操作
一、view
View也就是Activity(或是Fragment)
這里有一個BaseView是所有View的接口,定義了大部分view會有的操作,來看代碼:
public interface BaseView {
//顯示等待框
void showLoading();
//顯示錯誤提示
void showError();
}
接口里面只有兩個方法,一般的view都會有這兩個操作,如果你有更多可以添加更多方法。
二、model
public interface BaseModel {
}
model是一個空接口,因為每一個模塊使用到的數據會不一樣,具體的數據由他的實現類來確定,比如我們這里需要獲取網絡的User數據,那么BaseModel的實現就應該有getUser()這樣的方法。
三、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的內容比較多,畢竟他是model和view的中間人,所以它要同時持有這兩個引用
這里使用了兩個泛型M、V,BasePresenter并不知道model和view的具體類型,里面提供了一個初始化Model和view的方法。
到了這里base寫好了,下面就來到了我們的Login模塊
登錄的邏輯很簡單,輸入用戶名和密碼,顯示登錄結果(失敗或是成功),成功會返回User對象
LoginContract里面定義了兩個接口和一個抽象類,你也可以分開了寫,但是就會造成類太多開起來亂。
public interface LoginContract {
interface LoginView extends BaseView{
//在LoginPresenter中調用這個方法顯示數據
void setUser(TestData<User> user);
}
interface LoginModel extends BaseModel{
//獲取數據庫的數據
void getUser(String name , String password, MVPListener listener);
}
abstract class LoginPresenter extends BasePresenter<LoginModel,LoginView>{
//調用LoginModel的getUser方法獲取數據,傳遞給LoginView顯示,起到中間人的作用
abstract void getUser(String name,String password);
}
}
這里面的方法都是Login模塊需要用到的數據LoginPresenter的getUser會調用LoginModel的getUser,最后調用LoginView的setUser,也就是剛開始的流程圖的實現。
好了,寫了那么多接口終于到了他們的實現類的時候了
首先看看LoginModel
public class LoginModel implements LoginContract.LoginModel {
Retrofit retrofit;
@Override
public void getUser(String name, String password, final MVPListener mvpListener) {
//做一些網絡請求
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) {
//回調給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只負責獲取數據,所以方法也簡單只有一個getUser(),這里面用到了Retrofit和Rxjava,不會的請自行查閱(這個現在必須會),當然了我們這里只是講解MVP的實現,具體的網絡請求使用什么框架不重要。
這里面還有一個回調接口MVPListener 是用來將獲取到的數據返回給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();
}
});
}
}
方法也是非常的簡單,就是調用Model的getUser方法讓view顯示(注意presenter持有model和view的引用,具體請看BasePresenter)
最后看看view
我們的activity基本都會有一個baseactivity,這里我在baseactivity里面增加一些內容
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()由子類實現。
基類定義好之后就到了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();
}
}
//由父類調用
@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設置了確定的類型,因為是view所以要實現LoginView接口,里面的方法都很簡單,
注:這里講的是MVP的工作流程,所以對于代碼的安全檢查并沒有做
其他的模塊照著login模塊就可以了
最后是一張接口和類的關系圖
在登錄模塊我把兩個接口和一個抽象類放在了一起,其他模塊建議也這樣做