推荐资源站:https://zhimalier.com/

本文章原作者已授权转载   原文地址http://blog.spinytech.com/2016/11/17/android_multiple_process_usage_scenario/

在上一篇《Android 多线程介绍》中,我们大概了解了一下Android中的进程的概念以及其生命周期,另外还有Low Memory Killer相关知识。了解完原理,就该进行实践了。今天这篇文章,我们就来聊一聊,到底如何利用多进程开发,使我们的应用更稳定、用户体验更好?换言之就是Android开发中多进程开发的使用场景分析。

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

什么情况需要使用多进程

常驻后台任务应用

类似音乐类、跑步健身类、手机管家类等长时间需要在后台运行的应用。这些应用的特点就是,当用户切到别的应用,或者关掉手机屏幕的时候,应用本身的核心模块还在正常运行,提供服务。如果因为手机内存过低,或者是进程重要性降低,导致应用被杀掉,后台服务停止,对于这些应用来说,就是灭顶之灾。合理利用多进程,将核心后台服务模块和其他UI模块进行分离,保证应用能更稳定的提供服务,从而提升用户体验。

举个例子:

现在要做一款音乐播放器,现在有以下几种方案:
A. 在Activity中直接播放音乐。
B. 启动后台Service,播放音乐。
C. 启动前台Service,播放音乐。
D. 在新的进程中,启动后台Service,播放音乐。
E. 在新的进程中,启动前台Service,播放音乐。

首先我们分析A

在A中,我们的播放器是直接在activity中启动的。首先这么做肯定是不对的,我们需要在后台播放音乐,所以当activity退出后就播不了了,之所以给出这个例子是为了控制变量作对比。
然后我们来看下A的使用场景。
音乐播放器无非是打开app,选歌,播放,退到桌面,切其他应用。我们选取了三个场景,打开、按home切换其他应用、按back退回桌面。让我们看一下A的相对应的oom_adj、oom_score、oom_score_adj的值。(下面三张图依次对应为【打开状态】、【按了Home键被切换状态】、【按了Back键被退出状态】)

上图为打开状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Home键状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Back键状态下oom_adj、oom_score、oom_score_adj的值

当我们应用在前台的时候,无论adj还是score还是score_adj,他们的值都非常的小,基本不会被LMK所杀掉,但是当我们按了Home之后,进程的adj就会急剧增大,变为7,相应的score和score_adj也会增大。在上篇文章中我们得知,adj=7即为被切换的进程,两个进程来回切换,上一个进程就会被设为7。当我们按Back键的时候,adj就会被设为9,也就是缓存进程,优先级比较低,有很大的几率被杀掉。

接着我们分析B

B是直接启动一个后台service并且播放音乐,这个处理看起来比A好了很多,那么实际上,B的各个场景的优先级和A又有什么不同呢?让我们来看下B的对应的打开、切换、退出相应的adj、score、score_adj的值。(下面三张图依次对应为【打开状态】、【按了Home键被切换状态】、【按了Back键被退出状态】)

上图为打开状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Home键状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Back键状态下oom_adj、oom_score、oom_score_adj的值

B的情况其实是与A类似的,三种状态的adj、score_adj的值都是一样的,只有score有一点出入,其实分析源码得知,LMK杀进程的时候,score的左右其实并不大,所以我们暂时忽略它。所以,与A相比,他们的adj和score_adj的值都相同,如果遇到内存不足的情况下,这两个应用谁占得内存更大,谁就会被杀掉。不过鉴于A实在activity中播放音乐,所以B还是比A略好的方案。

这里有朋友肯定要问了,为什么切到后台后,adj的值是7而不是5,后台不是还有service在跑吗?
我们通过查看源码可以找出来,当切换Home的时候,会调用ActivityStack.javafinishCurrentActivityLocked函数,然后调用到了ActivityManagerService.javacomputeOomAdjLocked函数,在这里,对进程的ADJ值进行重新计算。

if (app == mPreviousProcess && app.activities.size() > 0) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {adj = ProcessList.PREVIOUS_APP_ADJ;schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;app.cached = false;app.adjType = "previous";
}if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;}
}

当进程为PreviousProcess情况,则ADJ=7。
具体的计算流程请看这里computeOomAdjLocked计算流程

接下来分析C

C的话是启动一个前台Service来播放音乐。让我们来看一下对应的值。(下面三张图依次对应为【打开状态】、【按了Home键被切换状态】、【按了Back键被退出状态】)

上图为打开状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Home键状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Back键状态下oom_adj、oom_score、oom_score_adj的值

在前台的时候,和AB是一样的,adj都是0,当切到后台,或者back结束时,C对应的adj就是2,也就是可感知进程。adj=2可以说是很高优先级了,非root手机,非系统应用已经没有办法将其杀掉了。具体可参见ActivityManagerService的killBackgroundProcesses方法以及killPackageProcessesLocked方法。adj<5的应用不会被杀掉。
总的来说,C方案比B优秀,拥有前台Service的C更不容易被系统或者其他应用所杀掉了,进程的优先级一下子提高到了2,相对于B来说更稳定,用户体验更好。不过有一点不足是必须启动一个前台service。不过现在大部分的音乐类软件都会提供一个前台service,也就不是什么缺点了。其实也是有灰色方法可以启动一个不显示通知的前台service,这里就不过多介绍了。

那么还有可改进的余地吗?
答案当然是肯定的。

让我们来看看D ,终于我们的主角,多进程登场了。
D把应用进行了拆分,把用于播放音乐的service放到了新的进程内,让我们看一下对应的值。(下面三张图依次对应为【打开状态】、【按了Home键被切换状态】、【按了Back键被退出状态】)

上图为打开状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Home键状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Back键状态下oom_adj、oom_score、oom_score_adj的值

上面三张图对应的是D应用主进程的ADJ相关值,我们可以看出来,跟A类似,adj都是0,7,9。由于少了service部分,内存使用变少,最后计算出的oom_score_adj也更低了,意味着主进程部分也更不容易被杀死。

下面我们看下拆分出的service的相关值

上图为后台Service的oom_adj、oom_score、oom_score_adj的值

因为是service进程,所以不受打开,关闭,切换所影响,这里就放了一张图。
我们可以看到,service的adj值一直是5,也就是活跃的服务进程,相比于B来说,优先级高了不少。不过对于C来说,其实这个方案反倒不如C的adj=2的前台进程更稳定。但是D可以自主释放主进程,使D实际所占用的内存很小,从而不容易被杀掉。那么到底C和D谁是更优秀的设计?我个人认为,在ABCDE这5个设计中,D是最具智慧的设计,具体是为什么?先卖个关子,等我们说完了E,再作总结。

那就赶紧分析E吧

E也是使用了多进程,并且在新进程中,使用了前台service,先来看下对应的值。(下面三张图依次对应为【打开状态】、【按了Home键被切换状态】、【按了Back键被退出状态】)

上图为打开状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Home键状态下oom_adj、oom_score、oom_score_adj的值

上图为按了Back键状态下oom_adj、oom_score、oom_score_adj的值

这个不多解释,和ABD基本差不多,都是0,7,9。我们看下拆分出来的进程的值。

上图为后台Service的oom_adj、oom_score、oom_score_adj的值

我们可以看到,这个进程的值是2,像C方案,非常小,非常稳定,而且,我们还可以在系统进入后台后,手动杀掉主进程,使整个应用的内存消耗降到最低,内存低,优先级又高,E获得了今天的最稳定的方案奖。

小结

ABCDE,5种方案都已经分析完了。显然,E是最稳定的方案,不过,我刚才说过,我个人最倾向于D方案,并且认为D是最智慧的方案,这是为什么呢?
其实我们可以做个比喻,把整个Android系统比喻成一个旅游景点,Low Memory Killer就是景点的门卫兼保安,然后我们每个进程的ADJ相当于手里的门票,有的人是VIP门票,有的人是普通门票。景点平常没人的时候还好,谁拿票都能进,当人逐渐拥挤的时候,保安就开始根据票的等级,往外轰人。E方案就是一个拿着普通票的妈妈,带着一个VIP的孩子去参观,D方案就是一个拿着普通票的妈妈,带着一个拿着中等票的孩子参观。当内存不够的时候,保安会先把两个妈妈轰出去,孩子们在里面看,再不够了,就会把D孩子给轰出去。这么看来,显然E的效果更好一些,不过由于Android系统对于VIP票的发放没有节制,大家都可以领VIP票,那也就是相当于没有VIP票了。所以如果E方案是一种精明,那么D才是真正的智慧。将调度权还给系统,做好自己,维护好整个Android生态。

其实现阶段,如果公司要做一个后台类型的应用,我个人也是会选择追逐眼前里利益,采用E方案的,这也是不得已而为之,大家都利用漏洞拿VIP票,你不拿,将来做出来的APP出了偏差,你是要负责任的,所以还是希望Android能把漏洞堵住,把内存分配给真正需要的人,而我们自己也应该遵守规矩,维护整个生态平衡。
还有一点,是因为现在部分Root的手机都有优化大师,其实这个优化大师,就好比是个临时工门卫,告诉你他能解决景区爆满问题,实际上他的做法是,把一些票的等级降低,比如把中等票变成赠票,然后给你名正言顺的轰出去,听着是不是很耳熟?“让一部分人先富裕起来,然后把不富裕的杀掉,达成共同富裕。”

我的测试机之前装了某款优化软件,然后,在正常手机上的adj的值,都有一定程度的降低,来我们上证据。

上图为D方案下,Service进程的oom_adj、oom_score、oom_score_adj的值

看到没,安装了优化应用之后,本应该adj=5的活跃服务进程,被调整为8,意思是不活跃的服务进程,这种做法本身就违反了最初Android设计的思想。

还有

上图为E方案下,主进程在按了back键退出之后进程的oom_adj、oom_score、oom_score_adj的值

本来应该adj=9的缓存进程,调整为adj=11。adj=11在Android中都没给出定义。

所以,选择E也是无奈之举。还是呼吁大家要克制吧,维护Android的生态系统是每个工程师的责任。

“喜欢是放肆,但爱是克制” — 阿尔伯特·爱因斯坦 [手动滑稽]

多模块应用

多进程还有一种非常有用的场景,就是多模块应用。比如我做的应用大而全,里面肯定会有很多模块,假如有地图模块、大图浏览、自定义WebView等等(这些都是吃内存大户),还会有一些诸如下载服务,监控服务等等,一个成熟的应用一定是多模块化的。

首先多进程开发能为应用解决了OOM问题,Android对内存的限制是针对于进程的,这个阈值可以是48M、24M、16M等,视机型而定,所以,当我们需要加载大图之类的操作,可以在新的进程中去执行,避免主进程OOM。

多进程不光解决OOM问题,还能更有效、合理的利用内存。我们可以在适当的时候生成新的进程,在不需要的时候及时杀掉,合理分配,提升用户体验。减少系统被杀掉的风险。

多进程还能带来一个好处就是,单一进程崩溃并不影响整体应用的使用。例如我在图片浏览进程打开了一个过大的图片,java heap 申请内存失败,但是不影响我主进程的使用,而且,还能通过监控进程,将这个错误上报给系统,告知他在什么机型、环境下、产生了什么样的Bug,提升用户体验。

再一个好处就是,当我们的应用开发越来越大,模块越来越多,团队规模也越来越大,协作开发也是个很麻烦的事情。项目解耦,模块化,是这阶段的目标。通过模块解耦,开辟新的进程,独立的JVM,来达到数据解耦目的。模块之间互不干预,团队并行开发,责任分工也明确。至于模块化开发与多进程的结合,后续会写一篇专门的文章来研究这个问题。

总结

关于多进程的使用场景今天大概先说这么多。使用多进程开发还是有很多好处的,不过也有不少坑,下一篇文章我就想总结一下《多进程开发注意事项》。

最后,多进程不一定适合所有的应用,合理利用分配进程,使程序更加稳定,才是我们追求的目标。

Android 多进程使用场景相关推荐

  1. [Android]多进程知识点详解

    作者:Android开发_Hua 链接:https://www.jianshu.com/p/e0f833151f66 多进程知识点汇总: 一:了解多进程 二:项目中多进程的实现 三:多进程的优缺点与使 ...

  2. Android 多进程开发

    前言 正常情况下,一个apk启动后只会运行在一个进程中,其进程名为AndroidManifest.xml文件中指定的应用包名,所有的基本组件都会在这个进程中运行.但是如果需要将某些组件(如Servic ...

  3. Android IPC机制之IPC概念、Android 多进程和相关基础知识

    1.IPC 的基本概念 1.1 IPC的使用场景 IPC 即 Inter-Process Communication 进程间通信,IPC用于多进程,而Android的多进程情况一般有: 1.一个应用需 ...

  4. Android多进程实现,一个APP多个进程

    相关文章: Android IPC机制(一)开启多进程(文章1:刘望舒大神的文章,1.3w阅读量,20赞) Android IPC机制(二)用Messenger进行进程间通信(文章2:刘望舒大神的文章 ...

  5. Android——多进程

    之前我们了解了 : -----------------------------------Java--多线程浅析. -----------------------------------Android ...

  6. Android 多进程编程 15问15答!

    ps:阅读本文 需要对android 多进程编程有一定了解. 1.Android中总共有几种方式进行IPC? 答:一共有两种,一种是binder 还有一种是socket.Binder 大家用的比较多. ...

  7. Android多进程(一)—— 开启多进程

    Android多进程:一般情况下,一个应用程序就是一个进程,进程名就是应用程序的包名.进程是系统分配资源的基本单位,每个进程都有自己独立的资源和内存空间. 1 Android开启多进程的原因 单进程分 ...

  8. Android 多进程的利弊分析

    多进程的使用方法: Android多进程概念:一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名.我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的资源和内存空间, ...

  9. SharePreference源码学习和多进程的场景

    复习了下SharePreference的使用,以及了解下SharePreference的源码实现,解决多进程情况下的SharePreference问题,做下笔记. 参考文章: 源码分析: www.ji ...

最新文章

  1. 【Android开发经验】移动设备的“声波通信/验证”的实现——SinVoice开源项目介绍(一)
  2. VS2015启动调试程序变慢
  3. kindeditor编辑器 编辑器textarea不能获取到内容的解决办法
  4. java 异常处理的关键字_java异常,异常处理,异常类 关键字:throws 和 throw 自定义的异常类...
  5. vue项目中常用解决跨域的方法
  6. 1.0版走迷宫小游戏(C++)
  7. kali的vmtool工具安装
  8. 程序员无处安放的青春
  9. 牛客--2019网易--扭蛋机
  10. 查看数据库信息(一)
  11. Excel合并两列并自动换行
  12. 招商与代理--营销至上
  13. 随机搜索(Random Searching)算法
  14. 在Linux中配置软 RAID,使用mdadm命令创建RAID5, RAID设备的数据恢复
  15. java上看小说软件_i悦读小说阅读软件 For java
  16. 【java基础】java继承从“我爸是李刚”讲起
  17. 关于这些年做的 图片上传下载做的处理总结(including根据图片url上传文件)
  18. 虚拟机集群启动,Hadoop常用命令
  19. PHP——函数夯实基础篇
  20. ASP.NET动态网站制作(15)-- SQL数据库(1)

热门文章

  1. E03.03 A ‘Masculinity Crisis’? China Says the Boys Are Not All Right
  2. Oracle 用户无法登录 LOCKED(TIMED)
  3. Jenkins持续集成结合Docker Swarm集群实现Web应用部署的发布
  4. 【iOS开发】iOS系统更新到10.3以上无法下载企业应用
  5. Redis缓存有效时间设置到第二天凌晨的秒数
  6. 自动驾驶神经网络是什么,自动驾驶神经网络算法
  7. HTML给整个框架插入背景图片
  8. Invalid character (CR or LF) found in method name
  9. 在项目中CR是什么意思?
  10. 批量ping网段内的主机