MVP
下面這個圖,就解釋了MVP的數據流:
其中MVP,核心爲Presenter,View與Model沒有數據之間的交互。而MVC,核心爲Controller,View與Model有數據之間的交互。
所以MVP最大的優點就是Model與View之間的完全解耦。
MVP的權威資料
android 架構
https://github.com/googlesamples/android-architecture
todo-mvp 架構 github網站
https://github.com/googlesamples/android-architecture/tree/todo-mvp/
當你下載google官方的Demo後,建議你好好看看。
如果你一臉懵逼,可能你需要一些說明的資料:
Android官方MVP架構項目解析
http://www.android-doc.com/androiddocs/2017/0803/1218.html
這樣參照對比的看,你纔會看出來一點門道的。
MVP Demo 登錄操作
這是我參考官方Demo,再結合Android中用到的MVP模式
https://blog.csdn.net/weixin_28774815/article/details/80960779
一個文件,一行代碼的慢慢debug,才完成的Demo。
先看效果圖:
也就是輸入用戶名:test,密碼:1234,點擊CLICK,完成登錄操作,如果登錄成功,彈出登錄成功提示,或者彈出登錄失敗提示。
代碼的整件目錄如下:
先與界面佈局文件:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MVPDemoMainActivity">
<EditText
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="input user name"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<EditText
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="input password"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/user_name"/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click"
app:layout_constraintTop_toBottomOf="@id/password"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>
然後,再直接從官網將BasePresenter.java和BaseView.java複製過來。
public interface BasePresenter {
void start();
}
public interface BaseView<T> {
void setPresenter(T presenter);
}
再定義一個IModel接口:
public interface IModel {
}
然後實現LoginMode類,主要是實現一個登錄的操作:
public class LoginMode implements IModel {
private String mUserName = "test";
private String mPassWord = "1234";
private LoginLisentener loginLisentener;
public void login(String username, String password) {
if (loginLisentener == null) {
return;
}
if (mUserName.equals(username) && mPassWord.equals(password)){
loginLisentener.onSeccess();
} else {
loginLisentener.onFails();
}
}
public void setLoginLisentener(LoginLisentener loginLisentener) {
this.loginLisentener = loginLisentener;
}
public interface LoginLisentener {
void onSeccess();
void onFails();
}
}
下面再實現一個LoginContract類,這就是官方說的契約類。
在這個類中, 我們定義了View 接口,主要有二個接口,分別來表示登錄時成功和失敗的界面相關的操作。
再定義了Presenter 接口,主要是爲了執行登錄操作的總入口。
public class LoginContract {
public interface View extends BaseView<Presenter> {
void onLoginSeccess();
void onLoginFails();
}
public interface Presenter extends BasePresenter {
void onLogin(String name, String password);
}
}
下面就是我們的核心類LoginPresenter,一般來說view和model都是其成員方法,一個構造方法,還有就是其定義的核心登錄操作接口onLogin,而start一般就是實現一些初始化操作:
public class LoginPresenter implements LoginContract.Presenter{
private static final String TAG = "LoginPresenter";
private final LoginContract.View mView;
private LoginMode loginMode;
public LoginPresenter(final LoginContract.View mView) {
this.mView = mView;
this.loginMode = new LoginMode();
loginMode.setLoginLisentener(new LoginMode.LoginLisentener() {
@Override
public void onSeccess() {
mView.onLoginSeccess();
}
@Override
public void onFails() {
mView.onLoginFails();
}
});
}
@Override
public void onLogin(String name, String password) {
loginMode.login(name, password);
}
@Override
public void start() {
Log.i(TAG,"start");
}
}
而最後,在MVPDemoMainActivity界面登錄時,我們就是隻要調用:loginPresenter.onLogin(getUserName(),getPassword());
就可以完成登錄的邏輯了。
public class MVPDemoMainActivity extends AppCompatActivity implements LoginContract.View{
private static final String TAG = "MVPDemoMainActivity";
private EditText user_name;
private EditText password;
private Button button;
private LoginContract.Presenter loginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvpdemo_main);
init();
}
private void init() {
user_name = (EditText) findViewById(R.id.user_name);
password = (EditText) findViewById(R.id.password);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
loginPresenter.onLogin(getUserName(),getPassword());
}
});
loginPresenter = new LoginPresenter(this);
loginPresenter.start();
}
private String getUserName(){
return user_name.getText().toString();
}
private String getPassword(){
return password.getText().toString();
}
@Override
public void onLoginSeccess() {
Toast.makeText(getApplicationContext(), "登陸成功!", Toast.LENGTH_LONG).show();
Log.i(TAG,"onLoginSeccess");
}
@Override
public void onLoginFails() {
Toast.makeText(getApplicationContext(), "登陸失敗!", Toast.LENGTH_LONG).show();
Log.i(TAG,"onLoginFails");
}
@Override
public void setPresenter(LoginContract.Presenter presenter) {
loginPresenter = presenter;
}
@Override
protected void onDestroy() {
super.onDestroy();
this.loginPresenter = null;
}
}
MVP+OKhttp Demo 獲取網絡信息
從最簡單的Android MVP講起
https://www.jianshu.com/p/4736ebe1114b
我們參考上面這個例子,來實現一個mvp+okhttp的例子
輸入一個網絡地址,獲取網絡信息,再顯示出來。
成功獲取網絡信息:
獲取網絡信息失敗:
具體代碼結構:
添加庫依賴 app\build.gradle
dependencies {
implementation("com.squareup.okhttp3:okhttp:3.14.1")
implementation("com.squareup.okio:okio:2.2.2")
}
在AndroidManifest.xml文件中添加網絡權限
<uses-permission android:name="android.permission.INTERNET"/>
MainView.java代碼:
public interface MainView {
void getMessage(String message);
void error();
}
MainModel.java代碼
public class MainModel {
public Call getData(String url) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
return client.newCall(request);
}
}
MainPresenter.java代碼:
public class MainPresenter {
private MainView mainView;
private MainModel model;
public MainPresenter(MainView mainView) {
this.mainView = mainView;
model=new MainModel();
}
public void getUrlData(String url){
model.getData(url).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
mainView.error();
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
mainView.getMessage(response.body().string());
}
});
}
}
MVPDemo02MainActivity .java
public class MVPDemo02MainActivity extends AppCompatActivity implements MainView{
private TextView resultTextView;
private MyHandler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvpdemo02_main);
init();
}
private void init() {
Button button = (Button)findViewById(R.id.button);
handler = new MyHandler(this);
final EditText editText = (EditText)findViewById(R.id.et_url);
resultTextView = (TextView)findViewById(R.id.tv_result);
final MainPresenter presenter = new MainPresenter(this);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String url = editText.getText().toString();
presenter.getUrlData(url);
}
});
}
@Override
public void getMessage(String message) {
Message msg = handler.obtainMessage(0, message);
handler.sendMessage(msg);
}
@Override
public void error() {
Message msg = handler.obtainMessage(1, "error");
handler.sendMessage(msg);
}
private static class MyHandler extends Handler {
private WeakReference<MVPDemo02MainActivity> reference;
private MyHandler(MVPDemo02MainActivity activity) {
reference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MVPDemo02MainActivity activity = reference.get();
switch (msg.what) {
case 0:
activity.resultTextView.setText(msg.obj.toString());
break;
case 1:
activity.resultTextView.setText(msg.obj.toString());
break;
}
}
}
}