Android Application启动流程分析

转载 2018年04月16日 10:54:26

译者注:
原文分成两个部分, 链接如下:
http://multi-core-dump.blogspot.com/2010/04/android-application-launch.html
http://multi-core-dump.blogspot.com/2010/04/android-application-launch-part-2.html
本文合二为一, 章节标题由译者加注.

作者曾经在高通的Android性能组工作, 主要工作是优化Android Application的启动时间.

1, App基础理论

要想优化App启动时间, 第一步就是了解App启动进程的工作原理. 有几个基础理论:

Android Application与其他移动平台有两个重大不同点:

  1. 每个Android App都在一个独立空间里, 意味着其运行在一个单独的进程中, 拥有自己的VM, 被系统分配一个唯一的user ID.
  2. Android App由很多不同组件组成, 这些组件还可以启动其他App的组件. 因此, Android App并没有一个类似程序入口的main()方法.

Android Application组件包括:

  • Activities: 前台界面, 直接面向User, 提供UI和操作.
  • Services: 后台任务.
  • Broadcast Receivers: 广播接收者.
  • Contexnt Providers: 数据提供者.

Android进程与Linux进程一样. 默认情况下, 每个apk运行在自己的Linux进程中. 另外, 默认一个进程里面只有一个线程---主线程. 这个主线程中有一个Looper实例, 通过调用Looper.loop()从Message队列里面取出Message来做相应的处理.

那么, 这个进程何时启动的呢?
简单的说, 进程在其需要的时候被启动. 任意时候, 当用户或者其他组件调取你的apk中的任意组件时, 如果你的apk没有运行, 系统会为其创建一个新的进程并启动. 通常, 这个进程会持续运行直到被系统杀死. 关键是: 进程是在被需要的时候才创建的.

举个例子, 如果你点击email中的超链接, 会在浏览器里面打开一个网页. Email App和浏览器App是两个不同的App, 运行在不同的进程中. 这次点击事件促使Android系统去创建了一个新的进程来实例化浏览器的组件.

首先, 让我们快速看下Android启动流程. 与众多基于Linux内核的系统类似, 启动系统时, bootloader启动内核和init进程. init进程分裂出更多名为"daemons(守护进程)"的底层的Linux进程, 诸如android debug deamon, USB deamon等. 这些守护进程处理底层硬件相关的接口.

随后, init进程会启动一个非常有意思的进程---"Zygote". 顾名思义, 这是一个Android平台的非常基础的进程. 这个进程初始化了第一个VM, 并且预加载了framework和众多App所需要的通用资源. 然后它开启一个Socket接口来监听请求, 根据请求孵化出新的VM来管理新的App进程. 一旦收到新的请求, Zygote会基于自身预先加载的VM来孵化出一个新的VM创建一个新的进程.

启动Zygote之后, init进程会启动runtime进程. Zygote会孵化出一个超级管理进程---System Server. SystemServer会启动所有系统核心服务, 例如Activity Manager Service, 硬件相关的Service等. 到此, 系统准备好启动它的第一个App进程---Home进程了.

2, 启动App流程

用户点击Home上的一个App图标, 启动一个应用时:


app launch

Click事件会调用startActivity(Intent), 会通过Binder IPC机制, 最终调用到ActivityManagerService. 该Service会执行如下操作:

  • 第一步通过PackageManager的resolveIntent()收集这个intent对象的指向信息.
  • 指向信息被存储在一个intent对象中.
  • 下面重要的一步是通过grantUriPermissionLocked()方法来验证用户是否有足够的权限去调用该intent对象指向的Activity.
  • 如果有权限, ActivityManagerService会检查并在新的task中启动目标activity.
  • 现在, 是时候检查这个进程的ProcessRecord是否存在了.

如果ProcessRecord是null, ActivityManagerService会创建新的进程来实例化目标activity.

以桌面启动一个应用Activity为例,onClick事件后,会调用startActivityForResult(Intent, int)

public void startActivityForResult(Intent intent, int requestCode) {
if (mParent == null) {
//Activity启动执行交由Instrumentation对象去处理
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode);
//mMainThread 在attach方法中被设置,当ActivityThread PerformLauchActivity,调用attach把ActivityThread.this传送过来
//mMainThread.getApplicationThread()它是一个进程通信服务端存根对象,提供了很多操作ActivityThread的方法,它继承了ApplicationThreadNative 
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
Instrumentation.execStartActivity
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode) {
......
try {
//ActivityManagerNative.getDefault()实际返回的是一个ActivityManagerProxy对象
int result = ActivityManagerNative.getDefault()
.startActivity
(whoThread, intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
null, 0, token, target != null ? target.mEmbeddedID : null,
requestCode, false, false);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
}
return null;
}

ActivityManagerProxy是实现IActivityManager接口的一个进程通信代理对象,在该方法ActivityManagerProxy.startActivity中,它只负责
准备相关的数据发送到system_process进程去处理startActivity:

public int startActivity(IApplicationThread caller, Intent intent,
String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
IBinder resultTo, String resultWho,
int requestCode, boolean onlyIfNeeded,
boolean debug) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeTypedArray(grantedUriPermissions, 0);
data.writeInt(grantedMode);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(onlyIfNeeded ? 1 : 0);
data.writeInt(debug ? 1 : 0);
//mRemote是一个BinderProxy对象,transact方法本地化实现
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}

到此前面3步都是在Laucher2进程中执行,调用mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);后,系统转到
system_process进程中执行,根据我前面讲的进程通信机制,入口函数是ActivityManagerNative.onTransact方法,在system_process进程中
ActivityManagerNative被继承,实际执行的是ActivityManagerService.onTransact方法,调用堆栈如下:

ActivityManagerService(ActivityManagerNative).onTransact(int, Parcel, Parcel, int) line: 129 
ActivityManagerService.onTransact(int, Parcel, Parcel, int) line: 1481 
ActivityManagerService(Binder).execTransact(int, int, int, int) line: 288 
NativeStart.run() line: not available [native method]

在ActivityManagerService(ActivityManagerNative).onTransact中根据前面的参数START_ACTIVITY_TRANSACTION,执行对应的case代码:

case START_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
Uri[] grantedUriPermissions = data.createTypedArray(Uri.CREATOR);
int grantedMode = data.readInt();
IBinder resultTo = data.readStrongBinder();
String resultWho = data.readString(); 
int requestCode = data.readInt();
boolean onlyIfNeeded = data.readInt() != 0;
boolean debug = data.readInt() != 0;
//执行对应Stub的IActivityManager接口方法
int result = startActivity(app, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug);
reply.writeNoException();
reply.writeInt(result);
return true;
}

前面红色的startActivity方法实际是执行的ActivityManagerService中的startActivity,


2.1 创建进程

ActivityManagerService调用startProcessLocked()方法来创建新的进程, 该方法会通过前面讲到的socket通道传递参数给Zygote进程. Zygote孵化自身, 并调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid.

ActivityThread随后依次调用Looper.prepareLoop()和Looper.loop()来开启消息循环.

流程图如下:


process creation

接下来的执行很复杂,要想搞清楚这些细节还需要一些时间,我我们跳过这些,继续沿着主干道前行:

ActivityManagerService.startProcessLocked(ProcessRecord, String, String) line: 2043 
ActivityManagerService.startProcessLocked(String, ApplicationInfo, boolean, int, String, ComponentName, boolean) line: 1982 
ActivityManagerService.startSpecificActivityLocked(HistoryRecord, boolean, boolean) line: 1908 
ActivityManagerService.resumeTopActivityLocked(HistoryRecord) line: 2855 
ActivityManagerService.completePauseLocked() line: 2237 
ActivityManagerService.activityPaused(IBinder, Bundle, boolean) line: 5963 
ActivityManagerService.activityPaused(IBinder, Bundle) line: 5941 
ActivityManagerService(ActivityManagerNative).onTransact(int, Parcel, Parcel, int) line: 387 
ActivityManagerService.onTransact(int, Parcel, Parcel, int) line: 1481 
ActivityManagerService(Binder).execTransact(int, int, int, int) line: 288 
SystemServer.init1(String[]) line: not available [native method] 
SystemServer.main(String[]) line: 582 
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method] 
Method.invoke(Object, Object...) line: 521 
ZygoteInit$MethodAndArgsCaller.run() line: 868 
ZygoteInit.main(String[]) line: 626 
NativeStart.main(String[]) line: not available [native method]

在用例进程onPause后,通过ActivityManagerProxy.activityPaused执行相关操作,这也是一个用例进程到system_process进程的远程调用,原来的用例进程需要进栈,并启动一个新的Activity在屏幕最前端,我们只关注新Activity的启动,首先关注新Activity应用进程的创建,看上面的调用堆栈,在函数startProcessLocked:

//app记录的是一个要启动ActivityThread进程的信息,hostingType=activity,hostingNameStr="com.iaiai.activity/.IaiaiActivity"
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
......
//Process.start是一个静态方法,它将启动一个新的进程,新进程的的入口main方法为android.app.ActivityThread.main
int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid,
gids, debugFlags, null); 
......


现在,我们终于看到了一个新的应用进程的创建,别急,在启动主Activity我们还有很多工作要做,我我们可以想象到得,一个新的应用肯定要建立闭环的消息循环,然后它要把的一个ApplicationThreadProxy代理对象传递给system_process进程,这样system_process进程就可以通过ApplicationThreadProxy代理对象来控制我们的应用进程了,比如它把广播消息转给应用进程,关闭应用进程等

先看新进程的main函数:

public static final void main(String[] args) {
SamplingProfilerIntegration.start();

Process.setArgV0("<pre-initialized>");
//建立looper消息循环队列
Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);
//开始主线程消息循环
Looper.loop();

if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}

thread.detach();
String name = (thread.mInitialApplication != null)
? thread.mInitialApplication.getPackageName()
: "<unknown>";
Slog.i(TAG, "Main thread of " + name + " is now exiting");
}

在main函数中建立了闭环的消息循环,这个是一般ui程序做法,很容易理解,但是后续应用的启动工作是如何进程的,关注我上面标注的红色代码,这里创建了一个ActivityThread对象,ActivityThread构造时初始化了该应用进程的一些基本成员,最重要的我们关注

final Looper mLooper = Looper.myLooper();
final H mH = new H();//消息处理handler
在这里,我们建立了消息处理器,它将负责处理main线程中Looper消息循环中的消息。

还用一个成员对象值得我们关注,那就是ApplicationThread对象,在ActivityThread对象被创建时,它也被构造了,我前面已经提到过了,它继承了ApplicationThreadNative类,熟悉进程通信代理机制的朋友就清楚了,ApplicationThread就是一个通信代理存根实现类,我们可以看它的实现方法,都是调用queueOrSendMessage方法,派发消息交给ActivityThread的mH去处理,那么我们很清楚了,ActivityThread代理存根对象,它负责执行来自远程的调用,这些远程的调用大部分来自system_process,所以,system_process很容易通过ApplicationThread的客户端代理对象控制ActivityThread,事实就是如此,后面我们可以很好地看到这一点,

继续看thread.attach(false)函数,参数标识是否系统进程,系统进程的入口函数是systemMain,而不是main方法,

private final void attach(boolean system) {//system==false
sThreadLocal.set(this);//ActivityThread对象关联到主线程
mSystemThread = system;
if (!system) {//是非系统进程
...
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
//把ApplicationThread mAppThread attach到系统进程system_process,以便system_process控制当前应用的ActivityThread
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
}
} else {
//系统进程要作的处理
...
}
//接收来自ViewRoot的ConfigurationChanged消息,派发给mH处理(H.CONFIGURATION_CHANGED),
//一旦配置发生变更,mH将执行H.CONFIGURATION_CHANGED
ViewRoot.addConfigCallback(new ComponentCallbacks() {
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mPackages) {
// We need to apply this change to the resources
// immediately, because upon returning the view
// hierarchy will be informed about it.
if (applyConfigurationToResourcesLocked(newConfig)) {
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(newConfig)) {
mPendingConfiguration = newConfig;

queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig);
}
}
}
}
public void onLowMemory() {
}
});
}


2.2 绑定Application

接下来要做的就是将进程和指定的Application绑定起来. 这个是通过上节的ActivityThread对象中调用bindApplication()方法完成的. 该方法发送一个BIND_APPLICATION的消息到消息队列中, 最终通过handleBindApplication()方法处理该消息. 然后调用makeApplication()方法来加载App的classes到内存中.

流程如下:


bind application

再来看一下attach方法的调用堆栈:

ActivityManagerProxy.attachApplication(IApplicationThread) line: 1542 
ActivityThread.attach(boolean) line: 4555 
ActivityThread.main(String[]) line: 4632 
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method] 
Method.invoke(Object, Object...) line: 521 
ZygoteInit$MethodAndArgsCaller.run() line: 868 
ZygoteInit.main(String[]) line: 626 
NativeStart.main(String[]) line: not available [native method]

这里你又会看到一个熟悉的身影,ActivityManagerProxy,是的,这里又使用了进程通信,通知ActivityManagerService执行attachApplication
看一下ActivityManagerProxy.attachApplication方法的代码:

public void attachApplication(IApplicationThread app) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
//参数IApplicationThread app通过进程通信传送到system_process进程,而app是一个ApplicationThread对象,不要被它的名称所迷惑
//这里它只是一个对象,它继承了ApplicationThreadNative,而ApplicationThreadNative是实现IApplicationThread接口的一个进程通信接口存根类,当它到达system_process,system_process得到的是它的一个代理类ActivityManagerProxy
data.writeStrongBinder(app.asBinder());
mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
再次回到了system_process进程,先看一下system接收到来自新的Activity的远程调用堆栈:

ActivityManagerService.attachApplicationLocked(IApplicationThread, int) line: 5591 
ActivityManagerService.attachApplication(IApplicationThread) line: 5677 
ActivityManagerService(ActivityManagerNative).onTransact(int, Parcel, Parcel, int) line: 363 
ActivityManagerService.onTransact(int, Parcel, Parcel, int) line: 1481 
ActivityManagerService(Binder).execTransact(int, int, int, int) line: 288 
NativeStart.run() line: not available [native method]

我们看attachApplicationLocked的实现,由于函数比较长,而且我也没有深入仔细看,所以我只列出我理解的关键部分代码

//thread来用户进程的ApplicationThread代理对象,pid是用户进程的pid
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
......
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
//当用户进程创建时有一个标识用户进程的pid,它关联了ProcessRecord记录,现在根据pid或者该记录
app = mPidsSelfLocked.get(pid);
}
} else if (mStartingProcesses.size() > 0) {
app = mStartingProcesses.remove(0);
app.setPid(pid);
} else {
app = null;
}
......
//设置app相关参数
app.thread = thread;//设置app的thread为用户进程代理对象ActivityManagerProxy
app.curAdj = app.setAdj = -100;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.forcingToForeground = null;
app.foregroundServices = false;
app.debugging = false;

......
thread.bindApplication(processName, app.instrumentationInfo != null
? app.instrumentationInfo : app.info, providers,
app.instrumentationClass, app.instrumentationProfileFile,
app.instrumentationArguments, app.instrumentationWatcher, testMode, 
isRestrictedBackupMode || !normalMode,
mConfiguration, getCommonServicesLocked());
updateLruProcessLocked(app, false, true);
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
......
HistoryRecord hr = topRunningActivityLocked(null);
if (hr != null && normalMode) {
if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
//realStartActivityLocked会调用thread.scheduleLaunchActivity
if (realStartActivityLocked(hr, app, true, true))
 {
didSomething = true;
}
}

这里通过远程调用后thread并不是一个ApplicationThread对象,而是其一个远程代理对象ApplicationThreadProxy,通过thread,可以操作ApplicationThread对象调用bindApplication和scheduleLaunchActivity:

先看bindApplication:

ActivityThread$ApplicationThread.bindApplication(String, ApplicationInfo, List, ComponentName, String, Bundle, IInstrumentationWatcher, int, boolean, Configuration, Map) line: 1655 
ActivityThread$ApplicationThread(ApplicationThreadNative).onTransact(int, Parcel, Parcel, int) line: 251 
ActivityThread$ApplicationThread(Binder).execTransact(int, int, int, int) line: 288 
NativeStart.run() line: not available [native method]

public final void bindApplication(String processName,
ApplicationInfo appInfo, List<ProviderInfo> providers,
ComponentName instrumentationName, String profileFile,
Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
int debugMode, boolean isRestrictedBackupMode, Configuration config,
Map<String, IBinder> services) {
//获取来自system_process远程调用传递过来的相关参数
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}

AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.profileFile = profileFile;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.debugMode = debugMode;
data.restrictedBackupMode = isRestrictedBackupMode;
data.config = config;
//派发给ActivityThread.mH去处理执行H.BIND_APPLICATION
queueOrSendMessage(H.BIND_APPLICATION, data);
}

我们看mH是如何处理的,mH接收到H.BIND_APPLICATION消息执行的对应是handleBindApplication函数,handleBindApplication函数中做了大量初始化ActivityThread的操作:

初始化mConfiguration

设置进程名

本地语言设置

设置包名称

设置应用程序根路径

设置应用程序data路径

设置activity的context


2.3 启动Activity

经过前两个步骤之后, 系统已经拥有了该application的进程. 后面的调用顺序就是普通的从一个已经存在的进程中启动一个新进程的activity了.

实际调用方法是realStartActivity(), 它会调用application线程对象中的sheduleLaunchActivity()发送一个LAUNCH_ACTIVITY消息到消息队列中, 通过 handleLaunchActivity()来处理该消息.

假设点击的是一个视频浏览的App, 其流程如下:


start activity

接着是第二步,scheduleLaunchActivity,先看调用堆栈:
ActivityThread$ApplicationThread.scheduleLaunchActivity(Intent, IBinder, int, ActivityInfo, Bundle, List, List, boolean, boolean) line: 1526 
ActivityThread$ApplicationThread(ApplicationThreadNative).onTransact(int, Parcel, Parcel, int) line: 130 
ActivityThread$ApplicationThread(Binder).execTransact(int, int, int, int) line: 288 
NativeStart.run() line: not available [native method]

同样,该函数操作也是交给ActivityThread.mH来处理,对应case消息为:H.LAUNCH_ACTIVITY

交由函数handleLaunchActivity处理,在这个函数中,根据ActivityRecord对像,获取要启动的Activity类信息,然后创建一个Activity,执行Activity的生命周期函数:onCreate,onResume,至此ActivitThread的handleBindApplication和handleLaunchActivity完成Activity的启动操作。

最后,我再总结一下从桌面启动一个应用Activity的启动过程,画了一个时序图,有助于理解,希望走过路过的朋友别忘了留个脚印,或者留个砖板,这个blog花费了我不少时间:

Android系统启动流程分析之启动应用

继上一篇Android系统启动流程分析之安装应用文章接着分析系统启动应用的过程. Android系统的启动流程简要分析里已经介绍了SystemServer在main方法里创建了一个线程ServerTh...
  • qq_31530015
  • qq_31530015
  • 2016-06-24 17:37:27
  • 1339

Android 7.0系统启动流程分析

简单的分析Android 7.0系统启动流程
  • dd864140130
  • dd864140130
  • 2017-02-27 00:31:06
  • 5368

Activity启动流程分析(基于android 5.1)

最近由于工作需要,需要深入了解AMS的内部实现。说老实话,虽然已经经过了几轮重构,AMS的代码还是又臭又长。。。 万事开头难,先找个入口开始看吧。当我们从Launcher界面点击启动一个app时,会启...
  • TurkeyCock
  • TurkeyCock
  • 2016-01-18 20:30:21
  • 3288

android 6.0 SystemUI源码分析(2)-SystemUI启动流程

1.SystemUI启动 SystemUI是核心系统应用,需要开机启动,启动SystemUI进程,是通过启动SystemUIService来实现的。 frameworks\base\servi...
  • zhudaozhuan
  • zhudaozhuan
  • 2016-03-07 10:38:33
  • 6902

andriod启动流程

  • 2015年12月13日 20:46
  • 77KB
  • 下载

Android应用启动流程分析

版权声明:本文图文为博主原创,未经博主允许不得转载。 大家有没有好奇过点击 Launcher 图标时,到唤起一个应用页面,这个流程会是怎么样的?本文的目的就是尽可能梳理清楚流程,能够让大家对整个流程...
  • bfboys
  • bfboys
  • 2016-09-17 19:13:49
  • 1063

Android系统启动流程(四)Launcher启动过程与系统启动流程

此前的文章我们学习了init进程、Zygote进程和SyetemServer进程的启动过程,这一篇文章我们就来学习Android系统启动流程的最后一步:Launcher的启动流程,并结合本系列的前三篇...
  • itachi85
  • itachi85
  • 2017-02-23 13:59:38
  • 8607

Cocos2d-x程序在Android下的启动过程

本文通过分析cocos2d-x自身提供的示例程序HelloLua来分析cocos2d-x程序在android平台下的具体启动过程。...
  • MaximusZhou
  • MaximusZhou
  • 2014-09-21 09:06:57
  • 5162

Android启动流程分析

  • 2012年05月02日 20:04
  • 1.1MB
  • 下载

Android系统分析之Activity的启动流程

1 参考链接 Android View系统分析之三Activity的启动与显示–需要进一步分析理解 Android Activity学习笔记——Activity的启动和创建 Android ad...
  • chenliguan
  • chenliguan
  • 2016-12-13 19:05:16
  • 509
收藏助手
不良信息举报
您举报文章:Android Application启动流程分析
举报原因:
原因补充:

(最多只允许输入30个字)