Android Activity的工作原理

Activity 是四大组件中用的最多的,也是最复杂的,本篇文章就重点总结Activity的启动和通信原理

AMS(AcctivityManagerService)从字面意思理解看是管理Activity的,其实四大组件都归它管

PMS(PackageManagerService)

ActivityManagerNative(AMN)

ActivityManagerPoxy(AMP)

ApplicationThread(APT)

app是如何启动的

在手机屏幕上点击某个App的图标,假设是斗鱼app,这个APP的首页面就会展示在我们面前,看似简单的操作,背后经历了Activity和AMS的反反复复的通信过程。首选要搞清楚手机屏幕是一个Activity,这个Activity所在的app,业界称之为Launcher,Launcher是各大手机厂商提供的。

Launcher为每个app的图标提供了启动这个app所需要的Intent信息,如:包名、首页面地址、category等等,这些信息是app安装的时候,PackageManagerService从斗鱼apk包中的AndroidManifest文件中读取到的。

启动流程:

①Launcher通知AMS,要启动斗鱼app,而且要指定启动app的哪个页面

②AMS收到消息后,把要启动的页面记录下来

③Launcher当前页面进入Paused状态,然后通知AMS,“你去找对应的app吧”

④AMS检查斗鱼app是否已经启动了,是,则唤起斗鱼app,否,就要启动一个新的进程,AMS所在的新进程中创建一个ActivityThread对象,启动相对应的main函数

⑤app启动后,通知AMS,“我启动好了”

⑥AMS翻出之前记录下的值(要启动的页面),告诉斗鱼app,启动这个页面

⑦斗鱼app启动首页,创建Context并与首页Activity关联,然后调用启动页面的Activity的onCreate函数

流程至此完成,分为两部分,1~3阶段:Launcher和AMS相互通信,4~7阶段:斗鱼app与AMS相互通信

第一阶段:Launcher通知AMS

上图就是点击Launcher上的app之后,会调用Launcher的startActivitySafely方法,其实还是会调用Activity的startActivity方法,intent中带着要启动app所需要的关键信息,如:启动的页面类名等等,所以为什么要在Manfest中,给首页面指定的action和category了,就是在启动的时候Launcher会找到这个关键信息进行启动

启动app时候,调来调去最终会调用startActivityForResult方法,startActivityForResult的代码如下:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
              
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

在以上代码中,我们发现了一个mMainThread变量,这是一个ActivityThread类型的变量,就是主线程,也就是UI线程,它代表了整个app,读者不禁要问,ActivityThread代表了App应用程序,那么Application类代表什么?其实,Application对于android系统来说并没有那么重要,它只是个上下文,Application是app的上下文,Context是Activity的上下文。

通过ActivityThread的getApplicationThread方法取到一个Binder对象,这个对象类型为ApplicationThread,代表了launcher所在的app进程,.mToken也是一个Binder对象,代表launcher这个Activity也通过Instumentation传给AMS,AMS查询记录就知道是谁向AMS发送请求了。

查看mInstrumentation.execStartActivity方法,借助execStartActivity,Activity会把数据传递给ActivityManagerNative。

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

AMN的getDefault方法返回类型为IActivityManager,而不是AMP,AMN和AMP都实现了IActivityManager接口,AMS继承了AMN。

这样,第一步骤就完成了

第二阶段:AMS处理Launcher传过来的信息

1:Binder(也就是AMN和AMP)和AMS通信,肯定每次是做不同的事,比如这次Launcher要启动斗鱼app,那么会发送类型为START_ACTIVITY的请求给AMS,同时会告诉AMS要启动哪个Activity

2:AMS说:“好了,我知道了”,然后会检查AndroidManifest文件,检查是否存在要启动的Activity,如果不存在就抛出Activity not found异常信息

3:AMS通知Launcher:“没你什么事了,洗洗睡吧”,那么AMS是通过什么途径告诉Launcher的呢?结论是:AMS通过ApplicationThreadProxy发送消息,而App端则通过ApplicationThread来接受这个消息。

第三阶段:Launcher去休眠,然后通知AMS,“我休眠了”

ApplicationThread(APT)接收到来自AMS的消息后,调用AcctivityThread的sendMessage方法,向Launcher的主线程消息队列发送PAUSE_ACTIVITY消息,发送消息是通过一个名为H的Handler类完成的,AMS给Activity发送的所有消息,以及给其他三大组件发送的消息,都是经过H类,既然都经过这条路,我们就可以从这里做插件化技术。

第四阶段:AMS启动新的进程

AMS接下里要启动斗鱼App的首页,因为此app不在后台进程中,所以要启动一个新的进程,这里调用的事Process.start方法,并且指定了ActivityThread的main函数为入口函数。

第五阶段:新进程启动,以ActivityThread的main函数为入口

启动新进程,就是启动一个新的app,为这个进程创建ActivityThread对象,这就是我们熟悉的UI主线程

创建好UI线程后,立刻进入ActivityThread的main函数,接下来要做两件具有重大意义的事,

①创建一个主线程Looper,也就是MainLooper,MainLooper就是在这里创建的

②创建Applicaition,Application也是在这里创建的

主线程在收到BIND_APPLICATION消息后,根据传递过来的ApplicationInfo创建一个对应的LoadedApk对象(标志当前apk信息),然后创建ContextImpl对象(标志当前进程环境),紧接着通过反射创建目标Application,并调用其attach方法,将ContextImpl对象设置为目标Applcation的上下文环境,最后调用Application的onCreate函数,做一些初始化工作

App的灵魂是ActivityTread,也就是主线程,app开发人员用不到,但使用反射是可以修改这个类的一些行为的。

创建App的最后就是要告诉AMS,“我启动好了”,同时把自己的ActivityThread对象发送给AMS,AMS电话簿中就多了这个新的App登记信息,AMS以后就通过这个ActivityThread对象,向这个App发送消息

第六阶段:AMS告诉新App要启动哪个Acctivity

AMS把传入的ActivityThread对象转为一个ApplicationThread对象,用于以后和这个App跨进程通信,在第六阶段AMS从过去记录中翻出来要启动哪个Activity,然后通过ATP告诉App

第七阶段:启动斗鱼首页Activity

在Binder的另一端,App通过APT接收到AMS的消息,仍然在H的handleMessage方法的swicth语句中处理,每次都是APT执行AcctivityThread的sendMessage方法,在这个方法中,把消息拼装一下,然后扔给H的swicth语句分析,来决定要执行ActivityThread的那个方法。

handleLaunchActivity方法要做的是哪些事?

①通过Instrumentati0on的newActivity方法,创建要启动的Activity实例。

②为这个Acctivity创建一个上下文Context对象,并与Activity进行关联

③通过Instrumentaction的callActivityOnCreate方法,执行Activity的onCreate方法,从而启动Activity,至此App启动完毕,这个流程是经过多次握手,App和AMS频繁的向对方发送消息,而发送消息的机制,是建立在Binder的基础之上的。

 

以上内容出自包建强老师的Android插件化开发指南,以后几篇文章同样出自此书,如感觉文章写的清晰,可以直接阅读包老师的书,如有问题,可留言,共同探讨。

 

发布了15 篇原创文章 · 获赞 0 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qinggancha/article/details/103516963
今日推荐