原来一个App是这样启动起来的,一看就懂

前言

当咱们点击手机屏幕上的软件图标时,就能够打开这个软件,看似很简单的进程其实包含了许多的底层交互,看了还不了解,欢迎来打我。

发动流程简介

首先要知道的是,手机屏幕其实便是一个Activity,咱们专业点将其称为Launcher,信任做过车载设备开发的朋友肯定不会陌生,Launcher是手机厂商提供的,不同的手机厂商比拼的便是Launcher的设计。当然咱们自己也能够去编写Launcher,运转在手机上运用自己的桌面风格,当然这儿咱们不去讲怎么去编写一个Launcher,假如你感兴趣欢迎重视我。

写过AndroidDemo的朋友肯定都知道,咱们必须在AndroidManifest配置文件中配置默许发动的Activity。

<intent-filter>
  <action android:name="android.intent.action.MAIN" />
​
  <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> 

其实便是告知Launcher该发动这个App的哪个页面。这样当体系发动的时分,PackageManger-Service就能够从配置文件中读取到该发动哪个Activity。

其次要知道的是,Launch和其他APP,运转在不同的进程中,所以他们之间的通讯是经过Binder去完成的,所以AMS是必不可少的。下面咱们以发动微信为例,看看发动流程是怎样的。

原来一个App是这样启动起来的,一看就懂

原来一个App是这样启动起来的,一看就懂

原来一个App是这样启动起来的,一看就懂

原来一个App是这样启动起来的,一看就懂

原来一个App是这样启动起来的,一看就懂

原来一个App是这样启动起来的,一看就懂

原来一个App是这样启动起来的,一看就懂

原来一个App是这样启动起来的,一看就懂

原来一个App是这样启动起来的,一看就懂

简单归纳发动微信的流程便是:

1.Launcher告知AMS 要发动微信了,而且告知AMS要发动的是哪个页面也便是主页是哪个页面 2.AMS收到音讯告知Launcher知道了,而且把要发动的页面记下来 3.Launcher进入Paused状态,告知AMS,你去找微信吧

上述便是Launcher和AMS的交互进程

4.AMS查看微信是否已经发动了也便是是否在后台运转,假如是在后台运转就直接发动,假如不是,AMS会在新的进程中创立一个ActivityThread目标,并发动其间的main函数。 5.微信发动后告知AMS,发动好了 6.AMS经过之前的记载找出微信的主页,告知微信应该发动哪个页面 7.微信依照AMS告知的页面去发动就发动成功了。

上述阶段是微信和AMS的交互进程。 那么接下来咱们剖析下具体进程

发动流程剖析

点击Launcher上的微信图标时,会调用startActivitySafely办法,intent中携带微信的要害信息也便是咱们在配置文件中配置的默许发动页信息,其实在微信安装的时分,Launcher已经将发动信息记载下来了,图标仅仅一个快捷方式的进口。

startActivitySafely的办法终究仍是会调用Activity的startActivity办法

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
  if (options != null) {
    startActivityForResult(intent, -1, options);
   } else {
    // Note we want to go through this call for compatibility with
    // applications that may have overridden the method.
    startActivityForResult(intent, -1);
   }
} 

而startActivity办法终究又会回到startActivityForResult办法,这儿startActivityForResult的办法中code为-1,表示startActivity并不关心是否发动成功。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) { 

startActivityForResult办法中又会调用mInstrumentation.execStartActivity办法,咱们看到其间有个参数是

mMainThread.getApplicationThread()

关于ActivityThread曾在 深入了解Android音讯机制文章中提到过,ActivityThread是在发动APP的时分创立的,ActivityThread代表使用程序,而咱们开发中常用的Application其实是ActivityThread的上下文,在开发中咱们常常运用,但在Android体系中其实地位很低的。

原来一个App是这样启动起来的,一看就懂

Android的main函数就在ActivityThread中

public static void main(String[] args) {
  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
  SamplingProfilerIntegration.start();
​
  // CloseGuard defaults to true and can be quite spammy.  We
  // disable it here, but selectively enable it later (via
  // StrictMode) on debug builds, but using DropBox, not logs.
  CloseGuard.setEnabled(false);
​
  Environment.initForCurrentUser(); 

再回到上面办法

mMainThread.getApplicationThread()

得到的是一个Binder目标,代表Launcher地点的App的进程,mToken实践也是一个Binder目标,代表Launcher地点的Activity经过Instrumentation传给AMS,这样AMS就知道是谁发起的请求。

mInstrumentation.execStartActivity

instrumentation在测试的时分常常用到,instrumentation的官方文档:

developer.android.com/intl/zh-cn/…

这儿不对instrumentation进行具体介绍了,咱们首要接着看mInstrumentation.execStartActivity办法

public Instrumentation.ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
  IApplicationThread whoThread = (IApplicationThread)contextThread;
  if(this.mActivityMonitors != null) {
    Object e = this.mSync;
    synchronized(this.mSync) {
      int N = this.mActivityMonitors.size();
​
      for(int i = 0; i < N; ++i) {
        Instrumentation.ActivityMonitor am = (Instrumentation.ActivityMonitor)this.mActivityMonitors.get(i);
        if(am.match(who, (Activity)null, intent)) {
          ++am.mHits;
          if(am.isBlocking()) {
            return requestCode >= 0?am.getResult():null;
           }
          break;
         }
       }
     }
   }
​
  try {
    intent.setAllowFds(false);
    intent.migrateExtraStreamToClipData();
    int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
    checkStartActivityResult(var16, intent);
   } catch (RemoteException var14) {
     ;
   }
​
  return null;
}

其实这个类是一个Binder通讯类,相当于IPowerManager.java便是完成了IPowerManager.aidl,咱们再来看看getDefault这个函数

public static IActivityManager getDefault() {
  return (IActivityManager)gDefault.get();
} 

getDefault办法得到一个IActivityManager,它是一个完成了IInterface的接口,里边界说了四大组件的生命周期

public static IActivityManager asInterface(IBinder obj) {
    if(obj == null) {
        return null;
    } else {
        IActivityManager in = (IActivityManager)obj.queryLocalInterface("android.app.IActivityManager");
        return (IActivityManager)(in != null?in:new ActivityManagerProxy(obj));
    }
} 

终究回来一个ActivityManagerProxy目标也便是AMP,AMP便是AMS的署理目标,提到署理其实便是署理模式,关于什么是署理模式以及动态署理和静态署理的运用能够继续重视我,后面会单独写篇文章进行介绍。

原来一个App是这样启动起来的,一看就懂

AMP的startActivity办法

public int startActivity(IApplicationThread caller, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, Bundle options) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken("android.app.IActivityManager");
    data.writeStrongBinder(caller != null?caller.asBinder():null);
    intent.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(resultTo);
    data.writeString(resultWho);
    data.writeInt(requestCode);
    data.writeInt(startFlags);
    data.writeString(profileFile);
    if(profileFd != null) {
        data.writeInt(1);
        profileFd.writeToParcel(data, 1);
    } else {
        data.writeInt(0);
    } 

首要便是将数据写入AMS进程,等待AMS的回来结果,这个进程是比较枯燥的,因为咱们做插件化的时分只能对客户端Hook,而不能对服务端操作,所以咱们只能静静的看着。(温馨提示:假如文章到这儿你已经有允许晕了,那就对了,研讨源码首要便是梳理整个流程,千万不要纠结源码细节,那样会无法自拔)。

AMS处理Launcher的信息

AMS告知Launcher我知道了,那么AMS怎么告知Launcher呢?

Binder的通讯是平等的,谁发音讯谁便是客户端,接纳的一方便是服务端,前面已经将Launcher地点的进程传过来了,AMS将其保存为一个ActivityRecord目标,这个目标中有一个ApplicationThreadProxy即Binder的署理目标,AMS通ApplicationTreadProxy发送音讯,App经过ApplicationThread来接纳这个音讯。

Launcher收到音讯后,再告知AMS,好的我知道了,那我走了,ApplicationThread调用ActivityThread的sendMessage办法给Launcher主线程发送一个音讯。这个时分AMS去发动一个新的进程,而且创立ActivityThread,指定main函数进口。

发动新进程的时分为进程创立了ActivityThread目标,这个便是UI线程,进入main函数后,创立一个Looper,也便是mainLooper,而且创立Application,所以说Application仅仅对开发人员来说重要罢了。创立好后告知AMS微信发动好了,AMS就记载了这个APP的登记信息,以后AMS经过这个ActivityThread向APP发送音讯。

这个时分AMS依据之前的记载告知微信应该发动哪个Activity,微信就能够发动了。

public void handleMessage(Message msg) {
    ActivityThread.ActivityClientRecord data;
    switch(msg.what) {
    case 100:
        Trace.traceBegin(64L, "activityStart");
        data = (ActivityThread.ActivityClientRecord)msg.obj;
        data.packageInfo = ActivityThread.this.getPackageInfoNoCheck(data.activityInfo.applicationInfo, data.compatInfo);
        ActivityThread.this.handleLaunchActivity(data, (Intent)null);
        Trace.traceEnd(64L); 
ActivityThread.ActivityClientRecord

便是AMS传过来的Activity

data.activityInfo.applicationInfo

总结

所得到的属性咱们称之为LoadedApk,能够提取到apk中的一切资源,那么APP内部是怎么页面跳转的呢,比方咱们从ActivityA跳转到ActivityB,咱们能够将Activity看作Launcher,唯一不同的便是,在正常情况下ActivityB和ActivityA地点同一进程,所以不会去创立新的进程。

APP的发动流程便是这样了。

更多Android进阶指南 能够具体Vx重视大众号:Android老皮 解锁 《Android十大板块文档》

1.Android车载使用开发体系学习指南(附项目实战)

2.Android Framework学习指南,助力成为体系级开发高手

3.2023最新Android中高档面试题汇总+解析,离别零offer

4.企业级Android音视频开发学习道路+项目实战(附源码)

5.Android Jetpack从入门到通晓,构建高质量UI界面

6.Flutter技术解析与实战,跨平台首要之选

7.Kotlin从入门到实战,全方面提升架构基础

8.高档Android插件化与组件化(含实战教程和源码)

9.Android 功能优化实战+360全方面功能调优

10.Android零基础入门到通晓,高手进阶之路

敲代码不易,重视一下吧。ღ( ・ᴗ・` )