第二章 深入启动虎穴

tips:部分读者可能对此文会引起不适,若有此感,敬请跳过。

“我就知道你还会再来的,说吧,这次想学习什么?”,还没等我出声,Story大神就开口了,“上一次我进来是学到了不少知识,而且app启动也优化了不少,但是这些都是表面的知识,我在优化的过程中突然就想到了,我点击图标的时候,app是怎么启动起来的?”我充满疑问的问道,“哈哈,我就知道你是孺子可教也,你确定要深入虎穴吗?这可是比较烧脑的环节哦!”,“确定!”,我想都没想就回答了,为了追求未知的知识,我还是比较坚定的。

虎穴,打开!

就在这时,一堆代码仿佛像挣脱了缰绳的马一样飞奔而来,并且整齐的列好队排列在我眼前。“请大家自我介绍一下”,Story大神对它们说道。

“你好,我是 Zygote,我是Android系统创造出来的,而我的作用就是为Android系统生孩子。

“我叫 ActivityManager,一听我的名字就知道我是一个Activity管理层(不单单是Activity的管理,更是其他三大组件的管理者),无论是创建还是跳转,都要经过我的审判,才能继续下去,也就是说我掌握着Activity的生死大权。还有,你看到站在我身边的都是我的手下,我左边的这个是 IActivityManager,它就是对我的命令进行了封装装饰,我右手边的是 ActivityManagerProxy ,它就是对封装装饰好的命令进行下传的,而我后面的那位是 ActivityManagerService ,它就是要对命令执行的人了,别看我衣着光鲜,其实还是挺累的,一个个 代理 着我的命令进行传递。”

“大家好,我叫 ActivityThread , 我的职责是管理应用进程的主线程的执行,比如快速处理UI界面的变化,还有一些需要在主线程上调度都是我的管辖范围。”

“我是 Process ,想要进程,就来我这里申领吧。”

听着它们讲了一大堆,我一直在点头,其实我内心是懵逼的,这是什么跟什么啊。貌似Story大神看出了我的疑问,就对我说了,“是不是没听懂它们在说什么?”,我点点头,“哈哈,来吧,走进它们的世界看一下你就会了如指掌的了,过来吧,”我还是心怀疑虑的走了过去。

简直是走进了Android系统的五章六腑,各个部门配合紧密,处理速度之迅猛。“到了”,Story大神带我走进了一个类似车间的一个工作室,眼前的那一幕是我从所未见的,当然,自从进来这里之后,每次都会刷新我的世界观和人生观!

刚刚还在自我介绍的它们,现在已经在认真地工作中了,Zygote 正在分裂复制着(fork),真的像它刚刚介绍的那样,在生孩子,所有的应用程序的进程都是又它生出来的。这时候从中间浮出一段文字。

应用程序的运行,都是基于 Android的虚拟机,而且每次启动都会带来很大的开销,而 Zygote 的生产方式则能够提升不小的效率。在这个生产的过程中,采用了Linux 的 写时拷贝技术 Copy-on-Write 的方式,重复利用了 Zygote 上的资源,达到了最大的效率。

首先, Zygote 会开启一个Socket:registerZygoteSocket(socketName),也就是Zygote Socket,这个Socket就是去监听应用程序的请求的,当有请求的话就会立刻通知 Zygote 进行生产,同时还启动了 SystemServer

SystemServer ?这又是什么来的?“刚刚因为它太忙了,所以没出来跟你打招呼,现在跟你介绍一下它吧”。

SystemServer ,是 Zygote 的大儿子,在生产过程中扮演中无比重要的角色,正如其名,系统中的所有服务都与它有着密切的关系,而且整个Android中的核心Service都在其中,比如刚刚跟你介绍的 ActivityManagerServiceZygote 统管着后宫大权(生孩子),而 SystemServer 则是名副其实的一人之上万人之下的统治者了!

紧接着, ActivityManager 出现了,只见最上层的编码者(就是我了),点击了app,这时, ActivityManager 开发工作了,发出 startActivity(intent) 的命令,app启动了,而在系统中,又多了一个进程出来,而系统又是一个进程,那么这个命令就由中间的管理服务者
ActivityManagerService来调度了,而这是属于两个进程间的通信,又设计到了跨进程通信了(IPC)。

掌权者 ActivityManager 是所有人都能看得到权力所有者,对接着所有的程序员,下发了命令之后通过 IActivityManager 的修饰封装,比如 startActivityfinishActivity等命令,通过 ActivityManagerProxy 把命令传递到执行者 ActivityManagerService 手中,再进行相应的处理。

还不明白吗?我们从上至下再详细讲解下这个流程吧。

当我们点击App时,调用了Activity的startActivity,接着调用了startActivityForResult。

    @Override
    public void startActivity(Intent intent) {
       this.startActivity(intent, null);  //  <------调用
    }
    
    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);  //  <------调用
        } else {
            startActivityForResult(intent, -1);
        }
    }
    
    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);//  <------调用  来到这里: mInstrumentation.execStartActivity!!!
            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);
            }
        }
    }

看到代码,会调用startActivityForResult中的 mInstrumentation.execStartActivity,那这个Instrumentation又是有什么作用的呢?一环套着一环!

远处慢慢飘来Instrumentation的介绍。

Base class for implementing application instrumentation code. When running with instrumentation turned on, this class will be instantiated for you before any of the application code, allowing you to monitor all of the interaction the system has with the application. An Instrumentation implementation is described to the system through an AndroidManifest.xml's <instrumentation> tag.

这个是Android系统对它的一个定义,说了什么呢?我们来翻译一下,现在知道英语好的重要性了吗?

用于实现应用程序代码的基类。当使用Instrumentation进行运行时,该类将在任何应用程序代码之前为您实例化,允许您监视系统与应用程序之间的所有交互。Instrumentation实现了系统通过AndroidManifest. xml的<instrumentation>标签。

简单来说 Instrumentation ,它拥有强大的跟踪Application以及Activity生命周期的功能,而且一个应用有且仅有一个 Instrumentation,并且每个Activity都有一个 Instrumentation 的引用。ActivityThread 要创建或暂停某个Activity时,都需要通过 Instrumentation 来进行具体的操作。

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        // 自动屏蔽一些不重要的代码,易于查看重点内容!!
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
        }
        return null;
    }

我们又发现了 startActivity 这个方法,刚刚我们说到了, Instrumentation 拥有强大的跟踪Application以及Activity生命周期的功能,那么说吧,它就是完成对Application以及Activity初始化和管理生命周期的一个大管家,我就举几个例子给你看看,什么叫证据!

    public void callActivityOnStart(Activity activity) {
        activity.onStart();
    }
    
    public void callActivityOnRestart(Activity activity) {
        activity.onRestart();
    }
    
    public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
        activity.onResume();
        
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    am.match(activity, activity, activity.getIntent());
                }
            }
        }
    }

这些方法都是Acitivity生命周期的方法吧?所以说,Instrumentation 是非常重要的,但是,你会发现,这个 Instrumentation 非常重要,你在编程的时候有用到这个东西吗?并没有,可见它只是在背后默默地做出自己的奉献!

介绍完 Instrumentation ,我们再回到 execStartActivity 这个方法,出现了一个新面孔 ActivityManagerNative ,直接来看下它的代码实现吧。

public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
    /**
     * 将绑定对象插入活动管理器接口,如果需要生成代理。
     */
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);  //  <------获取到了一个ActivityManagerProxy对象,而且传递了一个IBinder进去,这个IBidder就是用来进行IPC通信的。
    }

    /**
     * 检索系统的默认/全局活动管理器。
     */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }
    
    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

从上面的代码我们可以了解到 ActivityManagerNative 是 Binder的一个子类,并且实现了IActivityManager 这个接口,得到 ActivityManagerProxy 这个对象,正如上面我们所说的:

下发了命令之后通过 IActivityManager 的修饰封装, startActivity 这个命令,通过 ActivityManagerProxy 把命令传递到执行者 ActivityManagerService 手中,再进行相应的处理。

我们已经到达了命令封装,然后叫给传达者 ActivityManagerProxy 这一步,接下来就是看传达者 ActivityManagerProxy 如何将命令交给执行者 ActivityManagerService 了。

我们走近一步,来瞧一瞧 ActivityManagerProxy 中的startActivity()方法是如何编写的。

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

相信你也可以看到,通过传到 ActivityManagerProxy 的IBinder,利用这个对象,调用了transact()方法,把所有参数封装成了Parcel对象,这样就向 ActivityManagerService 发射信号弹,进行通信了,执行者 ActivityManagerService 收到指令后立马进行了处理。

我深吸一口气,什么 ProxyActivityManagerService 等等一直在我脑海里回旋,突然想起了一个设计模式好像就是这样的啊,就是那个叫代理模式的。

代理:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

对,就是这样的,看来今天的知识要好好消化一下了。

“等等,先别想其他的,你以为到这一步就结束了吗?刚刚才到执行者这边呢,还有一段路要走啊,我们接着看吧”,还有啊...我当时的内心是崩溃的,还以为就这样结束了。

ActivityManagerService 接收到指令之后,正式开始启动Activity,这时,ActivityManagerService 要作出严格的审核过程。只见它把指令转换为了 ResolveInfo

ResolveInfo:从一个intent对应的IntentFilter所解析获取的信息。这部分地对应于从AndroidManifest .xml的<intent>标签。

然后 ActivityManagerService 调用 startActivityLocked() 方法进行下一步,再来到startSpecificActivityLocked() 方法,这时候就需要判断是否需要创建一个全新的进程,如果需要则调用startProcessLocked() 方法进行创建,而在这个方法的内部有一个Process.start的方法,这个时候正式到了我们的 ActivityThread 的工作,还记得在介绍 Zygote 时,它会打开一个Socket来监听需要创建 Process 的请求吗? Zygote 会不断取出建立的连接,再进行 fork 进程,这就是我们的应用进程了,而且返回了每个进程所对应的 process id,这时就回到了 ActivityThread ,众所周知的UI线程,进行处理页面的显示,它将和 ActivityManagerService 配合,一起完成接下来的工作,比如activity的管理。到此,整个应用启动完毕,所有的工作就告一段落了。

赠一个高清大图!

图一

虎穴,关闭!

“好了,本章的学习就要结束了,回去好好回味一下吧,老规矩,我还是要提一个问题给你,不过这个问题你可以回去好好想想的,不用着急回答我”。

既然,我们从底层去学习了整个app的启动过程,那对于上一章所讲到的优化启动时间有什么作用呢?

我带着整个问题回到了现实,只是,这一次回来之后感觉头脑一片模糊,知识量对于我这种新手来说确实是大了些,只能在以后的编程路上不断去琢磨了。

为了进步,只能不断地学习和总结!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容