题记
最近由搞framework又转到应用跟中间件来了,在写了几个小型工具型应用之后,逐渐从技术方面思考,对Android开发来说,什么样的应用才能算是高质量的应用,一个高质量的应用应该具有哪些特点? 目前我所想到的有以下几点:
- 拥有适合业务方向的开发框架
- 高度解耦的模块
- 漂亮的coding风格
- 符合开闭原则
- 运行稳定
上述特点除第6之外,其余均属于在软件设计阶段就需要完成的工作;而运行稳定则是我们在开发过程中需要考虑的事情,影响一个Android应用稳定的因素有很多,常见的如oom导致系统应用crash; 系统环境,网络状态变化影响界面UI异常影响体验等,而且最烦的一件事情就是处理Activity, Fragment等android组件的生命周期,稍有不慎,则会出现Fragment,Activity对象在onDestroy之后不释放的情况,着实令人头疼,而针对这种情况,android 官方推出Jetpack架构组件来解决这些问题。
Jetpack是什么
Jetpack是一组Android API库组成的套件,为了简化Android开发过程中的代码编写量以及Android多版本多设备的一致性问题而诞生的,使用Jetpack组件API后,我们可以将工作重心放在主要的业务上。
应用设计原则
对于Android移动开发应用程序来说,其设计原则应最大限度遵循以下几点:
1.避免使用应用组件,如Activity、Service和BroadcastReceiver指定为数据源
(组件具有生命周期的特性,使用其作为数据源会让数据变得不可靠)
2.在应用的各个模块之间设定明确定义的职责界限
遵循关注点分离的特点,每个模块之间高度解耦,这样做的优点显而易见的会使应用更加便于维护与扩展
3.尽量少公开每个模块中的代码
定义函数以及变量的可访问范围限制其他模块之间没必要的访问交互
4.考虑如何使每个模块可独立测试
减少上线之后的问题爆出率
5.专注于应用的独特核心
尽量将重心放在业务逻辑中
6.保留尽可能多的相关数据与最新数据
使用持久化数据模型来驱动界面,数据模型负责处理应用数据,独立于View与一个用组件,在组件因生命周期或者系统环境改变时能够继续工作,最大限度的减少因系统环境问题造成的用户体验差,如横竖屏切换,网络状态变化等。
7.数据单一可信来源
单一的数据来源能使数据界面一致性的更新更加可靠
数据来源1
数据来源2 --> 持久化数据库 --> UI
数据来源3
Android架构组件
针对应用设计原则,Android官方推出了架构组件
形态: 组库(ViewModel + LiveData + Lifecyle + Room)
目的: 设计稳健、可测试且易维护的应用
设计原则:
1.关注点分离 separation of concerns (SoC),可以理解为高度解耦,每个子模块只关心自己内部的逻辑, 修改该模块,将会最低限度的减少对其他模块的影响。
2.通过模型驱动界面,模型(持久性模型)负责处理应用数据的组件,它们一般独立于View与应用组件。持有性模型有如下优点系统销毁应用释放资源时,用户不会丢失数据。网络连接不稳定或者不可用,应用可以继续工作。
模型类的职责应该明确定义为数据管理,这样将使得可测性与系统一致性大大提升。
设计模式:基于MVVM模式,顾名思义为Model-View-ViewModel,View 为视图或者应用组件,ViewModel是视图模型是暴露公共属性和命令的视图的抽象。
组件成员:
LiveData: 响应式数据类型,当数据变化时,监听者能及时得到反馈。
ViewModel: 视图模型是暴露公共属性和命令的视图的抽象。MVVM没有MVC模式的控制器,也没有MVP模式的presenter, 有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器之间进行通信。
ViewModelStoreOwner:接口定义,由AppCompatActivity父类ComponentActivity实现,用来保存ViewModel引用,保证Acitivty在横竖屏切换,以及其他配置变更的情况下,只要Activity没有finish(), 那么还是用原来的ViewModel
ViewModelProvider:ViewModel提供器,是一个工具类,用来初始化并获取ViewModel对象。
Room: 是一个 SQLite 对象映射库。它可用来避免样板代码,并轻松地将 SQLite 表数据转换为 Java 对象。Room 提供 SQLite 语句的编译时检查,并且可以返回 RxJava、Flowable 和 LiveData 可观察对象。
Android架构集成
project中的build.gradle文件,添加google()
allprojects {
repositories {
google()
jcenter()
}
}
module中的build.gradle文件,添加androidx库的依赖
dependencies {
......
implementation "androidx.activity:activity:1.1.0"
implementation 'androidx.appcompat:appcompat:1.2.0'
}
依赖配置完成后,建立四个简单的类,分别是MainActivity,TestViewModel, TestRepository, TestEntity
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ViewModelProvider mVmProvider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("zj_debug","onCreate");
mVmProvider = new ViewModelProvider(this);
TestViewModel tvm = mVmProvider.get(TestViewModel.class);
tvm.getLiveData().observe(this, testEntity -> {
Log.e("zj_debug", "observer provider test entity = " + testEntity.mId + " name = " + testEntity.mName);
});
tvm.printValue(2);
finish();
}
@Override
protected void onDestroy() {
Log.e("zj_debug","onDestroy");
super.onDestroy();
System.gc();
}
}
TestViewModel.java
public class TestViewModel extends ViewModel {
private TestRepository mTestRepository = new TestRepository();
public LiveData<TestEntity> getLiveData(){
return mTestRepository.getLiveData();
}
public void printValue(int value){
mTestRepository.printValue(value);
}
@Override
protected void onCleared() {
super.onCleared();
Log.d("zj_debug", "oCleared");
}
}
TestRepository.java
public class TestRepository {
private MutableLiveData<TestEntity> mTestEntityLiveData = new MutableLiveData<>();
public LiveData<TestEntity> getLiveData(){
return mTestEntityLiveData;
}
public void printValue(final int value){
new Thread(){
@Override
public void run() {
super.run();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("zj_debug", "do printValue = " + value);
TestEntity testEntity = new TestEntity();
testEntity.mId = value;
testEntity.mName = "testName";
mTestEntityLiveData.postValue(testEntity);
}
}.start();
}
}
TestEntity.java
public class TestEntity {
public int mId;
public String mName;
}
运行后打印日志:
10-13 20:00:01.696 11125 11125 E zj_debug: onCreate
10-13 20:00:01.894 11125 11125 D zj_debug: oCleared
10-13 20:00:01.894 11125 11125 E zj_debug: onDestroy
10-13 20:00:03.706 11125 11163 E zj_debug: do printValue = 2
10-13 20:00:06.930 11125 11134 D zj_debug: finalize...
如果不执行finish,那最后Entity数据有变化的时候,UI处可以直接响应数据变化更新UI
10-13 20:01:57.484 12204 12240 E zj_debug: do printValue = 2
10-13 20:01:57.486 12204 12204 E zj_debug: observer provider test entity = 2 name = testName