深入理解Dalvik虚拟机- Android应用进程启动过程分析
来源:互联网 发布:windows http 编辑:程序博客网 时间:2024/05/02 01:10
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.rcservice 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。
@Overrideprotected 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应用进程启动过程分析
- Android源码解析之Dalvik虚拟机的启动过程分析
- Dalvik虚拟机的启动过程分析
- Dalvik虚拟机的启动过程分析
- Dalvik虚拟机的启动过程分析
- Dalvik虚拟机的启动过程分析
- Dalvik虚拟机的启动过程分析
- 【分析】dalvik虚拟机启动过程(一)
- 【分析】dalvik虚拟机启动过程(二)
- 【分析】dalvik虚拟机启动过程(三)
- Dalvik虚拟机的启动过程分析
- Dalvik虚拟机的启动过程分析
- Dalvik虚拟机的启动过程分析
- 深入理解Android之Java虚拟机Dalvik
- 深入理解Android之Java虚拟机Dalvik
- 深入理解Android之Java虚拟机Dalvik
- Android虚拟机学习总结Dalvik虚拟机进程和线程的创建过程分析
- Dalvik虚拟机进程和线程的创建过程分析
- 项目经理之软件项目经理必须具备的素质
- 关于Java 中回调函数的一些理解及与JavaScript的对比
- Gson 常用用法
- Eclipse“控制台”视图详解
- 短彩信文档
- 深入理解Dalvik虚拟机- Android应用进程启动过程分析
- 应用 (App) 和移动端网站 (Mobile Web) 的优缺点及适用领域
- poj 2407 Relatives
- python 面向对象高级编程
- 项目经理之提高会议效率
- Fragment更容易实现页面的切换
- navicat导出sql语句失败
- 17. Letter Combinations of a Phone Number
- 18--26 面向对象程序设计20(属性是否在对象(和原型对象)中)