1.简介
September 6, 2023 · View on GitHub
应用开发者面临的常见问题
在大多数情况下,桌面应用程序在启动器快捷方式中有一个单一的入口并且作为单独的独立进程运行,与桌面应用程序不同的是Android应用具有更复杂的结构。一个典型的Android应用是由多个应用程序组件构成的,包括activity,fragment,service,content provider和broadcast receiver。
这些应用程序组件中的大部分声明在manifest.xml文件中,用来决定如何将应用融入到用户设备的整体体验中。尽管如前所述,传统的桌面应用程序作为独立进程运行,但是正确的编写Android应用程序需要更加灵活,因为用户会同过设备上不同的应用程序组织成自己的方式不断切换流程和任务。
例如,考虑下在你喜欢的社交网络应用中分享照片时会发生什么。该应用会触发一个启动相机的intent,从该intent会启动一个相机应用来处理这个请求。在此刻,用户离开社交网络应用但是用户的体验是无缝的。相机应用转而可能会触发其它的intent例如启动文件选择器,这可能会启动另一个应用。最终用户回到社交网络应用并且分享照片。此外,在这个过程中的任何时刻用户都有可能会被一个电话打断,并且在结束通话后再回来继续分享照片。
在Android中,这种应用切换行为很常见,所以你的应用程序必须正确处理这些流程。记住,移动设备的资源是有限的,所以在任何时候,操作系统都可能会杀死一些应用为新的应用腾出空间。
其中的重点是应用程序组件可能会被单独和无序的启动,并且可能会被用户或系统在任何时候销毁。因为应用程序组件是短暂的,并且其声明周期(什么时候被创建和销毁)不受你控制,所以不应该在应用程序组件中存储任何应用数据或状态,同时应用程序组件不应该相互依赖。
通用的框架准则官方建议在架构App的时候遵循以下两个准则:
-
关注分离
其中早期开发
App最常见的做法是在Activity或者Fragment中写了大量的逻辑代码,导致Activity或Fragment中的代码很臃肿,十分不易维护。现在很多App开发者都注意到了这个问题,所以前两年MVP结构就非常有市场,目前普及率也很高。 -
模型驱动
UI模型持久化的好处就是:即使系统回收了
App的资源用户也不会丢失数据,而且在网络不稳定的情况下App依然可以正常地运行。从而保证了App的用户体验。
面对越来越复杂的App需求,Google官方发布了Android框架组件库(Android Architecture Components)使App的架构更加健壮。
Android Architecture Components
googlesamples/android-architecture-components
A collection of libraries that help you design robust, testable, and maintainable apps. Start with classes for managing your UI component lifecycle and handling data persistence.
Android Architecture Components,简称AAC意思就是一个处理UI的生命周期 与数据的持久化的架构
- 一个全新的库集合,可帮助您设计强大,可测试和可维护的应用程序。用于管理
UI组件生命周期和处理数据持久性。 - 便捷管理
App的声明周期:新的生命周期感知(lifecycle-aware)组件可帮助您管理Activity和Fragment的生命周期。存储配置改变,避免内存泄漏,并使用LiveData,ViewModel,LifecycleObserver和LifecycleOwner轻松将数据加载到UI中。 Room:一个SQLite对象映射库
平时我们比较熟悉的的架构有MVC,MVP及MVVM.这些结构有各自的优缺点, 以现在比较流行的MVP为例, 它将不是关于界面的操作分发到Presenter中操作,再将结果通知给View接口的实现(通常是Activity/Fragment).
MVP架构,当异步获取结果时,可能UI已经销毁,而Presenter还持有UI的引用,从而导致内存泄漏
fun getName() {
ExecutorServiceManager.getInstance().execute(Runnable {
try {
TimeUnit.SECONDS.sleep(5)
} catch (e: InterruptedException) {
e.printStackTrace()
}
if (iView != null) {
iView.setName("siyehua")
}
})
}
以上代码,iView的具体实现是Activity,当Activity已经执行了onDestroy()方法,此时Runnable还在获取数据,就会导致内存泄漏.
通常的做法是在给Presenter定义一个方法,当Activity销毁的时候同时移除对iView的引用,完整代码如下:
class PersonPresenter(private var iView: IView?) : IPresenter {
fun getName() {
ExecutorServiceManager.getInstance().execute(Runnable {
try {
TimeUnit.SECONDS.sleep(5)
} catch (e: InterruptedException) {
e.printStackTrace()
}
if (iView != null) {
iView!!.setName("siyehua")
}
})
}
fun removeView() {
iView = null
}
}
在Activity中,代码调用如下:
override fun onDestroy() {
//不移除 View 有可能导致内存泄漏
personPresenter.removeView()
super.onDestroy()
}
至此,即可解决MVP内存泄漏的问题,但是这么做不够优雅,需要手动管理Presenter,当然可以定义基类写入到BaseActivity中.
除了有可能引发内存泄漏的风险, 数据持久化也是一个经常困扰我们的问题.通常在屏幕旋转后,UI的对象都会被销毁重建,这将导致原来的对象数据不得不重新创建和获取,浪费资源的同时也会影响用户的体验.
通常的解决方法是,通过SavedInstanceState来存取数据,但SavedInstanceState存储的数据一般比较小,且数据对象还是必须重新构建.
为了将代码解耦以应对日益膨胀的代码量,工程师在应用程序中引入了“架构”的概念。使之在不影响应用程序各模块组件间通信的同时,还能够保持模块的相对独立。这样不仅有利于后期维护,也有利于代码测试。
上述两个问题可以通过使用AAC架构解决.
AAC主要提供了Lifecycle,ViewModel,LiveData,Room等功能,下面依次说明:
Lifecycle:生命周期管理,把原先Android生命周期的中的代码抽取出来,如将原先需要在onStart()等生命周期中执行的代码分离到Activity或者Fragment之外。LiveData:一个数据持有类,持有数据并且这个数据可以被观察被监听,和其他Observer不同的是,它是和Lifecycle是绑定的,在生命周期内使用有效,减少内存泄露和引用问题。ViewModel:用于实现架构中的ViewModel,同时是与Lifecycle绑定的,使用者无需担心生命周期。可以在多个Fragment之间共享数据,比如旋转屏幕后Activity会重新create,这时候使用ViewModel还是之前的数据,不需要再次请求网络数据。Room:谷歌推出的一个Sqlite ORM库,不过使用起来还不错,使用注解,极大简化数据库的操作,有点类似Retrofit的风格。
-
Activity/Fragment
UI层,通常是Activity/Fragment等,监听ViewModel,当ViewModel数据更新时刷新UI,监听用户事件反馈到ViewModel,主流的数据驱动界面。 -
ViewModel
持有或保存数据,向Repository中获取数据,响应UI层的事件,执行响应的操作,响应数据变化并通知到UI层。 -
Repository
App的完全的数据模型,ViewModel交互的对象,提供简单的数据修改和获取的接口,配合好网络层数据的更新与本地持久化数据的更新,同步等 -
Data Source
包含本地的数据库等,网络api等,这些基本上和现有的一些MVVM,以及Clean架构的组合比较相似
- 邮箱 :charon.chui@gmail.com
- Good Luck! `