前言:
各位同学大家好 有段时间没有给各位更新文章了, 具体多久我也不清楚 ,最近整理了一下andorid 开发中几种常用的代码架构模式 (这里要跟java的设计模式区分开, 是代码整体架构不是 传统java 23种设计模式)今天就写了一个简单例子分享给大家,那么废话不多说 我们正式开始。
具体使用场景
效果图
我们这边在输入框 输入我们要查询的账号 然后点击中间button 完成查询 ,然后将结果显示在屏幕上面的textview里面 (这里只是模式的查询效果)然后分析其功能在不同架构上面的实现方式
-
无框架
具体代码实现(无框架)
package com.app.mvc_demo.normal;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.app.mvc_demo.R;
import com.app.mvc_demo.bean.Account;
import com.app.mvc_demo.callback.Mcallback;
import java.util.Random;
/***
*
*创建人:xuqing
* 类说明:无架构实现
*
*
*/public class NormalActivity extends AppCompatActivity implements View.OnClickListener{
private EditText ed_account;
private TextView text_account;
private Button getacount_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal);
initview();
}
private void initview() {
ed_account=findViewById(R.id.ed_account);
text_account=findViewById(R.id.account_text);
getacount_btn=findViewById(R.id.get_account_btn);
getacount_btn.setOnClickListener(this);
}
private String getUserInput(){
return ed_account.getText().toString().trim();
}
private void showSuccessPage(Account account){
text_account.setText("用户账号:"+account.getName()+"用户等级"+account.getLevel());
}
private void showFailedPage(){
text_account.setText("获取用户数据失败");
}
private void getAccountData(String accountName, Mcallback mcallback){
Random random=new Random();
boolean isSuccess=random.nextBoolean();
if(isSuccess){
Account account=new Account();
account.setLevel(100);
account.setName(accountName);
mcallback.onSuccess(account);
}else{
mcallback.onFailed();
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.get_account_btn:
String userInput=getUserInput();
getAccountData(userInput, new Mcallback() {
@Override
public void onSuccess(Account account) {
showSuccessPage(account);
}
@Override
public void onFailed() {
showFailedPage();
}
});
}
}
}
我们观察整个实现的代码 ,我们可以看到为了实现上面的查询账户的功能 在无架构的情况下 我们把所有的逻辑代码都写在一个activity 里面 虽然功能可以实现,但其负担过重,代码复查时繁琐,一旦需要修改,复杂项目极难维护 ,所以一般实战项目开发我们非常不推荐这种做法 除非你只是一个简单的学习demo逻辑很少 可以简单实现。
-
mvc架构
在mvc 架构里面我们把这个项目分成 model view controller 三层 model 是我们获取数据的具体方法(mvcmodel )view就是我们的xml布局文件 controller 就是我们的activity fragment 等
具体代码实现(mvc 架构)
activity 代码
package com.app.mvc_demo.mvc;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.app.mvc_demo.R;
import com.app.mvc_demo.bean.Account;
import com.app.mvc_demo.callback.Mcallback;
public class MvcActivity extends AppCompatActivity implements View.OnClickListener{
private EditText ed_account;
private TextView text_account;
private Button getacount_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal);
initview();
}
private void initview() {
ed_account=findViewById(R.id.ed_account);
text_account=findViewById(R.id.account_text);
getacount_btn=findViewById(R.id.get_account_btn);
getacount_btn.setOnClickListener(this);
}
private String getUserInput(){
return ed_account.getText().toString().trim();
}
private void showSuccessPage(Account account){
text_account.setText("用户账号:"+account.getName()+"用户等级"+account.getLevel());
}
private void showFailedPage(){
text_account.setText("获取用户数据失败");
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.get_account_btn:
String userInput=getUserInput();
MvcModel.getAccountData(userInput, new Mcallback() {
@Override
public void onSuccess(Account account) {
showSuccessPage(account);
}
@Override
public void onFailed() {
showFailedPage();
}
});
}
}
}
mvc model
package com.app.mvc_demo.mvc;
import com.ap.mvc_demo.bean.Account;
import com.app.mvc_demo.callback.Mcallback;
import java.util.Random;
public class MvcModel {
public static void getAccountData(String accountName, Mcallback mcallback){
Random random=new Random();
boolean isSuccess=random.nextBoolean();
if(isSuccess){
Account account=new Account();
account.setLevel(100);
account.setName(accountName);
mcallback.onSuccess(account);
}else{
mcallback.onFailed();
}
}
}
我们看到在mvc 架构模式下我们将获取数据的逻辑 抽离到mvcModel 中实现 我们就会简化掉 activity中的代码逻辑 ,但是缺点仍然存在,在MVC框架下,虽然将获取数据与界面展示分割开来,但对于Controller层,仍然拥有很多权利,随着功能的增多,其代码量也将会大大增长,不利于维护修改。
-
MVP架构模式
在mvp架构中 我们把整个项目代码的逻辑分为 model view Presenter model model 是我们获取数据的具体方法 (mvpmodel )view 是我们的xml文件 原来在mvc 中的controller 处理繁杂的逻辑我们都放到 Presenter 中进行
具体代码(MVP)
package com.app.mvc_demo.mvp;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.app.mvc_demo.R;
import com.app.mvc_demo.bean.Account;
public class MvpActivity extends AppCompatActivity implements View.OnClickListener,IMVPView {
private EditText ed_account;
private TextView text_account;
private Button getacount_btn;
private MVPPersenter mvpPersenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal);
initview();
mvpPersenter=new MVPPersenter(this);
}
private void initview() {
ed_account=findViewById(R.id.ed_account);
text_account=findViewById(R.id.account_text);
getacount_btn=findViewById(R.id.get_account_btn);
getacount_btn.setOnClickListener(this);
}
public String getUserInput(){
return ed_account.getText().toString().trim();
}
public void showSuccessPage(Account account){
text_account.setText("用户账号:"+account.getName()+"用户等级"+account.getLevel());
}
public void showFailedPage(){
text_account.setText("获取用户数据失败");
}
@Override
public void onClick(View v) {
String userInput=getUserInput();
mvpPersenter.getData(userInput);
}
}
mvpmodel
package com.app.mvc_demo.mvp;
import com.app.mvc_demo.bean.Account;
import com.app.mvc_demo.callback.Mcallback;
import java.util.Random;
public class MVPModel {
public void getAccountData(String accountName, Mcallback mcallback){
Random random=new Random();
boolean isSuccess=random.nextBoolean();
if(isSuccess){
Account account=new Account();
account.setLevel(100);
account.setName(accountName);
mcallback.onSuccess(account);
}else{
mcallback.onFailed();
}
}
}
IMVPView
IMvpview
package com.app.mvc_demo.mvp;
import com.app.mvc_demo.bean.Account;
public interface IMVPView {
String getUserInput();
void showSuccessPage(Account account);
void showFailedPage();
}
MVPPersenter
package com.app.mvc_demo.mvp;
import com.app.mvc_demo.bean.Account;
import com.app.mvc_demo.callback.Mcallback;
public class MVPPersenter {
private IMVPView imvpView;
private MVPModel mvpModel;
public MVPPersenter(IMVPView imvpView) {
this.imvpView = imvpView;
mvpModel=new MVPModel();
}
public void getData(String accountName){
mvpModel.getAccountData(accountName, new Mcallback() {
@Override
public void onSuccess(Account account) {
imvpView.showSuccessPage(account);
}
@Override
public void onFailed() {
imvpView.showFailedPage();
}
});
}
}
在mvp架构中 我们发现我们会在activtiy 直接操作那些逻辑 而是在MVPPersenter 中持有mvpmodel的引用然后在 MVPPersenter 的构造方法中传入 IMVPView的对象 我们在mvpModel 的回调方法里面在用imvpView 讲拿到的数据结果回调到activity 或者fragment 中,然后我们在activity 或者fragment中实现imvpvidew中方法即可获取数据结果
在使用MVP框架时,View层与Model层不通信,都通过 Presenter层传递,并且Presenter层与具体的View是没有直接关联的,而是通过定义好的接口进行交互,这就可能会导致有大量的接口生成,代码复杂繁琐,难维护。
- 1 因此,在使用MVP时最好按照一定规范去做:
- 2 接口规范化(封装父类接口以减少接口的使用量)
- 3使用第三方插件自动生成MVP代码
- 4 对于一些简单的界面,可以选择不使用框架
- 5根据项目复杂程度,部分模块可以选择不使用接口
MVVM架构
在mvvm 架构中 我们将整个项目分为 model view viewmodel model model 是我们获取数据的具体方法 (mvpmodel ) view 是我们的xml文件 viewmodel 处理业务逻辑数据更新 MVVM框架实现了数据与视图的绑定(DataBinding),当数据变化时,视图会自动更新;反之,当视图变化时,数据会自动更新。
我们要使用mvvm 首先我们要学会使用 DataBinding
DataBinding 使用步骤
- 1启用DataBinding
- 2修改布局文件为DataBinding布局
- 3数据绑定
启用DataBinding
在build.gradle里面添加依赖代码配置开启 databinding支持
dataBinding {
enabled = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
修改布局文件为DataBinding布局
我们在布局文件中鼠标放在最外层布局 然后按住alt+enter 键就可以修改为databinding布局
MVVM框架使用步骤:
- 1提供View、ViewModel以及Model三层
- 2 将布局修改为DataBinding布局
- 3View与ViewMedel之间通过DataBinding进行通信
- 4获取数据并展示在界面上
再更深层次学习,可以使用LiveData+ViewModel
具体代码实现(mvvm )
mvvmactivity
package com.app.mvc_demo.mvvm;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.app.mvc_demo.R;
import com.app.mvc_demo.databinding.ActivityMvvmBinding;
/***
*
* 创建人:xuqing
* 类说明:mvvmactivity
*
*
*/
public class MVVMActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMvvmBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
MVVMViewModel mvvmViewModel=new MVVMViewModel(getApplication(),binding);
binding.setViewModel(mvvmViewModel);
}
}
修改布局setContentView 为
ActivityMvvmBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
实例化mvvmmodel 并且set到databinding上面
MVVMViewModel mvvmViewModel=new MVVMViewModel(getApplication(),binding);
binding.setViewModel(mvvmViewModel);
MVVMModel
package com.app.mvc_demo.mvvm;
import com.app.mvc_demo.bean.Account;
import com.app.mvc_demo.callback.Mcallback;
import java.util.Random;
public class MVVMModel {
public void getAccountData(String accountName, Mcallback mcallback){
Random random=new Random();
boolean isSuccess=random.nextBoolean();
if(isSuccess){
Account account=new Account();
account.setLevel(100);
account.setName(accountName);
mcallback.onSuccess(account);
}else{
mcallback.onFailed();
}
}
}
xml 布局文件
MVVMViewModel
package com.app.mvc_demo.mvvm;
import android.app.Application;
import android.util.Log;
import android.view.View;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import com.app.mvc_demo.bean.Account;
import com.app.mvc_demo.callback.Mcallback;
import com.app.mvc_demo.databinding.ActivityMvvmBinding;
public class MVVMViewModel extends BaseObservable {
private static final String TAG = "MVVMViewModel";
private MVVMModel mvvmModel;
private ActivityMvvmBinding binding;
private String result;
public MVVMViewModel(Application application,ActivityMvvmBinding binding){
mvvmModel=new MVVMModel();
this.binding=binding;
}
public void getData(View view){
String userInput = binding.edAccount.getText().toString();
mvvmModel.getAccountData(userInput, new Mcallback() {
@Override
public void onSuccess(Account account) {
String info=account.getName()+"+|"+account.getLevel();
Log.e(TAG, "onSuccess: info "+info );
setResult(info);
}
@Override
public void onFailed() {
setResult("获取数据失败");
}
});
}
@Bindable
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
notifyPropertyChanged(com.app.mvc_demo.BR.result);
}
}
我们可以看到在使用了databinding布局之后 我们可以直接就在xml文件 view 调用viewmodel里面的方法 然后我们在viewmodel中持有mvvmmodel的引用 我们实现了mvvmmodel中方法 然后我们通过
notifyPropertyChanged 来刷新xml文件 view 中显示效果.
使用了mvvm框架+上databinding可以·直接view调用viewmodel中的方法 大大简化了我们activity的逻辑代码 但是有些必须在activity中实现的代码 例如申请权限之类我们可以通过 可以使用LiveData+ViewModel 去处理这个同学们可以自己去研究 我这边就不张开讲了
最后总结
在安卓开发中我们应该都有接触到这三方代码架构模式 个人觉得具体用那种代码架构看项目 个开发项目组 我是做游戏SDK开发 就一个 所以我个人在开发中 首选mvc 应为mvvm要引入很多三方库不友好 mvp要封装很多接口 项目本身逻辑少 所以我也不去用 要是开发app同学可以优先使用mvvm或者mvp 在代码整个解耦方面和后期维护 要比mvc要友好很多,最后希望我的文章能帮助到各位解决问题 ,以后我还会贡献更多有用的代码分享给大家。各位同学如果觉得文章还不错 ,麻烦给关注和star,小弟在这里谢过啦!
项目地址:
码云 :https://gitee.com/qiuyu123/mvvm_demo