• Android源码剖析之Framework层实战版(Ams管理Activity启动)




      本文来自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的生命周期执行。


  • 相关阅读:
    AC自动机(转载)
    hdu 4352 XHXJ's LIS(数位dp+状压)
    hdu 4734 F(x)(数位dp)
    hdu 3709 Balanced Number(数位dp)
    hdu 6268 Master of Subgraph(点分治+bitset)
    poj 1741 tree(点分治)
    pytorch 矩阵数据增加维度unsqueeze和降低维度squeeze
    pytorch seq2seq模型中加入teacher_forcing机制
    pytorch seq2seq模型训练测试
    python os模块判断文件是否存在,file_path获取当前文件路径
  • 原文地址:https://www.cnblogs.com/fengju/p/6174389.html
Copyright © 2020-2023  润新知