|
| 1 | +简介(一) |
| 2 | +=== |
| 3 | + |
| 4 | +应用开发者面临的常见问题 |
| 5 | +--- |
| 6 | + |
| 7 | +在大多数情况下,桌面应用程序在启动器快捷方式中有一个单一的入口并且作为单独的独立进程运行,与桌面应用程序不同的是`Android`应用具有更复杂的结构。一个典型的`Android`应用是由多个应用程序组件构成的,包括`activity`,`fragment`,`service`,`content provider`和`broadcast receiver`。 |
| 8 | +这些应用程序组件中的大部分声明在`manifest.xml`文件中,用来决定如何将应用融入到用户设备的整体体验中。尽管如前所述,传统的桌面应用程序作为独立进程运行,但是正确的编写`Android`应用程序需要更加灵活,因为用户会同过设备上不同的应用程序组织成自己的方式不断切换流程和任务。 |
| 9 | +例如,考虑下在你喜欢的社交网络应用中分享照片时会发生什么。该应用会触发一个启动相机的`intent`,从该`intent`会启动一个相机应用来处理这个请求。在此刻,用户离开社交网络应用但是用户的体验是无缝的。相机应用转而可能会触发其它的`intent`例如启动文件选择器,这可能会启动另一个应用。最终用户回到社交网络应用并且分享照片。此外,在这个过程中的任何时刻用户都有可能会被一个电话打断,并且在结束通话后再回来继续分享照片。 |
| 10 | +在`Android`中,这种应用切换行为很常见,所以你的应用程序必须正确处理这些流程。记住,移动设备的资源是有限的,所以在任何时候,操作系统都可能会杀死一些应用为新的应用腾出空间。 |
| 11 | +其中的重点是应用程序组件可能会被单独和无序的启动,并且可能会被用户或系统在任何时候销毁。因为应用程序组件是短暂的,并且其声明周期(什么时候被创建和销毁)不受你控制,所以不应该在应用程序组件中存储任何应用数据或状态,同时应用程序组件不应该相互依赖。 |
| 12 | + |
| 13 | +通用的框架准则官方建议在架构`App`的时候遵循以下两个准则: |
| 14 | + |
| 15 | +- 关注分离 |
| 16 | + 其中早期开发`App`最常见的做法是在`Activity`或者`Fragment`中写了大量的逻辑代码,导致`Activity`或`Fragment`中的代码很臃肿,十分不易维护。现在很多`App` 开发者都注意到了这个问题,所以前两年`MVP`结构就非常有市场,目前普及率也很高。 |
| 17 | + |
| 18 | +- 模型驱动UI |
| 19 | + 模型持久化的好处就是:即使系统回收了`App`的资源用户也不会丢失数据,而且在网络不稳定的情况下`App`依然可以正常地运行。从而保证了`App`的用户体验。 |
| 20 | + |
| 21 | + |
| 22 | +面对越来越复杂的`App`需求,`Google`官方发布了`Android`框架组件库`(Android Architecture Components)`使`App`的架构更加健壮。 |
| 23 | + |
| 24 | + |
| 25 | +[Android Architecture Components](https://developer.android.com/topic/libraries/architecture/) |
| 26 | +[googlesamples/android-architecture-components](https://github.com/googlesamples/android-architecture-components) |
| 27 | + |
| 28 | +> 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. |
| 29 | +
|
| 30 | + |
| 31 | +`Android Architecture Components`,简称`AAC`意思就是一个处理`UI`的生命周期 与数据的持久化更加牛逼的架构 |
| 32 | + |
| 33 | +- 一个全新的库集合,可帮助您设计强大,可测试和可维护的应用程序。用于管理UI组件生命周期和处理数据持久性。 |
| 34 | +- 便捷管理App的声明周期:新的生命周期感知(`lifecycle-aware`)组件可帮助您管理`Activity`和`Fragment`的生命周期。存储配置改变,避免内存泄漏,并使用`LiveData`,`ViewModel`,`LifecycleObserver`和`LifecycleOwner`轻松将数据加载到UI中。 |
| 35 | +- `Room`:一个`SQLite`对象映射库 |
| 36 | + |
| 37 | + |
| 38 | +平时我们比较熟悉的的架构有`MVC`,`MVP`及`MVVM`.这些结构有各自的优缺点,以现在比较流行的`MVP`为例, 它将不是关于界面的操作分发到`Presenter`中操作,再将结果通知给`View`接口的实现(通常是 `Activity/Fragment`). |
| 39 | + |
| 40 | +这些结构有各自的优缺点, 以现在比较流行的`MVP`为例, 它将不是关于界面的操作分发到`Presenter`中操作,再将结果通知给`View`接口的实现(通常是`Activity/Fragment`). |
| 41 | +`MVP`架构,当异步获取结果时,可能`UI`已经销毁,而`Presenter`还持有`UI`的引用,从而导致内存泄漏 |
| 42 | + |
| 43 | + |
| 44 | +```kotlin |
| 45 | +fun getName() { |
| 46 | + ExecutorServiceManager.getInstance().execute(Runnable { |
| 47 | + try { |
| 48 | + TimeUnit.SECONDS.sleep(5) |
| 49 | + } catch (e: InterruptedException) { |
| 50 | + e.printStackTrace() |
| 51 | + } |
| 52 | + |
| 53 | + if (iView != null) { |
| 54 | + iView.setName("siyehua") |
| 55 | + } |
| 56 | + }) |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +以上代码,`iView`的具体实现是`Activity`,当`Activity`已经执行了`onDestroy()`方法,此时`Runnable`还在获取数据,就会导致内存泄漏. |
| 61 | +通常的做法是在给`Presenter`定义一个方法,当`Activity`销毁的时候同时移除对`iView`的引用,完整代码如下: |
| 62 | +```kotlin |
| 63 | +class PersonPresenter(private var iView: IView?) : IPresenter { |
| 64 | + fun getName() { |
| 65 | + ExecutorServiceManager.getInstance().execute(Runnable { |
| 66 | + try { |
| 67 | + TimeUnit.SECONDS.sleep(5) |
| 68 | + } catch (e: InterruptedException) { |
| 69 | + e.printStackTrace() |
| 70 | + } |
| 71 | + |
| 72 | + if (iView != null) { |
| 73 | + iView!!.setName("siyehua") |
| 74 | + } |
| 75 | + }) |
| 76 | + } |
| 77 | + |
| 78 | + fun removeView() { |
| 79 | + iView = null |
| 80 | + } |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +在`Activity`中,代码调用如下: |
| 85 | + |
| 86 | +```kotlin |
| 87 | +override fun onDestroy() { |
| 88 | + //不移除 View 有可能导致内存泄漏 |
| 89 | + personPresenter.removeView() |
| 90 | + super.onDestroy() |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +至此,即可解决`MVP`内存泄漏的问题,但是这么做不够优雅,需要手动管理`Presenter`,当然可以定义基类写入到`BaseActivity`中. |
| 95 | +除了有可能引发内存泄漏的风险, 数据持久化也是一个经常困扰我们的问题.通常在屏幕旋转后,`UI`的对象都会被销毁重建,这将导致原来的对象数据不得不重新创建和获取,浪费资源的同时也会影响用户的体验. |
| 96 | +通常的解决方法是,通过`SavedInstanceState`来存取数据,但`SavedInstanceState`存储的数据一般比较小,且数据对象还是必须重新构建. |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | +上述两个问题可以通过使用`AAC`架构解决. |
| 101 | + |
| 102 | + |
| 103 | +`AAC`主要提供了`Lifecycle`,`ViewModel`,`LiveData`,`Room`等功能,下面依次说明: |
| 104 | + |
| 105 | +- `Lifecycle`:生命周期管理,把原先`Android`生命周期的中的代码抽取出来,如将原先需要在`onStart()`等生命周期中执行的代码分离到`Activity`或者`Fragment`之外。 |
| 106 | +- `LiveData`:一个数据持有类,持有数据并且这个数据可以被观察被监听,和其他`Observer`不同的是,它是和`Lifecycle`是绑定的,在生命周期内使用有效,减少内存泄露和引用问题。 |
| 107 | +- `ViewModel`:用于实现架构中的`ViewModel`,同时是与`Lifecycle`绑定的,使用者无需担心生命周期。可以在多个`Fragment`之间共享数据,比如旋转屏幕后`Activity`会重新`create`,这时候使用`ViewModel`还是之前的数据,不需要再次请求网络数据。 |
| 108 | +- `Room`:谷歌推出的一个`Sqlite ORM`库,不过使用起来还不错,使用注解,极大简化数据库的操作,有点类似`Retrofit`的风格。 |
| 109 | + |
| 110 | +<img src="https://raw.githubusercontent.com/CharonChui/Pictures/master/final-architecture.png" width="100%" height="100%"> |
| 111 | + |
| 112 | + |
| 113 | +- `Activity/Fragment` |
| 114 | + `UI`层,通常是`Activity/Fragment`等,监听`ViewModel`,当`ViewModel`数据更新时刷新`UI`,监听用户事件反馈到`ViewModel`,主流的数据驱动界面。 |
| 115 | + |
| 116 | +- `ViewModel` |
| 117 | + 持有或保存数据,向`Repository`中获取数据,响应`UI`层的事件,执行响应的操作,响应数据变化并通知到`UI`层。 |
| 118 | + |
| 119 | +- `Repository` |
| 120 | + `App`的完全的数据模型,`ViewModel`交互的对象,提供简单的数据修改和获取的接口,配合好网络层数据的更新与本地持久化数据的更新,同步等 |
| 121 | + |
| 122 | +- `Data Source` |
| 123 | + 包含本地的数据库等,网络`api`等,这些基本上和现有的一些`MVVM`,以及`Clean`架构的组合比较相似 |
| 124 | + |
| 125 | + |
| 126 | +--- |
| 127 | + |
| 128 | +- 邮箱 :charon.chui@gmail.com |
| 129 | +- Good Luck! ` |
0 commit comments