欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

分析Android中应用的启动流程

程序员文章站 2024-03-07 20:10:39
前言 在我们开始之前,希望您能最好已经满足以下条件:      1、有一份编译后的android源码(亲自动手实践才会有更深入的...

前言

在我们开始之前,希望您能最好已经满足以下条件:

     1、有一份编译后的android源码(亲自动手实践才会有更深入的理解)

     2、对binder机制有一定的了解

本文启动流程分析基于android 5.1的源码。为什么是5.1的源码呢?因为手边编译完的代码只有这个版本…另外,用什么版本的源码并不重要,大体的流程并无本质上的区别,仅仅是实现细节的调整,找一个你熟悉的版本就好。

1、启动时序图

作为一个轻微强迫症的人,整理的时序图,相信大家按图索骥,一定能搞明白整个启动流程:

分析Android中应用的启动流程

说明:为了让大家更清楚的理解整个过程,将时序图中划分为三个部分:launcher进程system进程app进程,其中有涉及共用的类以l / a进行区分表示跟哪个进程有关,便于理解。

2、关键类说明

整个启动流程因为会涉及到多次binder通信,这里先简要说明一下几个类的用途,方便大家理解整个交互流程:

      1、activitymanagerservice:ams是android中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,因此它在android中非常重要,它本身也是一个binder的实现类。

      2、instrumentation:顾名思义,它用来监控应用程序和系统的交互。

      3、activitythread:应用的入口类,系统通过调用main函数,开启消息循环队列。activitythread所在线程被称为应用的主线程(ui线程)。

      4、applicationthread:applicationthread提供binder通讯接口,ams则通过代理调用此app进程的本地方法。

      5、activitymanagerproxy:ams服务在当前进程的代理类,负责与ams通信。

      6、applicationthreadproxy:applicationthread在ams服务中的代理类,负责与applicationthread通信。

3、流程分析

首先交代下整个流程分析的场景:用户点击launcher上的应用图标到该应用主界面启动展示在用户眼前

这整个过程涉及到跨进程通信,所以我们将其划分为时序图中所展示三个进程:launcher进程system进程app进程。为了不贴过长的代码又能说清楚进程间交互的流程,这里简述几个重要的交互点。

从时序图上大家也可以看到调用链相当长,对应的代码量也比较大,而且时序图只是分析了这个一个场景下的流程。道阻且长,行则将至!

3.1 launcher响应用户点击,通知ams

launcher做为应用的入口,还是有必要交代一下的,我们来看看launcher的代码片段,launcher使用的是packages/apps/launcher3的的源码。

public class launcher extends activity
  implements view.onclicklistener, onlongclicklistener, launchermodel.callbacks,
     view.ontouchlistener, pageswitchlistener, launcherproviderchangelistener {
 ...
 /**
  * launches the intent referred by the clicked shortcut.
  *
  * @param v the view representing the clicked shortcut.
  */
 public void onclick(view v) {
  // make sure that rogue clicks don't get through while allapps is launching, or after the
  // view has detached (it's possible for this to happen if the view is removed mid touch).
  if (v.getwindowtoken() == null) {
   return;
  }

  ...

  object tag = v.gettag();
  if (tag instanceof shortcutinfo) {
   onclickappshortcut(v);
  } else if (tag instanceof folderinfo) {
   ...
  } else if (v == mallappsbutton) {
   onclickallappsbutton(v);
  } else if (tag instanceof appinfo) {
   startappshortcutorinfoactivity(v);
  } else if (tag instanceof launcherappwidgetinfo) {
   ...
  }
 }
 
 private void startappshortcutorinfoactivity(view v) {
  ...
  boolean success = startactivitysafely(v, intent, tag);
  ...
 }
 
 boolean startactivitysafely(view v, intent intent, object tag) {
  ...
  try {
   success = startactivity(v, intent, tag);
  } catch (activitynotfoundexception e) {
   ...
  }
  return success;
 }
 
 boolean startactivity(view v, intent intent, object tag) {
  intent.addflags(intent.flag_activity_new_task);
  try {
   ...

   if (user == null || user.equals(userhandlecompat.myuserhandle())) {
    // could be launching some bookkeeping activity
    startactivity(intent, optsbundle);
   } else {
    ...
   }
   return true;
  } catch (securityexception e) {
   ...
  }
  return false;
 }
}

通过staracticity辗转调用到activity:startactivityforresult而后则调用至instrumentation:execstartactivity,代码片段如下:

public class instrumentation {
 ...
 public activityresult execstartactivity(
   context who, ibinder contextthread, ibinder token, activity target,
   intent intent, int requestcode, bundle options) {
  iapplicationthread whothread = (iapplicationthread) contextthread;
  ...
  try {
   ...
   int result = activitymanagernative.getdefault()
    .startactivity(whothread, who.getbasepackagename(), intent,
      intent.resolvetypeifneeded(who.getcontentresolver()),
      token, target != null ? target.membeddedid : null,
      requestcode, 0, null, options);
   ...
  } catch (remoteexception e) {
  }
  return null;
 }
 ...
}

这里的activitymanagernative.getdefault返回activitymanagerservice的远程接口,即activitymanagerproxy接口,有人可能会问了为什么会是activitymanagerproxy,这就涉及到binder通信了,这里不再展开。通过binder驱动程序,activitymanagerproxy与ams服务通信,则实现了跨进程到system进程。

3.2 ams响应launcher进程请求

从上面的流程我们知道,此时ams应该处理launcher进程发来的请求,请参看时序图及源码,此时我们来看activitystacksupervisor:startactivityuncheckedlocked方法,目测这个方法已经超过600行代码,来看一些关键代码片段:

public final class activitystacksupervisor implements displaylistener {
 ...
 final int startactivityuncheckedlocked(activityrecord r, activityrecord sourcerecord,
   ivoiceinteractionsession voicesession, ivoiceinteractor voiceinteractor, 
 int startflags, boolean doresume, bundle options, taskrecord intask) {
 final intent intent = r.intent;
 final int callinguid = r.launchedfromuid;
 ...
 final boolean launchsingletop = r.launchmode == activityinfo.launch_single_top;
 final boolean launchsingleinstance = r.launchmode == activityinfo.launch_single_instance;
 final boolean launchsingletask = r.launchmode == activityinfo.launch_single_task; 

 int launchflags = intent.getflags();
 ...
 // we'll invoke onuserleaving before onpause only if the launching
 // activity did not explicitly state that this is an automated launch.
 muserleaving = (launchflags & intent.flag_activity_no_user_action) == 0;
 ...
   
   activityrecord nottop =
    (launchflags & intent.flag_activity_previous_is_top) != 0 ? r : null;

 // if the onlyifneeded flag is set, then we can do this if the activity
 // being launched is the same as the one making the call... or, as
 // a special case, if we do not know the caller then we count the
 // current top activity as the caller.
 if ((startflags&activitymanager.start_flag_only_if_needed) != 0) {
 ...
 }
   ...
   // if the caller is not coming from another activity, but has given us an
 // explicit task into which they would like us to launch the new activity,
 // then let's see about doing that.
 if (sourcerecord == null && intask != null && intask.stack != null) {
 final intent baseintent = intask.getbaseintent();
 final activityrecord root = intask.getrootactivity();
 ...
 // if this task is empty, then we are adding the first activity -- it
 // determines the root, and must be launching as a new_task.
 if (launchsingleinstance || launchsingletask) {
 ...
 }
 ...
 }
   ...
   if (intask == null) {
 if (sourcerecord == null) {
 // this activity is not being started from another... in this
 // case we -always- start a new task.
 if ((launchflags & intent.flag_activity_new_task) == 0 && intask == null) {
  slog.w(tag, "startactivity called from non-activity context; forcing " +
       "intent.flag_activity_new_task for: " + intent);
  launchflags |= intent.flag_activity_new_task;
 }
 } else if (sourcerecord.launchmode == activityinfo.launch_single_instance) {
 // the original activity who is starting us is running as a single
 // instance... this new activity it is starting must go on its
 // own task.
 launchflags |= intent.flag_activity_new_task;
 } else if (launchsingleinstance || launchsingletask) {
 // the activity being started is a single instance... it always
 // gets launched into its own task.
 launchflags |= intent.flag_activity_new_task;
 }
 }
   
 ...
   
   // we may want to try to place the new activity in to an existing task. we always
 // do this if the target activity is singletask or singleinstance; we will also do
 // this if new_task has been requested, and there is not an additional qualifier telling
 // us to still place it in a new task: multi task, always doc mode, or being asked to
 // launch this as a new task behind the current one.
 if (((launchflags & intent.flag_activity_new_task) != 0 &&
    (launchflags & intent.flag_activity_multiple_task) == 0)
    || launchsingleinstance || launchsingletask) {
 // if bring to front is requested, and no result is requested and we have not
 // been given an explicit task to launch in to, and
 // we can find a task that was started with this same
 // component, then instead of launching bring that one to the front.
 if (intask == null && r.resultto == null) {
 // see if there is a task to bring to the front. if this is
 // a single_instance activity, there can be one and only one
 // instance of it in the history, and it is always in its own
 // unique task, so we do a special search.
 activityrecord intentactivity = !launchsingleinstance ?
  findtasklocked(r) : findactivitylocked(intent, r.info);
 if (intentactivity != null) {
  ...
 }
 }
 }
  
 ...
   
   if (r.packagename != null) {
   // if the activity being launched is the same as the one currently
   // at the top, then we need to check if it should only be launched
   // once.
   activitystack topstack = getfocusedstack();
   activityrecord top = topstack.toprunningnondelayedactivitylocked(nottop);
   if (top != null && r.resultto == null) {
    if (top.realactivity.equals(r.realactivity) && top.userid == r.userid) {
     ...
    }
   }
   } else{
   ...
   }
  
 boolean newtask = false;
 boolean keepcurtransition = false;

 taskrecord tasktoaffiliate = launchtaskbehind && sourcerecord != null ?
    sourcerecord.task : null;

 // should this be considered a new task?
 if (r.resultto == null && intask == null && !addingtotask
    && (launchflags & intent.flag_activity_new_task) != 0) {
 ...
 if (reusetask == null) {
    r.settask(targetstack.createtaskrecord(getnexttaskid(),
      newtaskinfo != null ? newtaskinfo : r.info,
      newtaskintent != null ? newtaskintent : intent,
      voicesession, voiceinteractor, !launchtaskbehind /* totop */),
      tasktoaffiliate);
    ...
   } else {
    r.settask(reusetask, tasktoaffiliate);
   }
    ...
 } else if (sourcerecord != null) {
   
 } else if (!addingtotask &&
     (launchflags&intent.flag_activity_reorder_to_front) != 0) {
      
 } else if (intask != null){
   
 } else {
   
 }
  
 ...
  
 targetstack.startactivitylocked(r, newtask, doresume, keepcurtransition, options);
   
 ...
   return activitymanager.start_success;
 }
 ...
}

函数经过intent的标志值设置,通过findtasklocked函数来查找存不存这样的task,这里返回的结果是null,即intentactivity为null,因此,需要创建一个新的task来启动这个activity。现在处理堆栈顶端的activitylauncher,与我们即将要启动的mainactivity不是同一个activity,创建了一个新的task里面来启动这个activity

经过栈顶检测,则需要将launcher推入paused状态,才可以启动新的activity。后续则调用至activitystack:startpausinglocked,我们来看一下这个函数:

final class activitystack {
 ...
 final boolean startpausinglocked(boolean userleaving, boolean uisleeping, boolean resuming,
   boolean dontwait) {
 if (mpausingactivity != null) {
  ...
 } 
 activityrecord prev = mresumedactivity;
 if (prev == null) {
  ...
 }
 ...
 mresumedactivity = null;
 mpausingactivity = prev;
 mlastpausedactivity = prev;
 mlastnohistoryactivity = (prev.intent.getflags() & intent.flag_activity_no_history) != 0
    || (prev.info.flags & activityinfo.flag_no_history) != 0 ? prev : null;
 prev.state = activitystate.pausing;
 ...
 
 if (prev.app != null && prev.app.thread != null) {
  try {
 ...
 prev.app.thread.schedulepauseactivity(prev.apptoken, prev.finishing,
      userleaving, prev.configchangeflags, dontwait);
  } catch (exception e) {
 ...
  }
 } else {
  ...
 }
 ...
 }
 ...
}

这里的prev.app.thread是一个applicationthread对象的远程接口,通过调用这个远程接口的schedulepauseactivity来通知launcher进入paused状态。至此,ams对launcher的请求已经响应,这是我们发现又通过binder通信回调至launcher进程。

3.3 launcher进程挂起launcher,再次通知ams

这个流程相对会简单一些,我们来看activitythread:

public final class activitythread {
 ...
 private void handlepauseactivity(ibinder token, boolean finished,
   boolean userleaving, int configchanges, boolean dontreport) {
  activityclientrecord r = mactivities.get(token);
  if (r != null) {
   ...
   performpauseactivity(token, finished, r.isprehoneycomb());

   // make sure any pending writes are now committed.
   if (r.isprehoneycomb()) {
    queuedwork.waittofinish();
   }

   // tell the activity manager we have paused.
   if (!dontreport) {
    try {
     activitymanagernative.getdefault().activitypaused(token);
    } catch (remoteexception ex) {
    }
   }
   ...
  }
 }
 ...
}

这部分launcher的activitythread处理页面paused并且再次通过activitymanagerproxy通知ams。

3.4 ams创建新的进程

创建新进程的时候,ams会保存一个processrecord信息,如果应用程序中的androidmanifest.xml配置文件中,我们没有指定application标签的process属性,系统就会默认使用package的名称。每一个应用程序都有自己的uid,因此,这里uid + process的组合就可以为每一个应用程序创建一个processrecord

public final class activitymanagerservice extends activitymanagernative
  implements watchdog.monitor, batterystatsimpl.batterycallback {
 ...
 private final void startprocesslocked(processrecord app, string hostingtype, string hostingnamestr, string abioverride, string entrypoint, string[] entrypointargs) {
 ...
 try {
  ...
  // start the process. it will either succeed and return a result containing
  // the pid of the new process, or else throw a runtimeexception.
  boolean isactivityprocess = (entrypoint == null);
  if (entrypoint == null) entrypoint = "android.app.activitythread";
  process.processstartresult startresult = process.start(entrypoint,
     app.processname, uid, uid, gids, debugflags, mountexternal,
     app.info.targetsdkversion, app.info.seinfo, requiredabi, instructionset,
     app.info.datadir, entrypointargs);
  ...
 } catch () {
  ...
 }
 }
 ...
}

这里主要是调用process:start接口来创建一个新的进程,新的进程会导入android.app.activitythread类,并且执行它的main函数,这就是每一个应用程序都有一个activitythread实例来对应的原因。

3.5 应用进程初始化

我们来看activitymain函数,这里绑定了主线程的looper,并进入消息循环,大家应该知道,整个android系统是消息驱动的,这也是为什么主线程默认绑定looper的原因:

public final class activitythread {
 ...
 public static void main(string[] args) {
  ...
  looper.preparemainlooper();

  activitythread thread = new activitythread();
  thread.attach(false);

  ...

  looper.loop();
  
  ...
 }
 
 private void attach(boolean system) {
  ...
  if (!system) {
   ...
   final iactivitymanager mgr = activitymanagernative.getdefault();
   try {
    mgr.attachapplication(mappthread);
   } catch (remoteexception ex) {
    // ignore
   }
  } else {
   ...
  }
  ...
 }
 ...
}

attach函数最终调用了activitymanagerservice的远程接口activitymanagerproxy的attachapplication函数,传入的参数是mappthread,这是一个applicationthread类型的binder对象,它的作用是ams与应用进程进行进程间通信的。

3.6 在ams中注册应用进程,启动启动栈顶页面

前面我们提到了ams负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,通过上一个流程我们知道应用进程创建后通过binder驱动与ams产生交互,此时ams则将应用进程创建后的信息进行了一次注册,如果拿windows系统程序注册到的注册表来理解这个过程,可能会更形象一些。

mmainstack.toprunningactivitylocked(null)从堆栈顶端取出要启动的activity,并在realstartactivitylockedhan函数中通过applicationthreadproxy调回app进程启动页面。

public final class activitystacksupervisor implements displaylistener {
 ...
 final boolean realstartactivitylocked(activityrecord r,
   processrecord app, boolean andresume, boolean checkconfig)
   throws remoteexception {
  ...
  r.app = app;
  ...
   
  try {
  ...
  app.thread.schedulelaunchactivity(new intent(r.intent), r.apptoken,
     system.identityhashcode(r), r.info, new configuration(mservice.mconfiguration),
     r.compat, r.launchedfrompackage, r.task.voiceinteractor, app.repprocstate,
     r.icicle, r.persistentstate, results, newintents, !andresume,
     mservice.isnexttransitionforward(), profilerinfo);
  ...
  } catch (remoteexception e) {
  ...
  }
  ...
 }
 ...
}

此时在app进程,我们可以看到,经过一些列的调用链最终调用至mainactivity:oncreate函数,之后会调用至onresume,而后会通知ams该mainactivity已经处于resume状态。至此,整个启动流程告一段落。

4、总结

通过上述流程,相信大家可以有了一个基本的认知,这里我们忽略细节简化流程,单纯从进程角度来看下图: launch_app_sim

分析Android中应用的启动流程

图上所画这里就不在赘述,activity启动后至resume状态,此时可交互。以上就是分析android中应用启动流程的全部内容了,如何有疑问欢迎大家指正交流。