MVC:
M:model,模型(业务逻辑):进行数据库的读写等等
V:view,视图(控件,XML):进行数据的显示
C:controller(Activity等界面逻辑,接受V层输入同时传出给M层进行逻辑运算);
其中:button是view,方法是controller.某个属性是model :但是button也可以直接在逻辑中设置属性,所以这样就会和model混淆;
View可以直接和model进行数据之间的传递,也可以通过controller传给model;
缺点就是:View和Controller分工不明显,同时会让Controller的Activity越来越臃肿;
举例:
C+V层:
View层:XML布局文件activity_mvcpattern代表的就是View层,用来显示布局,与用户进行交互。
Controller层:MVCActivity代表的是Controller层,View层会传递请求至Controller,Controller控制Model层进行业务的更新。
/**
* @author hongri
* @date 2018/9/4
*
* Controller层
*/
public class MVCActivity extends AppCompatActivity implements View.OnClickListener {
private Button btnRequest;
private ImageView iv;
private MVCHttpRequestModel mvcHttpRequestModel;
private static final String requestUrl = "https://avatar.csdnimg.cn/5/7/C/1_u012440207.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* xml文件属于理论上的View层
*/
setContentView(R.layout.activity_mvcpattern);
mvcHttpRequestModel = new MVCHttpRequestModel();
btnRequest = findViewById(R.id.btnRequest);
iv = findViewById(R.id.iv);
btnRequest.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnRequest:
/**
* Controller层将网络请求业务逻辑交由Model层处理
*/
mvcHttpRequestModel.onHttpRequest(requestUrl, new MVCRequestCallback() {
@Override
public void onSuccess(Object successData) {
if (successData != null) {
iv.setImageBitmap((Bitmap)successData);
}
}
@Override
public void onFailure(Object failureData) {
Toast.makeText(MVCActivity.this, failureData.toString(), Toast.LENGTH_LONG).show();
}
});
break;
default:
break;
}
}
}
M层:
这里的MVCHttpRequestModel便属于Model层,Model层主要用于处理数据库、网络请求等一系列复杂的耗时处理,Model数据请求完成后,通知View进行UI的更新。
/**
* @author hongri
* @date 2018/9/4
*
* Model层
*/
public class MVCHttpRequestModel implements MVCHttpRequestInterface {
@Override
public void onHttpRequest(final String urlString, final MVCRequestCallback listener) {
new RequestTask(listener).execute(urlString);
}
public class RequestTask extends AsyncTask<String, Void, Bitmap> {
private MVCRequestCallback listener;
public RequestTask(MVCRequestCallback listener) {
this.listener = listener;
}
@Override
protected Bitmap doInBackground(String... strings) {
Bitmap bitmap = null;
InputStream inputStream;
try {
URL url = new URL(strings[0]);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(10 * 1000);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
inputStream = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
} else {
bitmap = null;
}
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
/**
*Model层请求完数据后回调给Controller层,Controller层更新UI
*/
if (bitmap != null) {
listener.onSuccess(bitmap);
} else {
listener.onFailure("获取失败");
}
}
}
}
因此MVP将MVC中的V和C结合生成MVP中的V,引入新概念Presenter
MVP:
M层:业务层,具体逻辑功能实现
V层:视图层(包含了Activity,不同于MVC的V),用作显示
P层:连接层,用于连接M和V层桥梁
P层持有M层引用,为了调用M层中逻辑去计算结果;同时P层持有V引用,为了拿到M层逻辑后去显示在界面上;
V层持有P层引用,为了向P层中传用户参数或者启动P层中的调用逻辑;
优点:1.模型M与视图层V完全解耦,修改不会互相影响
2.可将一个Presenter用于多个视图,就算视图层修改大量逻辑,Presenter层不需要大量改动;
缺点:V层需要定义大量接口和P层进行交互,因此用到的Activity要实现所有的V中的接口方法,比较复杂;
下面举个简单的连网络例子:
看下所谓的MVP:M,V,P层都由接口来进行和其他层的数据传递;
Model层:
按理应该Bean类也算在Model层,由于此例只用String做Bean类,因此这里省去Bean类的构造
BaseNet.java:
定义一个请求服务器的方法
package app.project.mvp.model;
/**
* Created by QLY on 2016/9/1.
*/
public interface BaseNet {
String getDataFromServer();
}
BaseNetIml.java:
实现类
package app.project.mvp.model;
import android.util.Log;
/**
* Created by QLY on 2016/9/1.
*/
public class BaseNetIml implements BaseNet{
@Override
public String getDataFromServer() {
String data = "this is data from server"
return data;
}
}
Presenter层:
BasePresenter .java:
package app.project.mvp.presenter;
/**
* Created by QLY on 2016/9/1.
* 页面业务逻辑(一般都要参与服务器交互)
*/
public interface BasePresenter {
void loadData();
void loadResult();
}
PresenterIml.java
负责拿到BaseView 、BaseNet的引用,从而实现交互
package app.project.mvp.presenter;
import android.util.Log;
import app.project.mvp.model.BaseNet;
import app.project.mvp.model.BaseNetIml;
import app.project.mvp.view.BaseView;
/**
* Created by QLY on 2016/9/1.
*/
public class PresenterIml implements BasePresenter {
private BaseView mvpView;
private BaseNet baseNet;
public PresenterIml(BaseView mvpView) {
this.mvpView = mvpView;
baseNet = new BaseNetIml();
}
@Override
public void loadData() {
mvpView.loading();
baseNet.getDataFromServer();
}
@Override
public void loadResult() {
Log.i("test", "loadResult: 服务器返回结果。。。");
mvpView.cancelLoading();
}
}
View层:
BaseView.java:
一共2个方法,注释很清晰,当然我们的view(activity)要实现这个接口
package app.project.mvp.view;
/**
* Created by QLY on 2016/8/31.
*
* UI逻辑接口,一般有activity、Fragment实现
*/
public interface BaseView {
/**
* 显示加载进度
*/
void showLoadResult(String data);
/**
* 隐藏加载进度
*/
void cancelLoading();
}
接下来就是activity了:
package app.project.mvp.view;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.Random;
import app.project.R;
import app.project.mvp.presenter.BasePresenter;
import app.project.mvp.presenter.PresenterIml;
public class TestActivity extends AppCompatActivity implements BaseView {
private PresenterIml mPresenter;
boolean flag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
mPresenter = new PresenterIml(this);
//开始请求服务器
mPresenter.loadData();
//加载完成
mPresenter.loadResult();
}
@Override
public void showLoadResult(String data) {
//...
Toast.make(data);
}
@Override
public void cancelLoading() {
Log.i("test", "loading 加载完成。。。");
}
}
可以看看这个: http://www.jcodecraeer.com/a/anzhuokaifa/2017/1020/8625.html
优化:
因为P的实现类持有V层的引用,这样会造成内存泄漏;需要在P层新增destroy方法及时将view引用置空;
eg:
public class PresenterIml implements BasePresenter {
private BaseView mvpView;
private BaseNet baseNet;
public PresenterIml(BaseView mvpView) {
this.mvpView = mvpView;
baseNet = new BaseNetIml();
}
...
...
@Override
public void onDestory() {
mvpView == null;
}
}
MVVM:
Model层: 负责数据存储、读取网络数据,操作数据库I/O等操作比如某个数据仓库啥的包含了获取数据逻辑;
ViewModel:只做和业务逻辑与数据操作相关,不做和UI有关的事;通过UI控件主动和它绑定,数据源Model发生改变,viewmodel将主动驱动View去做UI变化;
View:纯粹的视图层,包括Activity,Fragment和XML等,会设置viewmodel的监听器,同时Model层通过setValue方法更新值也发生在这里;
(引用是单向依赖的,但V和ViewModel是双向绑定的)
View 持有 ViewModel 的引用:
ViewModel 持有 Model 的引用:
Model 不持有其他层的引用:
MVP和MVVM的区别:
P层虽然和ViewModel层相似,也是承担着V和M层之间交互的职责;但是P是持有V层对象,调用一些V的接口方法实现;而ViewModel是通过DataBinding方式和V层实现双向绑定;
举例说明(model少了个仓库类,模拟数据获取):
ViewModel:
View层(一般构建一个viewmodel对象并持有它进行数据操作):
上面View缺少了获取完数据源后主动设值的过程
下面补上:
{
...
UserRepository.getUserRepository().getUsersFromServer(new Callback<List<User>>() {
@Override
public void onSuccess(List<User> users) {
loginVm.login(users.get(0).name,users.get(0).password);
}
});
...
}
Model层 (Bean类+仓库类):
纯粹读数据库,网络操作等获取数据的类:
public class UserRepository {
private static UserRepository mUserRepository;
public static UserRepository getUserRepository(){
if (mUserRepository == null) {
mUserRepository = new UserRepository();
}
return mUserRepository;
}
//(假装)从服务端获取
public void getUsersFromServer(Callback<List<User>> callback){
new AsyncTask<Void, Void, List<User>>() {
@Override
protected void onPostExecute(List<User> users) {
callback.onSuccess(users);
//存本地数据库
saveUsersToLocal(users);
}
@Override
protected List<User> doInBackground(Void... voids) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//假装从服务端获取的
List<User> users = new ArrayList<>();
for (int i = 0; i < 20; i++) {
User user = new User("user"+i, i);
users.add(user);
}
return users;
}
}.execute();
}
优点:
1.提高可维护性。Data Binding可以实现双向的交互,使得视图和控制层之间的耦合程度进一步降低,分离更为彻底,同时减轻了Activity的压力。
2.简化测试。因为View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大减少了对View同步更新的测试。
缺点:
出现bug难以定位,可能是View层问题也可能是Model数据源获取出问题;
MVI
MVVM的View和ViewModel是双向绑定的,ViewModel变化自动更新UI。
而MVI是严格数据单向的,
View → Intent → ViewModel → State → View
且状态管理是单一不可变的:
// 状态 不可变
sealed class UiState {
object Loading : UiState()
data class Success(val data: String) : UiState()
data class Error(val message: String) : UiState()
}
// 意图 不可变
sealed class UiIntent {
object LoadData : UiIntent()
object Retry : UiIntent()
}
// ViewModel
class MyViewModel : ViewModel() {
private val _state = MutableStateFlow<UiState>(UiState.Loading)
val state: StateFlow<UiState> = _state
fun processIntent(intent: UiIntent) {
when (intent) {
UiIntent.LoadData -> loadData()
UiIntent.Retry -> loadData()
}
}
private fun loadData() {
viewModelScope.launch {
_state.value = UiState.Loading
try {
val result = repository.fetchData()
_state.value = UiState.Success(result)
} catch (e: Exception) {
_state.value = UiState.Error(e.message ?: "Unknown error")
}
}
}
}
在Compose中状态提升:
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val state by viewModel.state.collectAsState()
// 状态提升到顶层
MyScreenContent(
state = state,
onIntent = viewModel::processIntent
)
}
@Composable
fun MyScreenContent(
state: UiState,
onIntent: (UiIntent) -> Unit
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
when (state) {
is UiState.Loading -> {
CircularProgressIndicator()
// 初始加载
LaunchedEffect(Unit) {
onIntent(UiIntent.LoadData)
}
}
is UiState.Success -> {
Text(text = state.data)
Button(onClick = { onIntent(UiIntent.Retry) }) {
Text("Refresh")
}
}
is UiState.Error -> {
Text(text = state.message, color = Color.Red)
Button(onClick = { onIntent(UiIntent.Retry) }) {
Text("Retry")
}
}
}
}
}
如果有多层级组件:
@Composable
fun ParentComponent() {
val viewModel: MyViewModel = viewModel()
val state by viewModel.state.collectAsState()
ChildComponentA(
state = state,
onIntent = viewModel::processIntent
)
}
@Composable
fun ChildComponentA(
state: UiState,
onIntent: (UiIntent) -> Unit
) {
Column {
when (state) {
is UiState.Success -> {
ChildComponentB(
data = state.data,
onRefresh = { onIntent(UiIntent.Retry) }
)
}
// 其他状态处理...
}
}
}
@Composable
fun ChildComponentB(
data: String,
onRefresh: () -> Unit
) {
Text(text = data)
Button(onClick = onRefresh) {
Text("Refresh Data")
}
}