(五)AMS

如果站在四大组件的角度来看,AMS就是Binder中的Server。

AMS全称是ActivityManagerService,看字面意思是管理Activity的,但其实四大组件都归它管。估计是Android底层开发人员先写了ActivityManagerService用来管理Activity,后来写Service、Receiver、CP的时候发现代码都差不多,于是就全都用ActivityManagerService,但是却忘记改名字了——我也是猜的,纯属八卦。

由此而说到了插件化,我记得16年和Lody、张勇、林光亮一起吃夜宵的时候,我当时问了困惑已久的两个问题:

1)App的安装过程,为什么不把apk解压缩到本地,这样读取图片就不用每次从apk包中读取了——这个问题,我们放到PMS那一节再详细说。

2)为什么Hook永远是在Binder Client端,也就是四大组件这边,而不是在AMS那一侧进行Hook。

这里要说清楚第二个问题。就拿Android剪切板举例吧。前面说过,这也是个Binder服务。

AMS要负责和所有App的四大组件进行通信,也真够他忙的。如果在一个App中,在AMS层面把剪切板功能给篡改了,那会导致Android系统所有的剪切板功能被篡改——这就是病毒了,如果是这样的话,Android系统早就死翘翘了。所以Android系统不允许我们这么做。

我们只能在AMS的另一侧,Client端,也就是四大组件这边做篡改,这样即使我们把剪切板功能篡改了,也只影响篡改代码所在的App,在别的App中,剪切板功能还是正常的。

关于AMS我们就说这么多,下面介绍四大组件时,会反复提到四大组件和AMS的跨进程通信。

(六)Activity 第1讲

对于做App的开发人员而言,Activity是四大组件中用的最多的,也是最复杂的,我这里只讲Activity的启动和通信原理。还有一些相关的概念,比如说View、Looper、Intent、Resource,我以后另起章节来介绍。

注:我对四大组件的分析,都是基于罗升阳的那本分析Android底层的书,我把其中罗列的大部分代码都删掉了,只保留那些对App开发人员有用的一些代码片段和一些关键类名,并融入了我对四大组件的理解。

1)首先要搞清,App是怎么启动的。

在手机屏幕上点击某个App的Icon,假设就是斗鱼App吧,这个App的首页(或引导页)就出现在我们面前了。这个看似简单的操作,背后经历了Activity和AMS的反反复复的通信过程。

首先要搞清楚,在手机屏幕上点击App的icon快捷图标,此时手机屏幕就是一个Activity,而这个Activity所在的App,业界称之为Launcher。Launcher是手机系统厂商提供的,类似小米华为这样的手机,比拼的就是谁的Launcher绚丽和人性化。

Launcher这个App,其实和我们做的各类应用类App没有什么不同,我们大家用过华为、小米之类的手机,预装App以及我们下载的各种App,都显示在Launcher上,每个App表现为一个Icon。Icon多了可以分页,可以分组,此外,Launcher也会发起网络请求,调用天气的数据,显示在屏幕上,所谓的人性化界面。

还记得我们在开发一款App时,在Manifest文件中是怎么定义默认启动Activity的么?如下所示:

而Launcher中为每个App的icon提供了启动这个App所需要的Intent信息,如下所示(比如说斗鱼的包名是):

action:android.intent.action.MAIN

category: android.intent.category.LAUNCHER

cmp: 斗鱼的包名+ 首页Activity名

这些信息是App安装(或Android系统启动)的时候,PackageManagerService从斗鱼的apk包的manifest文件中读取到的。

所以点击icon就启动了斗鱼App中的首页。

2)启动App哪有那么简单

前面介绍的只是App启动的一个最简单的概述。

仔细看,我们会发现,Launcher和斗鱼是两个不同的App,他们位于不同的进程中,它们之间的通信是通过Binder完成的——这时候AMS出场了。

仍然以启动斗鱼App为例子,整体流程是:

  1. Launcher通知AMS,要启动斗鱼App,而且指定要启动斗鱼的哪个页面(也就是首页)。
  2. AMS通知Launcher,好了我知道了,没你什么事了,同时,把要启动的首页记下来。
  3. Launcher当前页面进入Paused状态,然后通知AMS,我睡了,你可以去找斗鱼App了。
  4. AMS检查斗鱼App是否已经启动了。是,则唤起斗鱼App即可。否,就要启动一个新的进程。AMS在新进程中创建一个ActivityThread对象,启动其中的main函数。
  5. 斗鱼App启动后,通知AMS,说我启动好了。
  6. AMS翻出之前在第二步存的值,告诉斗鱼App,启动哪个页面。
  7. 斗鱼App启动首页,创建Application,创建Context并与首页Activity关联。然后调用首页Activity的onCreate函数。

至此启动流程完成,分成两部分,1-3步,Launcher和AMS相互通信,而后面几步,斗鱼App和AMS相互通信。

这会牵扯一堆类进来,列举如下,在接下来的分析中,我们都会遇到:

  • Instrumentation
  • ActivityThread
  • H
  • LoadedApk
  • AMS
  • ActivityManagerNative和ActivityManagerProxy
  • ApplicationThread和ApplicationThreadProxy

第1阶段 Launcher通知AMS

这是我根据老罗那本书中对Activity的分析,自己手绘的UML图,一共七张,也就是Activity启动所经历的七个阶段。建议各位读者也亲自手绘一遍,从而就加深理解。

  • 第1步、第2步

从上图中我们看到,点击Launcher上的斗鱼App的icon快捷图标,这时会调用Launcher的startActivitySafely方法,其实还是会调用Activity的startActivity方法,intent中带着要启动斗鱼App所需要的关键信息,如下所示:

action = “android.intent.action.MAIN”

category = “android.intent.category.LAUNCHER”

cmp = “com.douyu.activity.MainActivity”

第3行代码是我猜的,就是斗鱼App在Mainfest文件中指定为首页的那个Activity。这样,我们终于明白,为什么在Mainfest中,给首页指定action和category了。在app的安装过程中,会把这个信息“记录”在Launcher的斗鱼启动快捷图标中。关于App的安装过程,我会在后面的文章详细介绍。

startActivity这个方法,如果我们看它的实现,会发现它调来调去,经过一系列startActivity的重载方法,最后会走到startActivityForResult方法。

我们知道startActivityForResult需要两个参数,一个是intent,另一个是code,这里code是-1,表示Launcher才不关心斗鱼的App是否启动成功了呢。

第3步: startActivityForResult

Activity内部会保持一个对Instrumentation的引用,但凡是做过App单元测试的同学,对这个类都很熟悉,称之为仪表盘。

在startActivityForResult方法的实现中,会调用Instrumentation的execStartActivity方法。

看到这里,我们发现有个mMainThread变量,这是一个ActivityThread类型的变量。

这个家伙的来头可不小。

ActivityThread,就是主线程,也就是UI线程,它是在App启动时创建的,它代表了App应用程序。

啥?ActivityThread代表了App应用程序,那Application类岂不是被架空了?其实,Application对我们App开发人员来说也许很重要,但是在Android系统中还真的没那么重要,他就是个上下文。Activity不是有个Context上下文吗?Application就是整个ActivityThread的上下文。

ActivityThread则没有那么简单了。它里面有main函数。

我们知道大部分程序都有main函数,比如java、C#,远了不说,iPhone App用到的Objective-C,也有main函数,那么Android的main函数藏在哪里?就在ActivityThread中,如下所示,代码太多,我只截取了一部分

又有人会问?不是说谁写的程序,谁就要提供main函数,作为入口吗?但Android App却不是这样的。Android App的main函数,在ActivityThread里面,而这个类是Android系统提供的底层类,不是我们提供的。

所以这就是Andoid有趣的地方。Android App的入口是Mainifest中定义默认启动Activity。这是由Android AMS与四大组件的通信机制决定的。

最近在看吴越版的西游记,就发现这个西天取经为啥用了十几年啊?因为这师徒四个取经路上爱管闲事,所以耽搁了很久。我这篇文章也是如此,经常讲着讲着就跑题了,再这么写下去,不知道要写到啥时候,所以我们一路向西,径直往前走,再遇到奇怪的类,先不要理它。

在回到代码来,这里要传递2个很重要的参数:

  • 通过ActivityThread的getApplicationThread方法取到一个Binder对象,它的类型为ApplicationThread,它代表着Launcher所在的App进程。
  • mToken,这也是个Binder对象,它代表了Launcher这个Activity,这里也通过Instrumentation传给AMS,AMS一查电话簿,就知道是谁向AMS发起请求了。

这两个参数是伏笔,传递给AMS,以后AMS想反过来通知Launcher,就能通过这两个参数,找到Launcher。

第4步,Instrumentation的execStartActivity方法

Instrumentation绝对是Adnroid测试团队的最爱,因为它可以帮我们启动Activity。

回到我们的App启动过程来,在Instrumentation的execStartActivity方法中,

我理解这就是一个透传,Activity把数据借助Instrumentation,传递给ActivityManagerNative,没太多有趣的内容,就不多讲了。

第5步:AMN的getDefault方法

ActivityManagerNative,简称AMN。这个类后面会反复用到。

AMN通过getDefault方法,从ServiceManager中取得一个名为activity的对象,然后把它包装成一个ActivityManagerProxy对象(简称AMP),AMP就是AMS的代理对象。

备注1:ServiceManager是一个容器类。

备注2:  AMN的getDefault方法返回类型为IActivityManager,而不是AMP。IActivityManager是一个实现了IInterface的接口,里面定义了四大组件所有的生命周期。

AMN和AMP都实现了IActivityManager接口,AMS继承自AMN(好乱),那么对照着前面AIDL的UML,就不难理解了:

第6步,AMP的startActivity方法

看到这里,你会发现AMP的startActivity方法,和AIDL的Proxy方法,是一模一样的,写入数据到另一个进程,也就是AMS,然后等待AMS返回结果。

至此,第一阶段的工作就做完了。

后续流程请参加下一篇文章。

写给Android App开发人员看的Android底层知识(2)的更多相关文章

  1. 写给Android App开发人员看的Android底层知识(1)

    这个系列的文章一共8篇,我酝酿了很多年,参考了很多资源,查看了很多源码,直到今天把它写出来,也是战战兢兢,生怕什么地方写错了,贻笑大方. (一)引言 早在我还是Android菜鸟的时候,有很多技术我都 ...

  2. 写给Android App开发人员看的Android底层知识(6)

    (十一)BroadcastReceiver BroadcastReceiver,也就是广播,简称Receiver. 很多App开发人员表示,从来没用过Receiver.其实吧,对于音乐播放类App,用 ...

  3. 写给Android App开发人员看的Android底层知识(3)

    (七)App启动流程第2篇 书接上文,App启动一共有七个阶段,上篇文章篇幅所限,我们只看了第一阶段,接下来讲剩余的六个阶段,仍然是拿斗鱼App举例子. 简单回顾一下第一阶段的流程,就是Launche ...

  4. 写给Android App开发人员看的Android底层知识(5)

    (十)Service Service有两套流程,一套是启动流程,另一套是绑定流程.我们做App开发的同学都应该知道. 1)在新进程启动Service 我们先看Service启动过程,假设要启动的Ser ...

  5. 写给Android App开发人员看的Android底层知识(7)

    (十二)ContentProvider (1)ContentProvider是什么? ContentProvider,简称CP. 做App开发的同学,尤其是电商类App,对CP并不熟悉,对这个概念的最 ...

  6. 写给Android App开发人员看的Android底层知识(8)

    (十)PMS及App安装过程 PMS,全称PackageManagerService,是用来获取Apk包的信息的. 在前面分析四大组件与AMS通信的时候,我们介绍过,AMS总是会使用PMS加载包的信息 ...

  7. 写给Android App开发人员看的Android底层知识(4)

    (八)App内部的页面跳转 在介绍完App的启动流程后,我们发现,其实就是启动一个App的首页. 接下来我们看App内部页面的跳转. 从ActivityA跳转到ActivityB,其实可以把Activ ...

  8. 一看就懂的Android APP开发入门教程

    一看就懂的Android APP开发入门教程 作者: 字体:[增加 减小] 类型:转载   这篇文章主要介绍了Android APP开发入门教程,从SDK下载.开发环境搭建.代码编写.APP打包等步骤 ...

  9. 谋哥:App开发人员的苦逼不值得怜悯!!

    [谋哥每天一干货,第四十篇]         为什么取这个标题呢?由于昨天一些本来"支持"谋哥的人看到谋哥搞收费VIP群,认为谋哥赚苦逼开发人员的钱非常不道德,且说谋哥我写的东西都 ...

随机推荐

  1. SQL 游标的应用

    ----------------SQL游标应用-----------------if object_id('tempdb..#test0001') is not null drop table #te ...

  2. Vue.use自定义自己的全局组件

    通常我们在vue里面使用别人开发的组件,第一步就是install,第二步在main.js里面引入,第三步Vue.use这个组件.今天我简单的也来use一个自己的组件. 这里我用的webpack-sim ...

  3. 云计算+SaaS+业务开发平台=JSAAS云平台

    我关注Google的代码托管.Open API,我也关注Oracle会把MYSQL怎么样云数据库化,我也虚拟化技术多实例化独立的数据库,我也关注facebook的平台插件应用架构,我也关注salesf ...

  4. Selenium测试专项一班隆重开班

    Selenium测试专项一班隆重开班 应广大测试技术人员要求,以及企业技术需求.Selenium提前一周开课了,只针对合作的每家企业提供1-2个参训名额.预计培训60人次.但报名人数却远远超出我们预期 ...

  5. 老李推荐:第8章1节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-运行环境初始化

    老李推荐:第8章1节<MonkeyRunner源码剖析>MonkeyRunner启动运行过程-运行环境初始化   首先大家应该清楚的一点是,MonkeyRunner的运行是牵涉到主机端和目 ...

  6. word-wrap: break-word;与word-break: break-all;文本自动换行

    word-break:break-all和word-wrap:break-word都是能使其容器如DIV的内容自动换行它们的区别就在于:1,word-break:break-all 例如div宽200 ...

  7. Python多线程Selenium跨浏览器测试

    前言 在web测试中,不可避免的一个测试就是浏览器兼容性测试,在没有自动化测试前,我们总是苦逼的在一台或多台机器上安装N种浏览器,然后手工在不同的浏览器上验证主业务流程和关键功能模块功能,以检测不同浏 ...

  8. 修改数据库用户名--CMD环境执行有效

    --CMD环境执行有效 --修改数据库用户名 select * from user$ where name='aa';   update user$ set name='bb' where name  ...

  9. URL转换成二维码

    转载请注明出处:http://www.cnblogs.com/cnwutianhao/p/6685804.html 二维码已经成为我们日常生活中的一个不可获取的产物,火车票上,景区门票,超市付款等等都 ...

  10. js创建xml对象

    js创建xml对象 //创建对象 function getDataXML() {     var objTds = $("TEXTAREA");     var count = o ...