美文网首页Android架构
Android应用热启动重用模型设计与实现

Android应用热启动重用模型设计与实现

作者: kkmoving | 来源:发表于2015-12-30 15:37 被阅读761次

    Android应用开发中,通常采用热启动的方式,来实现应用的快速启动。所谓热启动,就是在应用退出时,只finish主Activity,而不杀掉应用进程。这样在应用退出后热启时可以重用应用资源,比如Application,静态实例,线程等。这里牵涉到一个生命周期的问题。

    1 生命周期

    这里讨论的生命周期包括四个方面:
    -应用的生命周期
    -Activity的生命周期
    -线程的生命周期
    -静态实例的生命周期

    应用的生命周期

    应用的生命周期,简单来说就是Android应用进程的生命周期,应用进程由系统管理。启动是由其他应用触发,如Launcher。进程结束,可主动调用(Process.killPocessSystem.exit),也可能由系统杀死。

    应用进程结束后,应用占用资源将被回收,再次启动应用相当于全新启动,所有资源重新加载,与热启动对应,这种方式是冷启动。

    Activity的生命周期

    Activity的启动由系统AMS(ActivityManagerService)进程管理,Activity生命周期的结束由应用调用finish触发。

    线程的生命周期

    Android应用中线程的生命周期由应用进行控制,如果应用没有结束线程,那么它将与应用的生命周期一致。通常在应用中可能存在这样的全局线程。

    静态实例的生命周期

    静态实例的生命周期与应用的生命周期一致。

    可以看到,在热启动模型中,全局线程,静态实例的生命周期是与应用的生命周期一致的,独立于Activity的生命周期。

    基于这一点,可以在热启动时,重用应用的资源,缩短启动过程,提升启动速度。这里的资源是指应用程序中所用到的对象实例,尤其是与启动相关的实例。这里的实例就是上面讲到的静态实例,全局线程本质上也需要静态实例来实现重用。

    2 静态实例的重用

    静态实例通常会持有很多其他实例(这里统称为资源)的引用,在重用需要注意的一点,是与Context相关资源的重用。热启动模型中,主Activity在退出时会销毁,重新启动应用时Activity重新创建,因此,静态实例中与Context相当的资源是不能重用的。

    可以将静态实例的资源分为Context相关和Context无关,Context相关资源在实例重用时需要重新创建,Context无关资源直接重用。

    另外,Context相关资源必须在应用退出时进行释放,否则静态实例始终持有Context引用,导致Activity无法释放。

    这样重用设计的雏形就已经有了,但是重用模型牵涉到静态实例的创建、释放和重用,对于大型应用来讲,各个模块都来进行这些处理,显得比较繁琐。因此,可以基于模块化前提,抽象实现重用模型,这样对于应用模块开发就轻松了许多。

    3 应用模块化

    在大型Android应用当中,一种敏捷的设计模型是模块化模型,应用由若干模块组成,模块中采用MVC模型。模块MVC模型中,Controller由Manager角色和Bridger角色组成,Manager角色负责模块内部管理和提供对外接口,Bridger角色负责对外调用。对于小的模块,Manager角色和Bridger角色可以统一在Manager中,作为模块的管理者,负责整个模块内部管理,以及对外交互。

    按照模块在应用生命周期中使用的频率,可将模块分为常驻模块和即用模块。常驻模块是指经常会被其他模块调用的模块;即用模块是指使用频率低,在调用时再进行即时创建的模块。

    由于常驻模块会频繁被调用,因此常驻模块的管理者(Manager)通常设计为单例,即常驻模块存在一个常驻的静态实例。这里就涉及到前面所说的静态实例的重用, 这里重用其实就是模块的重用。

    4 模块化模型重用设计

    大型应用中模块本身的功能实现已经比较复杂,我们更倾向于在模块中只完成业务功能的实现,而将重用的实现抽象为基础框架。建立在重用框架之上的模块,可以把更专注于业务功能的实现。

    重用框架的核心是定义一个BasicManager,用于实现生命周期管理,包括重用逻辑。模块Manager仅需继承BasicManager,即可实现模块的重用。上文中提到的静态实例的重用,就是BasicManager实例的重用。

    BasicManager状态机

    在应用生命周期中,BasicManager存在若干状态,生命周期的管理就是对这些状态的管理,也就是一个状态机。

    BasicManager包含以下状态:

    1. Dead。应用从未启动或者启动后强制杀掉进程后的状态。
    2. Just Created。BasicManager实例刚创建的状态。
    3. Already Created。BasicManager实例已经创建的状态。
    4. Released。BasicManager实例释放后的状态。
    5. Just Reused。BasicManager刚重用的状态。
    6. Already Reused。BasicManager已经重用的状态。

    转换转换图如下

    BasicManger状态机.jpg

    状态由4个维度组成:

    1. 实例是否存在(instance
    2. 是否创建(isCreated
    3. 是否重用(isReused
    4. 是否释放(isReleased

    状态转换中,关键点有以下几个:

    1. Do exit节点。如果应用不采用热启动模型,那么执行Cold exit,直接退出应用程序,进入Dead状态。如果使用热启动模型,执行Hot exitHot exit执行Do releaseDo release中需要重用的模块保持instance,不需要重用的模块直接销毁instance。
    2. Check release节点。Check release节点有5个状态入口,用于检查是否已经释放(isReleased),如果未释放需要先释放。释放以后或者已经释放,再检查是否已经重用(isReused),如果未重用,则执行重用逻辑Do reuse,进入Just Reused状态,否则进入Already Reused状态。
      之所以要在重用之前检查是否释放,是因为某些模块的释放在主Activity的onDestroy中调用,而onDestroy的回调时机是不确认的,它可能在下一次启动应用之后再调用,因此会出现前一次没有未来得及释放,就重新进入了重用逻辑。因此,需要在重用之前强制进行释放。

    BasicManager提供三个接口进行状态转换触发:

    1. Constructor。触发实例创建。调用时机:在实例化时调用。
    2. reuse。触发重用。调用时机:在实例存在时调用。
      reuse的实现逻辑相对复杂,在实例存在(instance != null)时,可能进入Already Created状态,也可能进入Just Reused状态,也可能进入Already Reused状态。在创建态(isCreated为true)时进入Already Created状态;在非创建态且非重用态(isReused为false)时,执行重用逻辑,进入Just Reused状态;在非创建态且重用态时,进入Already Reused状态reuse提供onReuse接口供子类实现重用逻辑。
    3. release。触发释放。调用时机:在应用退出即主Activity结束时调用。reuse提供onRelease接口供子类实现释放逻辑。

    Constructorreuse在子类的getInstance方法中调用,实现如下:

    public static ModuleManager getInstance() {
        if (sInstance == null) {
            synchronized (ModuleManager.class) {
                if (sInstance == null) {
                    sInstance = new ModuleManager();
                }
            }
        } else {
            sInstance.reuse();
        }
    }
    

    release在主Activity退出时调用。但是如果每个模块都需要主动去调用,显得过于繁琐。因此BasicManager还实现了所有子类实例的统一释放管理。

    BasicManager统一管理

    要统一管理,首先需要将子类实例保存在列表中,在创建和重用时加入到列表,在统一释放时,遍历列表进行释放,并清空列表。这里的列表作为静态列表保存在BasicManager中。

    对列表的访问牵涉到一个同步的问题,当然,如果对列表的所有访问都加上同步锁就不会有问题,但是这样做就影响了性能,尤其是对启动速度的影响。

    基于性能考虑,BasicManager持有两类列表:主线程列表和非主线程列表。在访问列表时,如果是在主线程,那么直接加入主线程列表,都在主线程,不存在线程冲突的问题,不加同步锁也不会对程序造成阻塞;如果是在非主线程,则使用同步锁进行添加,由于是在非主线程,也不会造成阻塞。

    到这里重用框架就基本实现了。Android应用热启动重用模型对模块生命周期进行了统一管理,使模块开发能够专注在业务功能上,实现应用的快速开发迭代。同时,模块的重用使得应用在热启动时速度得到提升。

    当然时间和空间是不能兼得的,速度的提升是以牺牲空间为代价的,热启动模型中应用在退出后,仍然占用的了部分内存,因此在应用开发时需要注意,并不是所有的模块都需要作为常驻模块,进行模块的重用。建议与启动相关的模块为常驻模块,其他模块不进行重用,在释放时对静态实例进行销毁。

    相关文章

      网友评论

      本文标题:Android应用热启动重用模型设计与实现

      本文链接:https://www.haomeiwen.com/subject/zhxpcttx.html