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

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应用热启动重用模型对模块生命周期进行了统一管理,使模块开发能够专注在业务功能上,实现应用的快速开发迭代。同时,模块的重用使得应用在热启动时速度得到提升。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,498评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.h...
    eddy_wiki阅读 2,055评论 0 14
  • OC的理解与特性 OC作为一门面向对象的语言,自然具有面向对象的语言特性:封装、继承、多态。它既具有静态语言的特性...
    JW_T阅读 465评论 0 1
  • 遗忘是我们不可更改的宿命 一切好像对不齐的图纸 从前的一切已回不到过去 回去也不是当初的样子 你我相识的太迟 ...
    毛毛2018阅读 136评论 0 0