🔥 什么是架构 🔥
在维基百科里是这样定义的: 软件架构是一个系统的轮廓 . 软件架构描述的对象是直接构成系统的抽象组件. 各个组件之间的连接则明确和相对细致地描述组件之间的通讯 . 在实现阶段, 这些抽象组件被细化为实际组件 , 比如具体某个类或者对象 .
面试的过程中会问到如何设计架构 ? 应用从哪几方面进行考虑 ? 架构的职责是什么 ? 你会发现这样的问题并不好回答 . 这里可以通过如下三点回答 :
● 为了解决特定的问题而提出 .
● 有特定的原则将系统整体进行角色划分 .
● 约定角色间的沟通机制 (有分层、有联系才能形成框架 ; 就像mvp、mvc、mvvm为解决特定问题而提出来的 ) .
所谓的架构 , 其实更多的是一种思想 , 一种机制 .
🔥 如何理解架构设计 🔥
在进行学习框架的时候都要从下面三个方面进行考虑 :
● 架构解决了什么问题
● 架构模式如何划分角色的
● 角色是如何建立联系的
当我们在查看框架框架源码的时候会去查看框架里面的当前类的作用 , 类与类之间会存在什么联系 . 平常设计框架的时候, 可以从 上面三个方面 进行考虑 .
🔥 什么是 MVC 🔥
其实我们日常开发中的Activity , Fragment和XML界面就相当于一个MVC的架构模式 , 但往往Activity中需要处理绑定UI , 用户交互 , 以及数据处理 .
这种开发方式的缺点就是业务量复杂的时候一个Activity过于臃肿 . 但是页面机构不复杂的情况下使用这种方式就会显得很简单.
MVC架构模式本来并不属于Android的开放模式 , 而且来自于Web前端 . 在Android开始发展的时候并没有什么架构 , 就直接照搬了Web前端这套模式 .
MVC架构模式在前端的作用是为了分离数据和视图 , 但是在Android上面进行使用就不是那么灵光了 .
🔥 MVC实现 🔥
创建案例的 Android Studio 版本
package com.kotlin.testmvc; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; class User { String tvHelloWorldValue; public User(String tvHelloWorldValue) { this.tvHelloWorldValue = tvHelloWorldValue; } } interface HiCallBack<User> { void onSuccess(User user); } interface IHomeModel { void getUserInfo(HiCallBack<User> callback); } class HomeModel implements IHomeModel { @Override public void getUserInfo(HiCallBack<User> callback) { // 获取到数据并回传给callback User user = new User("设置 tvHelloWorld 的值"); callback.onSuccess(user); } } /** * MVC Controller */ public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /** * MVC Model */ HomeModel model = new HomeModel(); model.getUserInfo(new HiCallBack<User>() { @Override public void onSuccess(User user) { /** * MVC View */ ((TextView) findViewById(R.id.tvHelloWorld)).setText(user.tvHelloWorldValue); } }); } }
🔥 什么是 MVP 架构 🔥
为了解决Activity任务过于繁重 , 数据层和视图层交织在一起的问题就此诞生了mvp架构模式 .
● 让宿主专注UI逻辑和用户交互的处理 . 把宿主中的业务逻辑分离出来 , 所有跟Android API无关的业务逻辑由Presenter 层来完成 . 但是缺点就是增加了代码量 .
● Activity 和 Fragment 视为View 层 , 负责处理UI和用户交互 .
● Presenter 为业务处理层 , 负责处理业务逻辑 , 发起请求数据 .
● Model 层中包含着具体的数据请求 , 数据源. 但是这一层在 retrofit , restful 的场景下可以被弱化 . 三层之间调用顺序为 view -> presenter -> model , 为了调用安全着想不可反向调用 ! 不可跨级调用 !
● BaseView : 一般特指Activity和/Fragment , 可以定义一些通用方法 .
interface BaseView{ /** * 判断宿主是否还存活,避免NPE * @return */ boolean isAlive(); }
● Presenter : 用于处理业务数据逻辑 , 并通过持有View接口吧数据回传到View层
class BasePresenter <IView extends BaseView>{ /** * 根据MVP的模型,Presenter需要持有View对象,才能将处理好的数据回调出去 */ public IView mView; public void attach(IView mView){ this.mView=mView; } public void detach(){ this.mView=null; } }
🔥 MVP 架构实现 🔥
● HomeContract 业务接口的定义
/** * 定义业务接口 */ interface HomeContract { interface View extends BaseView { void onGetuserInfo(Object data, String errorMsg); void onSaveUserInfo(Object data, String errorMsg); } /** * 每个接口 都会在View 存在与之对应的callback回调 */ abstract class Presenter extends BasePresenter<View> { public abstract void getUserInfo(); public abstract void saveUserInfo(); } }
● 业务接口Presenter的实现
class HomePresenter extends HomeContract.Presenter { @Override public void getUserInfo() { if (mView != null && mView.isAlive()) { mView.onGetuserInfo(null, null); } } @Override public void saveUserInfo() { } }
● 业务接口View 的实现
public class MainActivity extends AppCompatActivity implements HomeContract.View { private HomePresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); presenter=new HomePresenter(); presenter.attach(this); presenter.getUserInfo(); } @Override public boolean isAlive() { /** * 当前Activity(宿主)是否存活 */ return (!isDestroyed() && !isFinishing()); } @Override protected void onDestroy() { super.onDestroy(); /** * 释放掉IView */ presenter.detach(); } @Override public void onGetuserInfo(Object data, String errorMsg) { } @Override public void onSaveUserInfo(Object data, String errorMsg) { } }
🔥 创建 BaseActivity 🔥
避免每次 使用Activity都创建 Presenter 实例、判断当前Activity(宿主) 是否销毁、每次都手动销毁IView
class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView { protected P mPresenter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Type superClass=this.getClass().getGenericSuperclass(); //具备泛型参数的类型 if(superClass instanceof ParameterizedType){ Type[] arguments = ((ParameterizedType) superClass).getActualTypeArguments(); if(arguments!=null&&arguments[0] instanceof BasePresenter){ try{ mPresenter= (P) arguments[0].getClass().newInstance(); mPresenter.attach(this); }catch (IllegalAccessException e){ e.printStackTrace(); }catch (InstantiationException e){ e.printStackTrace(); } } } } @Override public boolean isAlive() { return (!isDestroyed() && !isFinishing()); } @Override protected void onDestroy() { super.onDestroy(); if(mPresenter!=null){ mPresenter.detach(); } } }
🔥 使用BaseActivity 🔥
class MainActivity1 extends BaseActivity<HomePresenter> implements HomeContract.View { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPresenter.getUserInfo(); } @Override public void onGetuserInfo(Object data, String errorMsg) { } @Override public void onSaveUserInfo(Object data, String errorMsg) { } }
🔥 什么是MVVM 🔥
● 双向绑定 : 数据变更UI会自动刷新 , UI变化了数据也会自动同步到最新的值 .
● 数据驱动UI : 比如 User中的字段 , 数据变化了, 可以做到自动刷新UI .
● UI同步数据 : 比如 EditText、checkBox、toggleButton 具有状态的View , 当状态变化后 , 数据模型中与之关联的字段值也会自动同步最新状态 .
// 开启 databinding
android{ ...... dataBinding{ enabled = true } }
🔥 传统的MVVM 🔥
● 定义Bean 类 User
package com.test.mvvm; public class User{ String nickName; String address; public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
● 定义ViewModel用于处理数据相关的业务逻辑。并通过ObservableFiled 观察者把结果回传出去
package com.test.mvvm; import androidx.databinding.ObservableField; /** * 定义ViewModel用于处理数据相关的业务逻辑。并通过ObservableFiled 观察者把结果回传出去 */ public class HomeViewModel{ public ObservableField<User> userFiled=new ObservableField(); public void queryUserInfo(){ User user=new User(); user.nickName="nickName"; user.address="address"; //自动通知与之关联的观察者 userFiled.set(user); } }
● 基于DataBinding 在xml中进行数据绑定, 可以实现数据&UI双向绑定 => 数据变更UI自动刷新 , UI变更自动同步数据
<?xml version="1.0" encoding="utf-8"?> <layout 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"> <data> <variable name="viewModel" type="com.test.mvvm.HomeViewModel" /> </data> <LinearLayout android:layout_width="match_parent" tools:context=".MainActivity" android:orientation="vertical" android:layout_height="wrap_content"> <!--单向绑定 @ --> <TextView android:id="@+id/nick_name" android:text="@{viewModel.userFiled.nickName}" android:layout_width="match_parent" android:gravity="center" android:background="@color/purple_200" android:textColor="@color/white" android:layout_height="100dp"/> <!--双向绑定 @= --> <EditText android:id="@+id/edit_address" android:text="@={viewModel.userFiled.address}" android:layout_width="match_parent" android:gravity="center" android:textColor="@color/white" android:background="@color/colorPrimary" android:layout_height="100dp"/> </LinearLayout> </layout>
● Activity 控制数据的获取
package com.test.mvvm; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import com.test.mvvm.databinding.ActivityMainBinding; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; public class MainActivity extends AppCompatActivity { ActivityMainBinding mainBinding; @Override protected void onDestroy() { super.onDestroy(); mainBinding.unbind(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); HomeViewModel homeViewModel = new HomeViewModel(); mainBinding.setViewModel(homeViewModel); homeViewModel.queryUserInfo(); mainBinding.editAddress.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void afterTextChanged(Editable editable) { Log.e("MainActivity","afterTextChanged:"+homeViewModel.userFiled.get().address); } }); } }
TextView 文本 ( nick_name ) 与 ViewModel实现了单项绑定 , 当改变 ViewModel 时 TextView变化
EditText 编辑框( edit_address ) 与 ViewModel 实现了双向绑定 , 当改变 ViewModel 时 EditText 变化 ,当操作编辑框时ViewModel 变化
E/MainActivity: afterTextChanged:addres E/MainActivity: afterTextChanged:addre E/MainActivity: afterTextChanged:addr E/MainActivity: afterTextChanged:add E/MainActivity: afterTextChanged:ad E/MainActivity: afterTextChanged:a E/MainActivity: afterTextChanged: E/MainActivity: afterTextChanged:第一次 E/MainActivity: afterTextChanged:第一次修改 E/MainActivity: afterTextChanged:第一次修改编辑框
🔥 JetPack下的MVVM 🔥
JetPack下的MVVM . ViewModel + LiveData 组件结合 .
这样做的目的既能保证数据不会无缘无故丢失 , 还能自动关联宿主的生命周期 , 避免空指针的问题 . Activity , Fragment UI 逻辑和用户交互控制就可以了 . 数据的绑定可以交给DataBinding .
定义ViewModel2 继承自ViewModel , 通过 MutableLiveData 传递数据
package com.test.jetpack.mvvm; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; /** * * @author */ public class HomeViewModel2 extends ViewModel { public MutableLiveData<User> getUserInfo(){ MutableLiveData<User> liveData=new MutableLiveData<>(); User user=new User(); user.nickName="nickName"; user.address="address"; liveData.postValue(user); return liveData; } }
MainActivity2 绑定UI布局和对 ViewModel 数据的观察并进行刷新UI界面
package com.test.jetpack.mvvm; import android.os.Bundle; import com.test.mvvm.R; import com.test.mvvm.databinding.ActivityMain2Binding; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; public class MainActivity2 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final ActivityMain2Binding binding =DataBindingUtil.setContentView(this,R.layout.activity_main2); ViewModelProvider provider=new ViewModelProvider(this,new ViewModelProvider.NewInstanceFactory()); HomeViewModel2 model2=provider.get(HomeViewModel2.class); model2.getUserInfo().observe(this, new Observer<User>() { @Override public void onChanged(User user) { binding.setUser(user); } }); } }