深入理解Dalvik虚拟机- Android应用进程启动过程分析
Posted threepigs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Dalvik虚拟机- Android应用进程启动过程分析相关的知识,希望对你有一定的参考价值。
android的应用进程启动是apk在manifest里申明的Activity,Service,BroadcastReceiver等组件被调起时而触发的。我们以Activity为例,当点击桌面上的应用图标时,桌面会调用startActivity,启动manifest里申明的相应Launcher的Activity,而Activity的startActivity会通过Binder调用来到ActivityManagerService(AMS)里。AMS是system_server的一个服务,负责管理Android系统的所有Activity的栈,逻辑比较复杂,在这里就不详细分析,以后专门写AMS的专题。AMS里startActivity的时候,如果发现这个应用进程没有启动,那么就会通过Zygote创建出这个进程。
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java:
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr)
...
...
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, null);
...
...
显然是通过调用Process.start来启动进程,”android.app.ActivityThread” 这个参数是整个进程启动的入口类,后续的分析可以看到,进程被fork出来之后,就会调用android.app.ActivityThread的main函数。
frameworks/base/core/java/android/os/Process.java:
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String[] zygoteArgs)
try
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
catch (ZygoteStartFailedEx ex)
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String[] extraArgs)
throws ZygoteStartFailedEx
synchronized(Process.class)
ArrayList<String> argsForZygote = new ArrayList<String>();
// --runtime-init, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-init");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0)
argsForZygote.add("--enable-jni-logging");
if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0)
argsForZygote.add("--enable-safemode");
if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0)
argsForZygote.add("--enable-debugger");
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0)
argsForZygote.add("--enable-checkjni");
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0)
argsForZygote.add("--enable-assert");
if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER)
argsForZygote.add("--mount-external-multiuser");
else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL)
argsForZygote.add("--mount-external-multiuser-all");
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
//TODO optionally enable debuger
//argsForZygote.add("--enable-debugger");
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0)
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i++)
if (i != 0)
sb.append(',');
sb.append(gids[i]);
argsForZygote.add(sb.toString());
if (niceName != null)
argsForZygote.add("--nice-name=" + niceName);
if (seInfo != null)
argsForZygote.add("--seinfo=" + seInfo);
argsForZygote.add(processClass);
if (extraArgs != null)
for (String arg : extraArgs)
argsForZygote.add(arg);
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx
if (primaryZygoteState == null || primaryZygoteState.isClosed())
try
primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
catch (IOException ioe)
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
if (primaryZygoteState.matches(abi))
return primaryZygoteState;
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed())
try
secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
catch (IOException ioe)
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
if (secondaryZygoteState.matches(abi))
return secondaryZygoteState;
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
调用startViaZygote函数,从代码可以看到,Zygote创建进程是socket跨进程的调用。通过LocalSocket通信,来完成进程的创建,所以这里的Process.start只是一个Client端
的调用,实际是由Server端的接收到消息后处理的。
Server端是app_process这个进程里,这是个常驻的系统服务。
frameworks/base/cmds/app_process/app_main.cpp
class AppRuntime : public AndroidRuntime
AppRuntime继承自AndroidRuntime,AndroidRuntime类是libandroid_runtime.so里导出的,frameworks/base/core/jni/AndroidRuntime.cpp。
app_process这个进程在init.rc里会创建,
#init.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
app_process以zygote作为进程名,这个adb shell进入手机ps一下就可以看到zygote。
这个进程的main函数里有如下初始化代码片段:
AppRuntime runtime;
const char* argv0 = argv[0];
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// Everything up to '--' or first non '-' arg goes to the vm
int i = runtime.addVmArguments(argc, argv);
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
const char* parentDir = NULL;
const char* niceName = NULL;
const char* className = NULL;
while (i < argc)
const char* arg = argv[i++];
if (!parentDir)
parentDir = arg;
else if (strcmp(arg, "--zygote") == 0)
zygote = true;
niceName = "zygote";
else if (strcmp(arg, "--start-system-server") == 0)
startSystemServer = true;
else if (strcmp(arg, "--application") == 0)
application = true;
else if (strncmp(arg, "--nice-name=", 12) == 0)
niceName = arg + 12;
else
className = arg;
break;
if (niceName && *niceName)
setArgv0(argv0, niceName);
set_process_name(niceName);
runtime.mParentDir = parentDir;
if (zygote)
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server" : "");
else if (className)
// Remainder of args get passed to startup class main()
runtime.mClassName = className;
runtime.mArgC = argc - i;
runtime.mArgV = argv + i;
runtime.start("com.android.internal.os.RuntimeInit",
application ? "application" : "tool");
第一个if是正常的Zygote进程启动,执行AndroidRuntime::start,会调用ZygoteInit的类main函数。app_process的初始化就在ZygoteInit里,监听本地的socket,接收ZygoteClient的请求,当有请求来的时候,调用ZygoteConnection::runOnce,从而调用Zygote.forkAndSpecialize来创建新的进程,并且调用RuntimeInit.zygoteInit做进程的初始化,初始化过程就会调用ActivityThread.main
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
...
...
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL)
ALOGE("JavaVM unable to locate class '%s'\\n", slashClassName);
/* keep going */
else
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL)
ALOGE("JavaVM unable to find main() in '%s'\\n", className);
/* keep going */
else
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
...
第二个当指定了className的时候,则用com.android.internal.os.RuntimeInit作为入口,指定className的场景是用am命令创建的进程的时候:
exec app_process $base/bin com.android.commands.am.Am "$@"
而我们正常的Zygote进程则走的是ZygoteInit。
AndroidRuntime.start函数会调用startVm,frameworks/base/core/jni/AndroidRuntime.cpp
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
...
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;
/*
* Initialize the VM.
*
* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0)
ALOGE("JNI_CreateJavaVM failed\\n");
goto bail;
startVm会初始化JavaVm和JNIEnv,最终是通过JNI_CreateJavaVM实现的,JNI_CreateJavaVM是个接口,不同的虚拟机有不同的实现。
Dalvik虚拟机相关的代码主要在下面几个目录下:
libcore/libdvm
libcore/dalvik
dalvik/vm/
Dalvik的JNI_CreateJavaVM在dalvik/vm/Jni.cpp 里实现:
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
if (dvmIsBadJniVersion(args->version))
ALOGE("Bad JNI version passed to CreateJavaVM: %d", args->version);
return JNI_EVERSION;
// TODO: don't allow creation of multiple VMs -- one per customer for now
/* zero globals; not strictly necessary the first time a VM is started */
memset(&gDvm, 0, sizeof(gDvm));
/*
* Set up structures for JNIEnv and VM.
*/
JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
pVM->funcTable = &gInvokeInterface;
pVM->envList = NULL;
dvmInitMutex(&pVM->envListLock);
UniquePtr<const char*[]> argv(new const char*[args->nOptions]);
memset(argv.get(), 0, sizeof(char*) * (args->nOptions));
/*
* Convert JNI args to argv.
*
* We have to pull out vfprintf/exit/abort, because they use the
* "extraInfo" field to pass function pointer "hooks" in. We also
* look for the -Xcheck:jni stuff here.
*/
int argc = 0;
for (int i = 0; i < args->nOptions; i++)
const char* optStr = args->options[i].optionString;
if (optStr == NULL)
dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\\n", i);
return JNI_ERR;
else if (strcmp(optStr, "vfprintf") == 0)
gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;
else if (strcmp(optStr, "exit") == 0)
gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;
else if (strcmp(optStr, "abort") == 0)
gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;
else if (strcmp(optStr, "sensitiveThread") == 0)
gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;
else if (strcmp(optStr, "-Xcheck:jni") == 0)
gDvmJni.useCheckJni = true;
else if (strncmp(optStr, "-Xjniopts:", 10) == 0)
char* jniOpts = strdup(optStr + 10);
size_t jniOptCount = 1;
for (char* p = jniOpts; *p != 0; ++p)
if (*p == ',')
++jniOptCount;
*p = 0;
char* jniOpt = jniOpts;
for (size_t i = 0; i < jniOptCount; ++i)
if (strcmp(jniOpt, "warnonly") == 0)
gDvmJni.warnOnly = true;
else if (strcmp(jniOpt, "forcecopy") == 0)
gDvmJni.forceCopy = true;
else if (strcmp(jniOpt, "logThirdPartyJni") == 0)
gDvmJni.logThirdPartyJni = true;
else
dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\\n",
jniOpt);
free(pVM);
free(jniOpts);
return JNI_ERR;
jniOpt += strlen(jniOpt) + 1;
free(jniOpts);
else
/* regular option */
argv[argc++] = optStr;
if (gDvmJni.useCheckJni)
dvmUseCheckedJniVm(pVM);
if (gDvmJni.jniVm != NULL)
dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\\n");
free(pVM);
return JNI_ERR;
gDvmJni.jniVm = (JavaVM*) pVM;
/*
* Create a JNIEnv for the main thread. We need to have something set up
* here because some of the class initialization we do when starting
* up the VM will call into native code.
*/
JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
/* Initialize VM. */
gDvm.initializing = true;
std::string status =
dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
gDvm.initializing = false;
if (!status.empty())
free(pEnv);
free(pVM);
ALOGW("CreateJavaVM failed: %s", status.c_str());
return JNI_ERR;
/*
* Success! Return stuff to caller.
*/
dvmChangeStatus(NULL, THREAD_NATIVE);
*p_env = (JNIEnv*) pEnv;
*p_vm = (JavaVM*) pVM;
ALOGV("CreateJavaVM succeeded");
return JNI_OK;
malloc完了之后,会调用dvmStartup完成虚拟机的启动,dvmStartup定义在dalvik/vm/Init.cpp里:
/*
* VM initialization. Pass in any options provided on the command line.
* Do not pass in the class name or the options for the class.
*
* Returns 0 on success.
*/
std::string dvmStartup(int argc, const char* const argv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
ScopedShutdown scopedShutdown;
assert(gDvm.initializing);
ALOGV("VM init args (%d):", argc);
for (int i = 0; i < argc; i++)
ALOGV(" %d: '%s'", i, argv[i]);
setCommandLineDefaults();
/*
* Process the option flags (if any).
*/
int cc = processOptions(argc, argv, ignoreUnrecognized);
if (cc != 0)
if (cc < 0)
dvmFprintf(stderr, "\\n");
usage("dalvikvm");
return "syntax error";
#if WITH_EXTRA_GC_CHECKS > 1
/* only "portable" interp has the extra goodies */
if (gDvm.executionMode != kExecutionModeInterpPortable)
ALOGI("Switching to 'portable' interpreter for GC checks");
gDvm.executionMode = kExecutionModeInterpPortable;
#endif
/* Configure group scheduling capabilities */
if (!access("/dev/cpuctl/tasks", F_OK))
ALOGV("Using kernel group scheduling");
gDvm.kernelGroupScheduling = 1;
else
ALOGV("Using kernel scheduler policies");
/* configure signal handling */
if (!gDvm.reduceSignals)
blockSignals();
/* verify system page size */
if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE)
return StringPrintf("expected page size %d, got %d",
SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
/* mterp setup */
ALOGV("Using executionMode %d", gDvm.executionMode);
dvmCheckAsmConstants();
/*
* Initialize components.
*/
dvmQuasiAtomicsStartup();
if (!dvmAllocTrackerStartup())
return "dvmAllocTrackerStartup failed";
if (!dvmGcStartup())
return "dvmGcStartup failed";
if (!dvmThreadStartup())
return "dvmThreadStartup failed";
if (!dvmInlineNativeStartup())
return "dvmInlineNativeStartup";
if (!dvmRegisterMapStartup())
return "dvmRegisterMapStartup failed";
if (!dvmInstanceofStartup())
return "dvmInstanceofStartup failed";
if (!dvmClassStartup())
return "dvmClassStartup failed";
/*
* At this point, the system is guaranteed to be sufficiently
* initialized that we can look up classes and class members. This
* call populates the gDvm instance with all the class and member
* references that the VM wants to use directly.
*/
if (!dvmFindRequiredClassesAndMembers())
return "dvmFindRequiredClassesAndMembers failed";
if (!dvmStringInternStartup())
return "dvmStringInternStartup failed";
if (!dvmNativeStartup())
return "dvmNativeStartup failed";
if (!dvmInternalNativeStartup())
return "dvmInternalNativeStartup failed";
if (!dvmJniStartup())
return "dvmJniStartup failed";
if (!dvmProfilingStartup())
return "dvmProfilingStartup failed";
/*
* Create a table of methods for which we will substitute an "inline"
* version for performance.
*/
if (!dvmCreateInlineSubsTable())
return "dvmCreateInlineSubsTable failed";
/*
* Miscellaneous class library validation.
*/
if (!dvmValidateBoxClasses())
return "dvmValidateBoxClasses failed";
/*
* Do the last bits of Thread struct initialization we need to allow
* JNI calls to work.
*/
if (!dvmPrepMainForJni(pEnv))
return "dvmPrepMainForJni failed";
/*
* Explicitly initialize java.lang.Class. This doesn't happen
* automatically because it's allocated specially (it's an instance
* of itself). Must happen before registration of system natives,
* which make some calls that throw assertions if the classes they
* operate on aren't initialized.
*/
if (!dvmInitClass(gDvm.classJavaLangClass))
return "couldn't initialized java.lang.Class";
/*
* Register the system native methods, which are registered through JNI.
*/
if (!registerSystemNatives(pEnv))
return "couldn't register system natives";
/*
* Do some "late" initialization for the memory allocator. This may
* allocate storage and initialize classes.
*/
if (!dvmCreateStockExceptions())
return "dvmCreateStockExceptions failed";
/*
* At this point, the VM is in a pretty good state. Finish prep on
* the main thread (specifically, create a java.lang.Thread object to go
* along with our Thread struct). Note we will probably be executing
* some interpreted class initializer code in here.
*/
if (!dvmPrepMainThread())
return "dvmPrepMainThread failed";
/*
* Make sure we haven't accumulated any tracked references. The main
* thread should be starting with a clean slate.
*/
if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)
ALOGW("Warning: tracked references remain post-initialization");
dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
/* general debugging setup */
if (!dvmDebuggerStartup())
return "dvmDebuggerStartup failed";
if (!dvmGcStartupClasses())
return "dvmGcStartupClasses failed";
/*
* Init for either zygote mode or non-zygote mode. The key difference
* is that we don't start any additional threads in Zygote mode.
*/
if (gDvm.zygote)
if (!initZygote())
return "initZygote failed";
dvmPostInitZygote();
else
if (!dvmInitAfterZygote())
return "dvmInitAfterZygote failed";
#ifndef NDEBUG
if (!dvmTestHash())
ALOGE("dvmTestHash FAILED");
if (false /*noisy!*/ && !dvmTestIndirectRefTable())
ALOGE("dvmTestIndirectRefTable FAILED");
#endif
if (dvmCheckException(dvmThreadSelf()))
dvmLogExceptionStackTrace();
return "Exception pending at end of VM initialization";
scopedShutdown.disarm();
return "";
dvmStartup调用了很多初始化接口,比如dvmClassStartup:
/*
* Initialize the bootstrap class loader.
*
* Call this after the bootclasspath string has been finalized.
*/
bool dvmClassStartup()
/* make this a requirement -- don't currently support dirs in path */
if (strcmp(gDvm.bootClassPathStr, ".") == 0)
ALOGE("ERROR: must specify non-'.' bootclasspath");
return false;
gDvm.loadedClasses =
dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
if (gDvm.pBootLoaderAlloc == NULL)
return false;
if (false)
linearAllocTests();
exit(0);
/*
* Class serial number. We start with a high value to make it distinct
* in binary dumps (e.g. hprof).
*/
gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
/*
* Set up the table we'll use for tracking initiating loaders for
* early classes.
* If it's NULL, we just fall back to the InitiatingLoaderList in the
* ClassObject, so it's not fatal to fail this allocation.
*/
gDvm.initiatingLoaderList = (InitiatingLoaderList*)
calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
/*
* Create the initial classes. These are the first objects constructed
* within the nascent VM.
*/
if (!createInitialClasses())
return false;
/*
* Process the bootstrap class path. This means opening the specified
* DEX or Jar files and possibly running them through the optimizer.
*/
assert(gDvm.bootClassPath == NULL);
processClassPath(gDvm.bootClassPathStr, true);
if (gDvm.bootClassPath == NULL)
return false;
return true;
这个类初始化gDvm的bootClassPath,这样就能执行最早看到的ActivityThread的main函数。
具体的调用栈为:ActivityThread.main -> ActivityThread.attach -> ActivityThread.bindApplication -> Activity.handleBindApplication.
到目前的分析,应用的ClassLoader还是BootClassPath,只包含了Java和Android的类,Apk自身的类是找不到的,会报ClassNotFound,接下来就是介绍ClassLoader的加载过程。
handleBindApplication会初始化ApplicationInfo对象,通getPackageInfo初始化LoadedApk,而LoadedApk则会创建这个apk对应的ClassLoader,这个ClassLoader是集成自BaseDexClassLoader,加载了apk的dex。
ApplicationInfo instrApp = new ApplicationInfo();
instrApp.packageName = ii.packageName;
instrApp.sourceDir = ii.sourceDir;
instrApp.publicSourceDir = ii.publicSourceDir;
instrApp.dataDir = ii.dataDir;
instrApp.nativeLibraryDir = ii.nativeLibraryDir;
LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true);
ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try
java.lang.ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
catch (Exception e)
throw new RuntimeException(
"Unable to instantiate instrumentation + data.instrumentationName + ": " + e.toString(), e);
ContextImpl和Instrumentation的ClassLoader都已经初始化为apk对应的BaseDexClassLoader,在这之后的类加载,都会从这个ClassLoader对象里找。ClassLoader是个树状结构,查找规则是先从父节点查找,如果类已经加载,则直接返回加载好的Class<?>。
类的加载时机有两个,一个是new操作符创建对象的,一个是直接调用ClassLoader的loadClass的时候,new操作符的代码在dalvik解释器里,我们下一个专题会讲,最后会调用dvmResolveClass(dalvik/vm/oo/Resolve.cpp)来加载类。loadClass的实现如下:
libcore/libdvm/src/main/java/java/lang/ClassLoader.java
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException
Class<?> clazz = findLoadedClass(className);
if (clazz == null)
try
clazz = parent.loadClass(className, false);
catch (ClassNotFoundException e)
// Don't want to see this.
if (clazz == null)
clazz = findClass(className);
return clazz;
它首先看自己的parent是否已经加载过class了,加载了就返回,没有就调用BaseDexClassLoader的findClass。
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null)
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \\"" + name + "\\" on path: " + pathList);
for (Throwable t : suppressedExceptions)
cnfe.addSuppressed(t);
throw cnfe;
return c;
BaseDexClassLoader的findClass,实际是调用了DexPathList的findClass。
上面的整个过程就是Android的apk从启动,一直到ClassLoader被初始化完,之后就是走Android的Activity正常生命周期了。
下篇介绍一下Dalvik虚拟机的解析器的工作原理。
作者简介:
田力,网易彩票Android端创始人,小米视频创始人,现任roobo技术经理、视频云技术总监
欢迎关注微信公众号 磨剑石,定期推送技术心得以及源码分析等文章,谢谢
以上是关于深入理解Dalvik虚拟机- Android应用进程启动过程分析的主要内容,如果未能解决你的问题,请参考以下文章