文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6768304

在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育 (fork)出来的,这也许就是为什么要把它称为Zygote(受精卵)的原因吧。由于Zygote进程在Android系统中有着如此重要的地位,本文 将详细分析它的启动过程。

在前面一篇文章Android应用程序进程启动过程的源代码分析中,我们看到了,当ActivityManagerService启动一个应用程序的时候,就会通过Socket与Zygote进程进行通信,请求它fork一个子进程出来作为这个即将要启动的应用程序的进程;在前面两篇文章Android应用程序安装过程源代码分析Android系统默认Home应用程序(Launcher)的启动过程源代码分析中, 我们又看到了,系统中的两个重要服务PackageManagerService和ActivityManagerService,都是由 SystemServer进程来负责启动的,而SystemServer进程本身是Zygote进程在启动的过程中fork出来的。

我们知道,Android系统是基于Linux内核的,而在Linux系统中,所有的进程都是init进程的子孙进程,也就是说,所有的进程都是直接或 者间接地由init进程fork出来的。Zygote进程也不例外,它是在系统启动的过程,由init进程创建的。在系统启动脚本system/core /rootdir/init.rc文件中,我们可以看到启动Zygote进程的脚本命令:

  1. service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
  2. socket zygote stream 666
  3. onrestart write /sys/android_power/request_state wake
  4. onrestart write /sys/power/state on
  5. onrestart restart media
  6. onrestart restart netd

前面的关键字service告诉init进程创建一个名为"zygote"的进程,这个zygote进程要执行的程序是/system/bin/app_process,后面是要传给app_process的参数。

接下来的socket关键字表示这个zygote进程需要一个名称为"zygote"的socket资源,这样,系统启动后,我们就可以在/dev /socket目录下看到有一个名为zygote的文件。这里定义的socket的类型为unix domain socket,它是用来作本地进程间通信用的,具体可以参考前面一篇文章Android学习启动篇提到的一书《Linux内核源代码情景分析》的第七章--基于socket的进程间通信。前面我们说到的ActivityManagerService就是通这个socket来和zygote进程通信请求fork一个应用程序进程的了。

最后的一系列onrestart关键字表示这个zygote进程重启时需要执行的命令。

关于init.rc文件的更多信息,请参考system/core/init/readme.txt文件。

了解了这个信息之后,我们就知道Zygote进程要执行的程序便是system/bin/app_process了,它的源代码位于 frameworks/base/cmds/app_process/app_main.cpp文件中,入口函数是main。在继续分析Zygote进程 启动的过程之前,我们先来看看它的启动序列图:

下面我们就详细分析每一个步骤。

Step 1. app_process.main

这个函数定义在frameworks/base/cmds/app_process/app_main.cpp文件中:

  1. int main(int argc, const char* const argv[])
  2. {
  3. // These are global variables in ProcessState.cpp
  4. mArgC = argc;
  5. mArgV = argv;
  6. mArgLen = 0;
  7. for (int i=0; i<argc; i++) {
  8. mArgLen += strlen(argv[i]) + 1;
  9. }
  10. mArgLen--;
  11. AppRuntime runtime;
  12. const char *arg;
  13. argv0 = argv[0];
  14. // Process command line arguments
  15. // ignore argv[0]
  16. argc--;
  17. argv++;
  18. // Everything up to '--' or first non '-' arg goes to the vm
  19. int i = runtime.addVmArguments(argc, argv);
  20. // Next arg is parent directory
  21. if (i < argc) {
  22. runtime.mParentDir = argv[i++];
  23. }
  24. // Next arg is startup classname or "--zygote"
  25. if (i < argc) {
  26. arg = argv[i++];
  27. if (0 == strcmp("--zygote", arg)) {
  28. bool startSystemServer = (i < argc) ?
  29. strcmp(argv[i], "--start-system-server") == 0 : false;
  30. setArgv0(argv0, "zygote");
  31. set_process_name("zygote");
  32. runtime.start("com.android.internal.os.ZygoteInit",
  33. startSystemServer);
  34. } else {
  35. set_process_name(argv0);
  36. runtime.mClassName = arg;
  37. // Remainder of args get passed to startup class main()
  38. runtime.mArgC = argc-i;
  39. runtime.mArgV = argv+i;
  40. LOGV("App process is starting with pid=%d, class=%s.\n",
  41. getpid(), runtime.getClassName());
  42. runtime.start();
  43. }
  44. } else {
  45. LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
  46. fprintf(stderr, "Error: no class name or --zygote supplied.\n");
  47. app_usage();
  48. return 10;
  49. }
  50. }

这个函数的主要作用就是创建一个AppRuntime变量,然后调用它的start成员函数。AppRuntime这个类我们在Android应用程序进程启动过程的源代码分析一文中已经有过介绍了,它同样是在frameworks/base/cmds/app_process/app_main.cpp文件中定义:

  1. class AppRuntime : public AndroidRuntime
  2. {
  3. ......
  4. };

它约继承于AndroidRuntime类, AndroidRuntime类定义在frameworks/base/core/jni/AndroidRuntime.cpp文件中:

  1. ......
  2. static AndroidRuntime* gCurRuntime = NULL;
  3. ......
  4. AndroidRuntime::AndroidRuntime()
  5. {
  6. ......
  7. assert(gCurRuntime == NULL);        // one per process
  8. gCurRuntime = this;
  9. }

当AppRuntime对象创建时,会调用其父类AndroidRuntime的构造函数,而在AndroidRuntime类的构造函数里面,会将
this指针保存在静态全局变量gCurRuntime中,这样,当其它地方需要使用这个AppRuntime对象时,就可以通过同一个文件中的这个函数
来获取这个对象的指针:

  1. AndroidRuntime* AndroidRuntime::getRuntime()
  2. {
  3. return gCurRuntime;
  4. }

回到上面的main函数中,由于我们在init.rc文件中,设置了app_process启动参数--zygote和--start-system-server,因此,在main函数里面,最终会执行下面语句:

  1. runtime.start("com.android.internal.os.ZygoteInit",
  2. rtSystemServer);

这里的参数startSystemServer为true,表示要启动SystemServer组件。由于AppRuntime没有实现自己的
start函数,它继承了父类AndroidRuntime的start函数,因此,下面会执行AndroidRuntime类的start函数。

Step 2. AndroidRuntime.start

这个函数定义在frameworks/base/core/jni/AndroidRuntime.cpp文件中:

  1. /*
  2. * Start the Android runtime.  This involves starting the virtual machine
  3. * and calling the "static void main(String[] args)" method in the class
  4. * named by "className".
  5. */
  6. void AndroidRuntime::start(const char* className, const bool startSystemServer)
  7. {
  8. ......
  9. char* slashClassName = NULL;
  10. char* cp;
  11. JNIEnv* env;
  12. ......
  13. /* start the virtual machine */
  14. if (startVm(&mJavaVM, &env) != 0)
  15. goto bail;
  16. /*
  17. * Register android functions.
  18. */
  19. if (startReg(env) < 0) {
  20. LOGE("Unable to register all android natives\n");
  21. goto bail;
  22. }
  23. /*
  24. * We want to call main() with a String array with arguments in it.
  25. * At present we only have one argument, the class name.  Create an
  26. * array to hold it.
  27. */
  28. jclass stringClass;
  29. jobjectArray strArray;
  30. jstring classNameStr;
  31. jstring startSystemServerStr;
  32. stringClass = env->FindClass("java/lang/String");
  33. assert(stringClass != NULL);
  34. strArray = env->NewObjectArray(2, stringClass, NULL);
  35. assert(strArray != NULL);
  36. classNameStr = env->NewStringUTF(className);
  37. assert(classNameStr != NULL);
  38. env->SetObjectArrayElement(strArray, 0, classNameStr);
  39. startSystemServerStr = env->NewStringUTF(startSystemServer ?
  40. "true" : "false");
  41. env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
  42. /*
  43. * Start VM.  This thread becomes the main thread of the VM, and will
  44. * not return until the VM exits.
  45. */
  46. jclass startClass;
  47. jmethodID startMeth;
  48. slashClassName = strdup(className);
  49. for (cp = slashClassName; *cp != '\0'; cp++)
  50. if (*cp == '.')
  51. *cp = '/';
  52. startClass = env->FindClass(slashClassName);
  53. if (startClass == NULL) {
  54. ......
  55. } else {
  56. startMeth = env->GetStaticMethodID(startClass, "main",
  57. "([Ljava/lang/String;)V");
  58. if (startMeth == NULL) {
  59. ......
  60. } else {
  61. env->CallStaticVoidMethod(startClass, startMeth, strArray);
  62. ......
  63. }
  64. }
  65. ......
  66. }

这个函数的作用是启动Android系统运行时库,它主要做了三件事情,一是调用函数startVM启动虚拟机,二是调用函数startReg注册
JNI方法,三是调用了com.android.internal.os.ZygoteInit类的main函数。

Step 3. ZygoteInit.main

这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

  1. public class ZygoteInit {
  2. ......
  3. public static void main(String argv[]) {
  4. try {
  5. ......
  6. registerZygoteSocket();
  7. ......
  8. ......
  9. if (argv[1].equals("true")) {
  10. startSystemServer();
  11. } else if (!argv[1].equals("false")) {
  12. ......
  13. }
  14. ......
  15. if (ZYGOTE_FORK_MODE) {
  16. ......
  17. } else {
  18. runSelectLoopMode();
  19. }
  20. ......
  21. } catch (MethodAndArgsCaller caller) {
  22. ......
  23. } catch (RuntimeException ex) {
  24. ......
  25. }
  26. }
  27. ......
  28. }

它主要作了三件事情,一个调用registerZygoteSocket函数创建了一个socket接口,用来和
ActivityManagerService通讯,二是调用startSystemServer函数来启动SystemServer组件,三是调用
runSelectLoopMode函数进入一个无限循环在前面创建的socket接口上等待ActivityManagerService请求创建新的
应用程序进程。

Step 4. ZygoteInit.registerZygoteSocket

这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

  1. public class ZygoteInit {
  2. ......
  3. /**
  4. * Registers a server socket for zygote command connections
  5. *
  6. * @throws RuntimeException when open fails
  7. */
  8. private static void registerZygoteSocket() {
  9. if (sServerSocket == null) {
  10. int fileDesc;
  11. try {
  12. String env = System.getenv(ANDROID_SOCKET_ENV);
  13. fileDesc = Integer.parseInt(env);
  14. } catch (RuntimeException ex) {
  15. ......
  16. }
  17. try {
  18. sServerSocket = new LocalServerSocket(
  19. createFileDescriptor(fileDesc));
  20. } catch (IOException ex) {
  21. .......
  22. }
  23. }
  24. }
  25. ......
  26. }

这个socket接口是通过文件描述符来创建的,这个文件描符代表的就是我们前面说的/dev/socket/zygote文件了。这个文件描述符是通过环境变量ANDROID_SOCKET_ENV得到的,它定义为:

  1. public class ZygoteInit {
  2. ......
  3. private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
  4. ......
  5. }

那么,这个环境变量的值又是由谁来设置的呢?我们知道,系统启动脚本文件system/core/rootdir/init.rc是由init进程来解
释执行的,而init进程的源代码位于system/core/init目录中,在init.c文件中,是由service_start函数来解释
init.rc文件中的service命令的:

  1. void service_start(struct service *svc, const char *dynamic_args)
  2. {
  3. ......
  4. pid_t pid;
  5. ......
  6. pid = fork();
  7. if (pid == 0) {
  8. struct socketinfo *si;
  9. ......
  10. for (si = svc->sockets; si; si = si->next) {
  11. int socket_type = (
  12. !strcmp(si->type, "stream") ? SOCK_STREAM :
  13. (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
  14. int s = create_socket(si->name, socket_type,
  15. si->perm, si->uid, si->gid);
  16. if (s >= 0) {
  17. publish_socket(si->name, s);
  18. }
  19. }
  20. ......
  21. }
  22. ......
  23. }

每一个service命令都会促使init进程调用fork函数来创建一个新的进程,在新的进程里面,会分析里面的socket选项,对于每一个
socket选项,都会通过create_socket函数来在/dev/socket目录下创建一个文件,在这个场景中,这个文件便是zygote了,
然后得到的文件描述符通过publish_socket函数写入到环境变量中去:

  1. static void publish_socket(const char *name, int fd)
  2. {
  3. char key[64] = ANDROID_SOCKET_ENV_PREFIX;
  4. char val[64];
  5. strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
  6. name,
  7. sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
  8. snprintf(val, sizeof(val), "%d", fd);
  9. add_environment(key, val);
  10. /* make sure we don't close-on-exec */
  11. fcntl(fd, F_SETFD, 0);
  12. }

这里传进来的参数name值为"zygote",而ANDROID_SOCKET_ENV_PREFIX在system/core/include/cutils/sockets.h定义为:

  1. #define ANDROID_SOCKET_ENV_PREFIX   "ANDROID_SOCKET_"

因此,这里就把上面得到的文件描述符写入到以"ANDROID_SOCKET_zygote"为key值的环境变量中。又因为上面的
ZygoteInit.registerZygoteSocket函数与这里创建socket文件的create_socket函数是运行在同一个进程
中,因此,上面的ZygoteInit.registerZygoteSocket函数可以直接使用这个文件描述符来创建一个Java层的
LocalServerSocket对象。如果其它进程也需要打开这个/dev/socket/zygote文件来和Zygote进程进行通信,那就必须
要通过文件名来连接这个LocalServerSocket了,参考Android应用程序进程启动过程的源代码分析
文中的Step
4,ActivityManagerService是通过Process.start函数来创建一个新的进程的,而Process.start函数会首先
通过Socket连接到Zygote进程中,最终由Zygote进程来完成创建新的应用程序进程,而Process类是通过
openZygoteSocketIfNeeded函数来连接到Zygote进程中的Socket的:

  1. public class Process {
  2. ......
  3. private static void openZygoteSocketIfNeeded()
  4. throws ZygoteStartFailedEx {
  5. ......
  6. for (int retry = 0
  7. ; (sZygoteSocket == null) && (retry < (retryCount + 1))
  8. ; retry++ ) {
  9. ......
  10. try {
  11. sZygoteSocket = new LocalSocket();
  12. sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET,
  13. LocalSocketAddress.Namespace.RESERVED));
  14. sZygoteInputStream
  15. = new DataInputStream(sZygoteSocket.getInputStream());
  16. sZygoteWriter =
  17. new BufferedWriter(
  18. new OutputStreamWriter(
  19. sZygoteSocket.getOutputStream()),
  20. 256);
  21. ......
  22. } catch (IOException ex) {
  23. ......
  24. }
  25. }
  26. ......
  27. }
  28. ......
  29. }

这里的ZYGOTE_SOCKET定义为:

  1. public class Process {
  2. ......
  3. private static final String ZYGOTE_SOCKET = "zygote";
  4. ......
  5. }

它刚好就是对应/dev/socket目录下的zygote文件了。

Android系统中的socket机制和binder机制一样,都是可以用来进行进程间通信,读者可以自己对比一下这两者的不同之处,Binder进程间通信机制可以参考Android进程间通信(IPC)机制Binder简要介绍和学习计划一文。

Socket对象创建完成之后,回到Step 3中的ZygoteInit.main函数中,startSystemServer函数来启动SystemServer组件。

Step 5. ZygoteInit.startSystemServer
       这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

  1. public class ZygoteInit {
  2. ......
  3. private static boolean startSystemServer()
  4. throws MethodAndArgsCaller, RuntimeException {
  5. /* Hardcoded command line to start the system server */
  6. String args[] = {
  7. "--setuid=1000",
  8. "--setgid=1000",
  9. "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003",
  10. "--capabilities=130104352,130104352",
  11. "--runtime-init",
  12. "--nice-name=system_server",
  13. "com.android.server.SystemServer",
  14. };
  15. ZygoteConnection.Arguments parsedArgs = null;
  16. int pid;
  17. try {
  18. parsedArgs = new ZygoteConnection.Arguments(args);
  19. ......
  20. /* Request to fork the system server process */
  21. pid = Zygote.forkSystemServer(
  22. parsedArgs.uid, parsedArgs.gid,
  23. parsedArgs.gids, debugFlags, null,
  24. parsedArgs.permittedCapabilities,
  25. parsedArgs.effectiveCapabilities);
  26. } catch (IllegalArgumentException ex) {
  27. ......
  28. }
  29. /* For child process */
  30. if (pid == 0) {
  31. handleSystemServerProcess(parsedArgs);
  32. }
  33. return true;
  34. }
  35. ......
  36. }

这里我们可以看到,Zygote进程通过Zygote.forkSystemServer函数来创建一个新的进程来启动SystemServer组件,
返回值pid等0的地方就是新的进程要执行的路径,即新创建的进程会执行handleSystemServerProcess函数。

Step 6. ZygoteInit.handleSystemServerProcess
        这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

  1. public class ZygoteInit {
  2. ......
  3. private static void handleSystemServerProcess(
  4. ZygoteConnection.Arguments parsedArgs)
  5. throws ZygoteInit.MethodAndArgsCaller {
  6. closeServerSocket();
  7. /*
  8. * Pass the remaining arguments to SystemServer.
  9. * "--nice-name=system_server com.android.server.SystemServer"
  10. */
  11. RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
  12. /* should never reach here */
  13. }
  14. ......
  15. }

由于由Zygote进程创建的子进程会继承Zygote进程在前面Step
4中创建的Socket文件描述符,而这里的子进程又不会用到它,因此,这里就调用closeServerSocket函数来关闭它。这个函数接着调用
RuntimeInit.zygoteInit函数来进一步执行启动SystemServer组件的操作。

Step 7. RuntimeInit.zygoteInit

这个函数定义在frameworks/base/core/java/com/android/internal/os/RuntimeInit.java文件中:

  1. public class RuntimeInit {
  2. ......
  3. public static final void zygoteInit(String[] argv)
  4. throws ZygoteInit.MethodAndArgsCaller {
  5. ......
  6. zygoteInitNative();
  7. ......
  8. // Remaining arguments are passed to the start class's static main
  9. String startClass = argv[curArg++];
  10. String[] startArgs = new String[argv.length - curArg];
  11. System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);
  12. invokeStaticMain(startClass, startArgs);
  13. }
  14. ......
  15. }

这个函数会执行两个操作,一个是调用zygoteInitNative函数来执行一个Binder进程间通信机制的初始化工作,这个工作完成之后,这个进
程中的Binder对象就可以方便地进行进程间通信了,另一个是调用上面Step
5传进来的com.android.server.SystemServer类的main函数。

Step 8. RuntimeInit.zygoteInitNative

这个函数定义在frameworks/base/core/java/com/android/internal/os/RuntimeInit.java文件中:

  1. public class RuntimeInit {
  2. ......
  3. public static final native void zygoteInitNative();
  4. ......
  5. }

这里可以看出,函数zygoteInitNative是一个Native函数,实现在frameworks/base/core/jni/AndroidRuntime.cpp文件中,这里我们就不再细看了,具体可以参考Android应用程序进程启动过程的源代码分析一文的Step 9,完成这一步后,这个进程的Binder进程间通信机制基础设施就准备好了。

回到Step 7中的RuntimeInit.zygoteInitNative函数,下一步它就要执行com.android.server.SystemServer类的main函数了。

Step 9. SystemServer.main

这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中:

  1. public class SystemServer
  2. {
  3. ......
  4. native public static void init1(String[] args);
  5. ......
  6. public static void main(String[] args) {
  7. ......
  8. init1(args);
  9. ......
  10. }
  11. public static final void init2() {
  12. Slog.i(TAG, "Entered the Android system server!");
  13. Thread thr = new ServerThread();
  14. thr.setName("android.server.ServerThread");
  15. thr.start();
  16. }
  17. ......
  18. }

这里的main函数首先会执行JNI方法init1,然后init1会调用这里的init2函数,在init2函数里面,会创建一个ServerThread线程对象来执行一些系统关键服务的启动操作,例如我们在前面两篇文章Android应用程序安装过程源代码分析Android系统默认Home应用程序(Launcher)的启动过程源代码分析中提到的PackageManagerService和ActivityManagerService。
        这一步的具体执行过程可以参考Android应用程序安装过程源代码分析一文,这里就不再详述了。

这里执行完成后,层层返回,最后回到上面的Step
3中的ZygoteInit.main函数中,接下来它就要调用runSelectLoopMode函数进入一个无限循环在前面Step
4中创建的socket接口上等待ActivityManagerService请求创建新的应用程序进程了。

Step 10. ZygoteInit.runSelectLoopMode

这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中:

  1. public class ZygoteInit {
  2. ......
  3. private static void runSelectLoopMode() throws MethodAndArgsCaller {
  4. ArrayList<FileDescriptor> fds = new ArrayList();
  5. ArrayList<ZygoteConnection> peers = new ArrayList();
  6. FileDescriptor[] fdArray = new FileDescriptor[4];
  7. fds.add(sServerSocket.getFileDescriptor());
  8. peers.add(null);
  9. int loopCount = GC_LOOP_COUNT;
  10. while (true) {
  11. int index;
  12. ......
  13. try {
  14. fdArray = fds.toArray(fdArray);
  15. index = selectReadable(fdArray);
  16. } catch (IOException ex) {
  17. throw new RuntimeException("Error in select()", ex);
  18. }
  19. if (index < 0) {
  20. throw new RuntimeException("Error in select()");
  21. } else if (index == 0) {
  22. ZygoteConnection newPeer = acceptCommandPeer();
  23. peers.add(newPeer);
  24. fds.add(newPeer.getFileDesciptor());
  25. } else {
  26. boolean done;
  27. done = peers.get(index).runOnce();
  28. if (done) {
  29. peers.remove(index);
  30. fds.remove(index);
  31. }
  32. }
  33. }
  34. }
  35. ......
  36. }

这个函数我们已经在Android应用程序进程启动过程的源代码分析一文的Step 5中分析过了,这就是在等待ActivityManagerService来连接这个Socket,然后调用ZygoteConnection.runOnce函数来创建新的应用程序,有兴趣的读者可以参考Android应用程序进程启动过程的源代码分析这篇文章,这里就不再详述了。

这样,Zygote进程就启动完成了,学习到这里,我们终于都对Android系统中的进程有了一个深刻的认识了,这里总结一下:

1. 系统启动时init进程会创建Zygote进程,Zygote进程负责后续Android应用程序框架层的其它进程的创建和启动工作。

2. Zygote进程会首先创建一个SystemServer进程,SystemServer进程负责启动系统的关键服务,如包管理服务PackageManagerService和应用程序组件管理服务ActivityManagerService。

3. 当我们需要启动一个Android应用程序时,ActivityManagerService会通过Socket进程间通信机制,通知Zygote进程为这个应用程序创建一个新的进程。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

Android系统进程Zygote启动过程的源代码分析的更多相关文章

  1. Android应用程序进程启动过程的源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址: http://blog.csdn.net/luoshengyang/article/details/6747696 Android 应用程序框架层创 ...

  2. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  3. Android 6.0启动过程具体解析

    在之前的一篇文章中.从概念上学习了Andoird系统的启动过程.Android系统启动过程学习 而在这篇文章中,我们将从代码角度细致学习Android系统的启动过程,同一时候,学习Android启动过 ...

  4. Android 中View的绘制机制源代码分析 一

    尊重原创: http://blog.csdn.net/yuanzeyao/article/details/46765113 差点儿相同半年没有写博客了,一是由于工作比較忙,二是认为没有什么内容值得写, ...

  5. Android 中View的绘制机制源代码分析 二

    尊重原创:http://blog.csdn.net/yuanzeyao/article/details/46842891 本篇文章接着上篇文章的内容来继续讨论View的绘制机制,上篇文章中我们主要解说 ...

  6. Android Activity的启动过程

    文章编辑的太长了,请移步我的csdn博客:http://blog.csdn.net/xyh269 Android Activity的启动过程原文链接:http://blog.csdn.net/xyh2 ...

  7. Android应用程序启动过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6689748 前文简要介绍了Android应用程 ...

  8. Android 应用程序启动过程源代码分析

    本文转自:http://blog.csdn.net/luoshengyang/article/details/6689748 前文简要介绍了Android应用程序的Activity的启动过程.在And ...

  9. Android应用程序启动过程(二)分析

    本文依据Android6.0源码,从点击Launcher图标,直至解析到MainActivity#OnCreate()被调用. Launcher简析 Launcher也是个应用程序,不过是个特殊的应用 ...

随机推荐

  1. CSS 定位元素之 relative

    1. relative 和 absolute relative 会限制 absolute. absolute 会根据 父级的的定位元素来定位. 2. overflow 和 absolue 当overf ...

  2. 解决 innerHTML 在 IE6-IE9中不能赋值的bug

    在MSDN可以了解跟多,关于innerHTML的介绍,但是在这里只要是解决表格部分问题 MSDN上有这样的记录: When using innerHTML to insert script, you ...

  3. DBHerperl类

    using System; using System.Collections.Generic; using System.Text; using System.Data; using System.D ...

  4. iOS9适配+warning消除

    最近做了iOS 9的适配,程序出现大量警告也做了些处理,写出来分先给大家. 一.iOS 9适配 问题一: <Error>: CGContextSaveGState: invalid con ...

  5. Struts2的模型驱动

    Struts2即支持属性驱动,也支持模型驱动 属性驱动:在Action中提供与表单字段一一对应的属性,然后一一set赋值 模型驱动:使得表单字段都自动被set到一个JavaBean中,类似于Strut ...

  6. MFC 多线程

    MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息   队列和消息循环. 工作者线程没有消息机制,通 ...

  7. .Net 插入数据MySql数据库,中文乱码解决问题

    1, 修改mysql根目录下配置文件my.ini,在[client]节点下添加default-character-set=utf8 ,在[mysqld]节点下添加character_set_serve ...

  8. 通过jqueryui实现邮件提示

    //js代码$(function () { var availableTags = ["@qq.com", "@gmail.com", "@126.c ...

  9. IOS响应式编程框架ReactiveCocoa(RAC)使用示例-备

    ReactiveCocoa是响应式编程(FRP)在IOS中的一个实现框架,它的开源地址为:https://github.com/ReactiveCocoa/ReactiveCocoa# :在网上看了几 ...

  10. Winform(C#)限制程序只运行一个实例

    C#控制只运行开启一个程序 在这个例子中不需要调用ReleaseMutex,mutex会在程序结束时自动释放.为了防止mutex过早释放,在程序的最后调用下GC.KeepAlive (mutex). ...