本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!

讲到实战,就不得不拿两个例子来说明,本篇想拿的是应用最广泛的两个:Ams和Wms,一个管理activity,一个管理窗口,而前面我们已经讲了不少,本篇不再赘述。

关于Ams对activity的管理,无非这几个方面:启动哪个activity、物理按键对activity处理、内存骤减时activity的回收规则,以及暂停activity的一系列操作。

先说如何启动activity?有哪些知识点。

Ams调度activity,运行某activity需要通过Ams决定,一般情况下仅限允许均可执行;记录运行的页面,保存activity一些状态,以便下次启动更加快速,一般放在ActivityRecord,以前叫HistoryRecord;做进程管理,清楚运行哪些进程,各自的状态和信息集合

当然Ams除了管理activity,还会管理其他两个主要数据类,如Process和Task;Process同样也有个ProcessRecord用来记录进程运行信息和状态以及内部有哪些组件(指service、provider这样的),多个apk可以运行在同一进程中,可以通过android:process来设置,一般情况下还是不要,因为手机默认64M内存(不同分辨率不同),内存一般也仅够一个apk使用;而TaskRecord记录activity所在的栈的id、intent和activity的数量,用于对栈进行管理。

AMS常见的默认系统常量:

1、MAX_ACTIVITIES=20: 通过硬件设置,当前页面仅有一个activity存活,其他20个被缓存起来,超过则杀死优先低的

2、MAX_RECENT_TASKS=20:通过硬件设置,栈最多存储20个,超过后最先舍弃最早的栈

3、PAUSE_TIMEOUT=500:启动activity最长时间为500ms,否则关闭该页面,一般表现是“屏幕黑一下”

4、LAUNCH_TIMEOUT=10*1000:启动进程最长10s,否则舍弃

5、PROC_START_TIMEOUT=10*1000:启动进程后10s内必须报告AMS启动状态,否则AMS不承认此进程的存在

以上是最基础的变量,相信在多年开发中,大家对其引起的现象已经历历在目

其次在启动和暂停activity过程中,需要一些变量来保存状态,主要因为AMS是通过Service机制动作,比如

1、mPendingActivityLaunched:即将启动的activity列表

2、mStoppingActivities:A跳入B,B启动后A即被加入此队列,区别于mHistory的A跳入即加入

3、mHistory:所有后台activity,A跳入B即A被加入,或按Home键B被加入,回来按回退键则B被清除;finishing=true

4、mFinishingActivities:上面列表中执行完finish方法的activity,此时并未完全杀死等待被回收

5、mLRUActivities:最近使用的activity,包含mHistory里的activity和已经清除的activity

此外还有HistroyRecord记录mPausingActivity(record对象,记录执行onPause方法的activity),mResumeActivity、mFocusedActvity和mLastPausedActivity等。

上面讲完基础,接下来进程流程,讲讲如何启动Activity?

一般调用方式有四种,点击手机图标、执行startActivityForResult(startActivity同理)、注册硬件监听启动、被action启动

以下是启动Activity的过程和startActivityForResult的启动(startActivity传一个默认的requestCode-1,最后仍然调用startActivityForResult方法,所以放在一起讲)

启动前的权限检查和准备工作

启动Activity的时候往往还需要进行权限检查,以查看其是否符合启动条件。查询不满足条件则返回,否则继续;检查Component是否存在,不满足返回,满足继续,启动startActivityLocked(caller,intent)方法,完成以下几件事

1、检查如果启动自己则直接返回

2、会加入INTENT_FLAG_FORWARD_RESULT标志,用于从C直接返回结果给A,使用方法A调用startActivityForResut->B调用startActivity->C调用setResult直接回到A

3、检查call-ActivityRecord是否存在且有指定权限

4、如果Ams中有IActivityController对象,则通知Controller进行相应控制

5、创建临时HistoryRecord对象,不一定加入mHistory列表,如不关闭则加入

6、检查是否允许切换activity,否则加入mPendingActivityLaunched

7、判断pendingActivity是否有要启动的activity,有则先执行,无则执行该intent

如想了解launchMode,请移步:

Android基础之Activity launchMode详解

如想了解Intent,请移步: 

Intent中的四个重要属性——Action、Data、Category、Extras

以上是对intent的介绍,接下来会再介绍一下task,也就是如何启动,以什么样的规则启动和退出。以下均指launchFlag,标记均以FLAG_ACTIVITY_开头,介绍时会忽略,请注意一下;启动时会依次判断如下标识

1、NO_USER_ACTION:含义无用户交互;基本不用,主要防止一段时间后执行onUserLeaving方法;接下来如果立即启动,就把r.delay.Resume设为true

2、PREVIOUS_IS_TOP:含义上个intent是否位于栈顶,基本不用;然后为activity赋予权限加入缓存;此时区别于launchMode和launchFlag,前者指activity自己声明的启动方式,后者是明显启动者想让activity如何启动,能过intent设置,但两者有相通性

3、NEW_TASK、SINGLE_TASK、SINGLE_INSTANCE:使用这三者均不建议使用startActivityForResut,而只限于使用startActivity即r.resultTo=0,不需要回传数据的情况下;第1个是会新起一个task,即两个task之间最好不进行数据回传;2和3的相同在于,如果已经存在这样的task和component以及其他相同数据如intent,则均跳到相应栈中,不存在则声明一个新的task;不同点在于:2的task里可以包含多个activity,而3仅能包含一个;可能跟每个task内存大小有关,不同功用的activity可以申请不同的task使用,这一点也可以看上面“Android基本之Activity LaunchMode详解

4、CLEAR_TOP、REORDER_TO_FRONT:前者如自己存在,则清除该栈上面的其他activity;后者仅把自己放在最上面;举例A1->A2->A3,前者启动A2则变成A1->A2,后者启动A2则变成A1->A3->A2

5、NO_HISTORY:不要保存自己,设置A3,则A1->A2->A3->A4,执行完还是A1->A2,皮之不存,毛将焉附。

一般情况下,以上如果调整栈的顺序,那么是可以执行的;如A、B、C三个栈,分别有2个activity,如果从C切换到B,那么是这样的A1->A2->B1->B2->C1->C2,调整后A1->A2->C1->C2->B1->B2。

系统还提供另外一种办法来设置activity的启动方式,那就是

1、android:clearTaskOnLaunch=true/false:是否清除task里的其他activity

2、android:finishOnTaskLaunch:是否关闭task中已有的此activity

3、android:allowTaskReparent:是否将自己带入启动的task中

4、android:alwaysRetainTaskState:是否由系统维护activity状态;一般应用在根activity,每次可以打开最后打开的页面

最近讲的几个东西之间的关系,画了一张图,如下所示

正式启动工作(主要流程均为ActivityThread执行-在attach时初始化)

一、暂停当前activity

1、判断该activity是否存在;然后执行onUserLeaving方法,避免此activity再与物理按键交互,如后退键

2、调用performPauseActivity(告知暂停而非超时等情况,将prev指向自己),先onSaveInstanceState,再执行onPause

·3、报告AMS暂停完毕,通过IPC调用AMS的completePauseActivity方法

二、调用resumeTopActivity方法:

1、如果mHistory有记录且直接启动,否则执行startHomeActivityLocked方法启动主界面程序;

2、系统处于睡眠状态或当前activity未被暂停,则停止;

3、从mStoppingActivities和mWarningVisibleActivities里移除目标对象;

4、将被停止的activity设置为不可见状态,通知activty或task切换

5、判断目录进程是否存在并且activityThread存活,否则执行startSpecificActivityLocked方法;如果进程不存在,则调用

Process类启动新进程,设置pid加入ProcessRecord,启动完之后再通知Ams启动目标Activity,至于启动Process的过程跟调用

暂停或启用activity的过程无异,仅仅参数发生变化而已,以及变量不同和意义不同。当然我们再讲一点,启动进程毕

竟是个重要流程,提取odex文件,前面几篇文章有过介绍,指已经优化过的dex文件,通过Service、Provider和

Broadcast加入引用,创建完成。

6、最终调用performLaunchActivity,执行attach,执行setTheme,几个on方法,拿到DecorView加入Wms,设

置可见。

接下来咱们讲讲停止工作:一般情况下是长时间不使用,或者应用内存紧张,或者启动时设置no_history才会执行,执行过程

而应用与上面生命周期无异,也就是设置不可见,加个mStoppingActivities,异步通知Ams停止,最后调用onStop方法等等,

关闭activity也同样如此;至于关闭的优先级前面似乎没讲,接下来会着重介绍一下。

Android系统如何管理自己内存的?同样可以预习一下,原理协同,再做补充。

一般情况下优化级分为-16到15级,而android默认仅使用0-15级,越小优先级越高,当前可见activity最高为0。

为什么会产生这个优化级,主要android采用的是关闭而不退出,退出而不清理的原则,主要为了二次加载更加快速;

其次是上面停止activity的原因。而这个规则由Ams加权得出,有这样几个变量:是否展示在当前页或是持久化

activity、相应的配合组件、剩余内存量、HOME进程、活跃activity数量和组件等,就像JVM的内存管理规则一样,

详见:Java高级之虚拟机垃圾回收机制

由于Ams无法预知内存的变动(OOM除外),因而采用这套机制:最先是空进程(无activity进程),其次是

activity,再次是前台的配合组件,如Service、Receiver、Provider,最后才是前台activity;而这些操作由OOM Killer

进程直接管理,关于activity回收需要满足以下几种情况:

1、finish操作或crash或anr,通过updateOomAdjLocked方法让其指定优先级,动态调整

2、如果运行的activity超过20个,必须是已经stop的、不可见的、非常驻进程

因而不合理的手机架构也会造成系统的崩溃。PS:持久化对象的在ProcessRecord的persistent变量是否为true

LocalActivityManager存在于Activity内部,用来装载和管理activity以及各种状态,维持一个最低的内存消耗;核

心在于它使用UI线程的activityThread来装载指定的 activity;有同学可能会问task越少或者histroyRecord越小,内存

会占用越小吗?这个问题跟手机里装一个app和装n个app重量是否增加是一样的道理,但是越过限制以后(task允许)

内存占用确实会变大,直到出现OOM。

以下是按键方面的内容(锁屏下,基本均不操作)

1、后退键:Activity里监听到onBackPressed方法;一般执行的是finish动作,执行performDestroyActivity方法

2、Home键:Acitivty的onKey方法无法截取它,属于设计原因;Wms中使用PhoneWindowManager类中的

interceptKeyTi截取消息,发现是它,再执行launchHomeFromHotKey;2.0之后添加另一个Home键执行

startDockOrHome方法来监听硬件。

与普通的启动区别在于,能启动特殊的intent,方法在ContextImpl里。而长按震动、关闭所有窗口、会弹出LRT-

lasted recent task,可以调用Ams的getRecentTask来取出,通过弹出的窗体的点击事件,进入相应的应用。

总结一下:

进程启动:1、内核创建进程数据结构,指出其地址总线

2、装载函数,读取代码,拿到数据总线

3、将程序指针指向目标地址入口

虚拟机启动:首次是从Zygote进程fork出来一个子进程,用来加载资源,然后启动SystemSever,用来监

控手机按键和触摸事件,以及管理Ams、Pms、Wms等,最终根据配置文件的HomeActivity,启动它。

Activity启动,然后触发init.rc文件执行main函数,启动一个ActivityThread

这就是所谓的UI线程,由Looper声明一个MessageQueue,接着创建Activity对象,初始化ViewRoot和token,用来分

发消息和接收转换为本地消息,为后面执行Activity的生命周期,创建PhoneWindow,执行attach方法,初始化内部组

件,根据Wms返回的消息,适时的执行生命周期,执行setContentView创建DecorView(View内部也有ViewRoot来设置View的各种属性),由Wms加入到窗口中,设置Visiable,加载结束。

注:每个应用仅有一个ActivityThread来异步处理内部事务,如果出错则App Crash;具体应用启动后,会创建ApplicationThread和ActivityThread,分别用来处理应用事务和Activity事务,并且创建MessageQueue,不停的轮循;AMS通过判断加载某个Activity和资源的加载情况,将消息从手机端通过Binder发给当前应用的ViewRoot对象,通过handler把消息放入消息队列,轮循出来的消息处理,即推动Activity的生命周期执行。

Android源码剖析之Framework层实战版(Ams管理Activity启动)的更多相关文章

  1. Android源码剖析之Framework层基础版(窗口、linux、token、Binder)

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 关于Framework,就是应用层底下的控制层,离应用层最近,总想找个机会,写写WindowMang ...

  2. Android源码剖析之Framework层进阶版(Wms窗口管理)

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 上一篇我们主要讲了Ams,篇幅有限,本篇再讲讲Wms,即WindowManagerService,管 ...

  3. Android源码剖析之Framework层升级版(窗口、系统启动)

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 看本篇文章之前,建议先查看: Android源码剖析之Framework层基础版 前面讲了frame ...

  4. Android源码剖析之Framwork层后记篇(硬件消息传递、apk管理、输入法框架、编译过程)

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 既然写到后记篇,就代表本系列到此为止,暂时告一段落:其他一些Manager随后有时间再补,就像源码的 ...

  5. Android源码剖析之Framwork层消息传递(Wms到View)

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 前面讲过Wms.Ams与Activity的一系列交互,包括创建过程.消息传递.窗口展示等,紧接上篇介 ...

  6. (原创滴~)STL源码剖析读书总结1——GP和内存管理

    读完侯捷先生的<STL源码剖析>,感觉真如他本人所说的"庖丁解牛,恢恢乎游刃有余",STL底层的实现一览无余,给人一种自己的C++水平又提升了一个level的幻觉,呵呵 ...

  7. 为什么Android源码中都使用16进制进行状态管理?

    前言 在Android源码中,对于"多状态"的管理总是通过16进制数字来表示,类似这种格式: //ViewGroup.java protected int mGroupFlags; ...

  8. Spark源码剖析 - SparkContext的初始化(八)_初始化管理器BlockManager

    8.初始化管理器BlockManager 无论是Spark的初始化阶段还是任务提交.执行阶段,始终离不开存储体系.Spark为了避免Hadoop读写磁盘的I/O操作成为性能瓶颈,优先将配置信息.计算结 ...

  9. android 源码编译及其运行模拟器相关问题记录

    最近一直在看android源码相关的文档,包括编译源码,还有framework层的代码,本人很懒,一直没有写博客,今天想自己在编译一下源码,并且运行在模拟器中. 源码的版本不同,需要的jdk可能也有所 ...

随机推荐

  1. hdu 1455 Sticks

    Sticks Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Statu ...

  2. maven自动化部署插件sshexec-maven-plugin

    在maven pom.xml 文件plugins里增加               <plugin>                 <groupId>com.github.g ...

  3. 【python游戏编程之旅】第五篇---嗷大喵爱吃鱼小游戏开发实例

    本系列博客介绍以python+pygame库进行小游戏的开发.有写的不对之处还望各位海涵. 我们一同在前几期的博客中已经学到了很多pygame的基本知识了,现在该做个小游戏实战一下了. 前几期博客链接 ...

  4. BZOJ4389 : ZYB and Trees

    Link-Cut Tree维护. 每个点x维护以下信息: v:这个点的点权 s:实链上的信息和 st:子树信息和(不包括链上) sa:子树+链上的信息和 as:所有虚儿子的sa的和 则有 s[x]=v ...

  5. BZOJ2584 : [Wc2012]memory

    xy方向分开考虑 用扫描线处理出拓扑序,第二问直接回答拓扑序, 第一问: 将操作倒过来,变成加入线段,用线段树维护区间拓扑序的最值 #include<cstdio> #include< ...

  6. vnc使用

    使用rpm –qa vnc命令如果收到如下信息说明已经安装了vncserver, [root@localhost: ~]#rpm -qa |grep vnc gtk-vnc-python--.el5 ...

  7. java定时器的使用

    定时器类Timer在java.util包中.使用时,先实例化,然后使用实例的schedule(TimerTask task, long delay)方法,设定指定的任务task在指定的延迟delay后 ...

  8. [百科] - iLBC

    iLBC是一种专为包交换网络通信设计的编解码,优于目前流行的G.729.G.723.1,对丢包进行了特有处理,即使在丢包率相当高的网络环境下,仍可获得非常清晰的语音效果. 30ms ptime的iLB ...

  9. 关于isset使用产生Can't use function return value in write context错误

    在使用isset检测session的一个取值是否存在时,产生了这个问题 翻译一下:不能在填写的内容中使用函数的返回值.然后我查看了php手册看isset函数的使用:isset()只能用于变量,因为传递 ...

  10. JAVA字符串转日期或日期转字

    文章中,用的API是SimpleDateFormat,它是属于java.text.SimpleDateFormat,所以请记得import进来! 用法:    SimpleDateFormat sdf ...