MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。其中M层处理数据,业务逻辑等;V层处理界面的显示结果;C层起到桥梁的作用,来控制V层和M层通信以此来达到分离视图显示和业务逻辑层。
下面就列举登入的例子来了解MVC
module 层(主要是业务逻辑和数据的处理,下面是一个bean,也就是数据的处理 )
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User() {
}
}
View层 处理界面的显示结果(布局文件)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.arvin.mvcmode.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<TextView
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Username"/>
<EditText
android:id="@+id/username"
android:layout_width="200dp"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_marginTop="50dp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Password"/>
<EditText
android:id="@+id/password"
android:layout_width="200dp"
android:layout_height="wrap_content"/>
</LinearLayout>
<Button
android:layout_marginTop="50dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登入"
android:onClick="login"
android:layout_gravity="center_horizontal"
android:textSize="18sp"/>
</LinearLayout>
controller层(来控制V层和M层通信以此来达到分离视图显示和业务逻辑层,主要是activity 和 fragment
public class MainActivity extends AppCompatActivity {
private EditText mUsername;
private EditText mPassword;
private ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mUsername = (EditText) findViewById(R.id.username);
mPassword = (EditText) findViewById(R.id.password);
dialog = new ProgressDialog(this);
}
/**
* 按钮点击
*
* @param view
*/
public void login(View view) {
String username = mUsername.getText().toString().trim();
String password = mPassword.getText().toString().trim();
dialog.show();
//检查输入是否为空
boolean checkUserInfo = checkUserInfo(username, password);
if (checkUserInfo) {
final User user = new User();
user.setUsername(username);
user.setPassword(password);
new Thread() {
@Override
public void run() {
UserLoginNet userLoginNet = new UserLoginNet();
boolean login = userLoginNet.checkUserLoginInfo(user);
if (login){
//登入成功
success();
}else {
//登入失败
failed();
}
}
}.start();
}
}
/**
* 检验用户输入——界面相关逻辑处理
*
* @param username
* @param password
* @return
*/
private boolean checkUserInfo(String username, String password) {
if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
return false;
}
return true;
}
/**
* 登陆成功
*/
public void success(){
runOnUiThread(new Runnable() {
@Override
public void run() {
// 登陆成功
dialog.dismiss();
Toast.makeText(MainActivity.this, getString(R.string.welcome)+mUsername.getText().toString(), Toast.LENGTH_SHORT).show();
}
});
}
/**
* 登陆失败
*/
public void failed(){
runOnUiThread(new Runnable() {
@Override
public void run() {
// 登陆失败
dialog.dismiss();
Toast.makeText(MainActivity.this, R.string.login_fail, Toast.LENGTH_SHORT).show();
}
});
public class UserLoginNet {
/**
* 登入验证
*/
public boolean checkUserLoginInfo(User user){
SystemClock.sleep(2000);//模拟登入耗时
if ("username".equals(user.getUsername())&&"password".equals(user.getPassword())){
return true;//登入成功
}else {
return false;//登入失败
}
}
}
使用传统的MVC,其中的View,对应的是各种Layout布局文件,但是这些布局文件中并不像Web端那样强大,能做的事情非常有限;Controller对应的是Activity,而Activity中却又具有操作UI的功能,我们在实际的项目中也会有很多UI操作在这一层,也做了很多View中应该做的事情,当然Controller中也包含Controller应该做的事情,比如各种事件的派发回调,而且在一层中我们会根据事件再去调用Model层操作数据,所以这种MVC的方式在实际项目中,Activity所在的Controller是非常重的,各层次之间的耦合情况也比较严重,不方便单元测试。
使用MVC的进化版——MVP,MVP中把Layout布局和Activity作为View层,增加了Presenter,Presenter层与Model层进行业务的交互,完成后再与View层交互(也就是Activity)进行回调来刷新UI。这样一来,所有业务逻辑的工作都交给了Presenter中进行,使得View层与Model层的耦合度降低,Activity中的工作也进行了简化
下面正对上面的mvc进行改造,需要把activity中的登入业务抽取出来
Presenter层与Model层进行业务的交互,完成后再与View层交互(也就是Activity)进行回调来刷新UI
presenter 层
public class MainActivityPresenter {
MainActivity mMainActivity;
public MainActivityPresenter(MainActivity mainActivity) {
mMainActivity = mainActivity;
}
//登入业务的封装
public void login(String username,String password){
final User user = new User();
user.setUsername(username);
user.setPassword(password);
new Thread() {
@Override
public void run() {
UserLoginNet userLoginNet = new UserLoginNet();
boolean login = userLoginNet.checkUserLoginInfo(user);
if (login){
//登入成功
mMainActivity.success();
}else {
//登入失败
mMainActivity.failed();
}
}
}.start();
}
}
public void login(View view) {
String username = mUsername.getText().toString().trim();
String password = mPassword.getText().toString().trim();
dialog.show();
//检查输入是否为空
boolean checkUserInfo = checkUserInfo(username, password);
if (checkUserInfo) {
//开启业务,这里用实现m 和 c 的交互
mMainActivityPresenter.login(username,password);
}
}
实现了 activity 只作为view
留心观察,在MainActivity的onCreate方法中,我们有创建业务层对象,还是存在一定的耦合,下面采用dagger2进行解耦。
mMainActivityPresenter = new MainActivityPresenter(this);
Dagger2配置
1.首先project的gradle中添加 apt插件
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
2、apt插件的使用modle的gradle中添加
apply plugin: 'com.neenbedankt.android-apt'
3、添加dagger2的依赖 ,同步。
dependencies {
compile 'com.google.dagger:dagger:2.6'
apt 'com.google.dagger:dagger-compiler:2.6'
}
注解使用
@Module、@Provides、@Component、@Inject
@Module:标注对象创建的容器
@Provides:标识需要创建的对象
@Component:将创建好的对象与需要注入的对象组合到一起
完成dagger2的配置后,开始对代码解耦
- 首先注入实例MainActivityPresenter 对象
@Inject
MainActivityPresenter mMainActivityPresenter;
既然要注入MainActivityPresenter 实例,就需要有创建实例的组件,所以通过@Module封装@Provides创建的实例
@Module
public class MainActivityModule {
public MainActivityModule(MainActivity mainActivity) {
mMainActivity = mainActivity;
}
MainActivity mMainActivity;
@Provides
MainActivityPresenter providerMainActivityPresenter(){
return new MainActivityPresenter(mMainActivity);
}
}
封装好实例后,需要@Component和注入@Inject关联
@Component(modules = MainActivityModule.class)
public interface MainActivityComponent {
void in(MainActivity mainActivity);
}
完成上面工作后,bulid下工程,然后对代码解耦
//解耦前的代码
//mMainActivityPresenter = new MainActivityPresenter(this);
//解耦后
DaggerMainActivityComponent.builder().mainActivityModule(new MainActivityModule(this)).build().in(this);