在之前的一篇文章中。从概念上学习了Andoird系统的启动过程。Android系统启动过程学习

而在这篇文章中,我们将从代码角度细致学习Android系统的启动过程,同一时候,学习Android启动过程中的初始化脚本语言,即init.rc中的语言语法。在这里,不在具体介绍Linux内核的启动过程,主要学习从Linux内核启动之后,init初始化是怎样工作的,他是怎样启动Android系统的第一个进程–Zygote进程。而且还会继续了解后面其它的进程是怎样通过Zygote进程启动的。话不多说,我们如今就来气Android系统启动之路。

## Android系统启动流程图

我们都知道,Android系统内核是基于Linux内核。所以在Android系统启动过程中,首先启动Linux内核,Bootloader载入并启动Linux内核,内核启动完毕之后,内核開始启动Android系统的init进程,然后init进程通过init.rc启动脚本语言的执行。来启动Zygote进程,作为Android其它进程的父进程。Zygote进程做完初始化工作之后,启动SystemServer来启动其它系统服务。

以下我们从init进程的启动開始学习。


  1. int main(int argc, char** argv) {
  2. if (!strcmp(basename(argv[0]), "ueventd")) {
  3. return ueventd_main(argc, argv);
  4. }
  5. if (!strcmp(basename(argv[0]), "watchdogd")) {
  6. return watchdogd_main(argc, argv);
  7. }
  8. // Clear the umask.
  9. umask(0);
  10. add_environment("PATH", _PATH_DEFPATH);
  11. bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
  12. // Get the basic filesystem setup we need put together in the initramdisk
  13. // on / and then we'll let the rc file figure out the rest.
  14. if (is_first_stage) {
  15. mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
  16. mkdir("/dev/pts", 0755);
  17. mkdir("/dev/socket", 0755);
  18. mount("devpts", "/dev/pts", "devpts", 0, NULL);
  19. mount("proc", "/proc", "proc", 0, NULL);
  20. mount("sysfs", "/sys", "sysfs", 0, NULL);
  21. }
  22. // We must have some place other than / to create the device nodes for
  23. // kmsg and null, otherwise we won't be able to remount / read-only
  24. // later on. Now that tmpfs is mounted on /dev, we can actually talk
  25. // to the outside world.
  26. open_devnull_stdio();
  27. klog_init();
  28. klog_set_level(KLOG_NOTICE_LEVEL);
  29. NOTICE("init%s started!\n", is_first_stage ?
  30. "" : " second stage");
  31. if (!is_first_stage) {
  32. // Indicate that booting is in progress to background fw loaders, etc.
  33. close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
  34. property_init();
  35. // If arguments are passed both on the command line and in DT,
  36. // properties set in DT always have priority over the command-line ones.
  37. process_kernel_dt();
  38. process_kernel_cmdline();
  39. // Propogate the kernel variables to internal variables
  40. // used by init as well as the current required properties.
  41. export_kernel_boot_props();
  42. }
  43. // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
  44. selinux_initialize(is_first_stage);
  45. // If we're in the kernel domain, re-exec init to transition to the init domain now
  46. // that the SELinux policy has been loaded.
  47. if (is_first_stage) {
  48. if (restorecon("/init") == -1) {
  49. ERROR("restorecon failed: %s\n", strerror(errno));
  50. security_failure();
  51. }
  52. char* path = argv[0];
  53. char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
  54. if (execv(path, args) == -1) {
  55. ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
  56. security_failure();
  57. }
  58. }
  59. // These directories were necessarily created before initial policy load
  60. // and therefore need their security context restored to the proper value.
  61. // This must happen before /dev is populated by ueventd.
  62. INFO("Running restorecon...\n");
  63. restorecon("/dev");
  64. restorecon("/dev/socket");
  65. restorecon("/dev/__properties__");
  66. restorecon_recursive("/sys");
  67. epoll_fd = epoll_create1(EPOLL_CLOEXEC);
  68. if (epoll_fd == -1) {
  69. ERROR("epoll_create1 failed: %s\n", strerror(errno));
  70. exit(1);
  71. }
  72. signal_handler_init();
  73. property_load_boot_defaults();
  74. start_property_service();
  75. init_parse_config_file("/init.rc");
  76. action_for_each_trigger("early-init", action_add_queue_tail);
  77. // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
  78. queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
  79. // ... so that we can start queuing up actions that require stuff from /dev.
  80. queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
  81. queue_builtin_action(keychord_init_action, "keychord_init");
  82. queue_builtin_action(console_init_action, "console_init");
  83. // Trigger all the boot actions to get us started.
  84. action_for_each_trigger("init", action_add_queue_tail);
  85. // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
  86. // wasn't ready immediately after wait_for_coldboot_done
  87. queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
  88. // Don't mount filesystems or start core system services in charger mode.
  89. char bootmode[PROP_VALUE_MAX];
  90. if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
  91. action_for_each_trigger("charger", action_add_queue_tail);
  92. } else {
  93. action_for_each_trigger("late-init", action_add_queue_tail);
  94. }
  95. // Run all property triggers based on current state of the properties.
  96. queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
  97. while (true) {
  98. if (!waiting_for_exec) {
  99. execute_one_command();
  100. restart_processes();
  101. }
  102. int timeout = -1;
  103. if (process_needs_restart) {
  104. timeout = (process_needs_restart - gettime()) * 1000;
  105. if (timeout < 0)
  106. timeout = 0;
  107. }
  108. if (!action_queue_empty() || cur_action) {
  109. timeout = 0;
  110. }
  111. bootchart_sample(&timeout);
  112. epoll_event ev;
  113. int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
  114. if (nr == -1) {
  115. ERROR("epoll_wait failed: %s\n", strerror(errno));
  116. } else if (nr == 1) {
  117. ((void (*)()) ev.data.ptr)();
  118. }
  119. }
  120. return 0;
  121. }

该文件位于system/core/init/init.cpp中,我们来看看init进程都做了哪些工作。

首先,init进程加入环境变量,而且挂载对应的文件夹。

在主文件夹/之外为kmsg和null创建设备节点。初始化selinux,由于我们这里并不研究selinux的执行机制,所以其初始化细节也不在详究。

依据起凝视能够知道,假设当前系统处于内核域中,又一次执行init来转换到init域中,由于SELinux策略已经被载入了。以下接着通过restorecon命令来将在selinux启动之前创建的文件夹的安全上下文恢复到正确的属性。

接着,便是信号处理机制的初始化工作,载入启动属性,并启动属性服务器。以下。便进入至关重要的一个函数,也是init进程的主要工作。便是执行init_parse_config_file("/init.rc")函数。该函数的主要作用就是解析init.rc文件,并执行init初始化进程语言。以下我们来看一下这个函数:


  1. int init_parse_config_file(const char* path) {
  2. INFO("Parsing %s...\n", path);
  3. Timer t;
  4. std::string data;
  5. if (!read_file(path, &data)) {
  6. return -1;
  7. }
  8. data.push_back('\n'); // TODO: fix parse_config.
  9. parse_config(path, data);
  10. dump_parser_state();
  11. NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
  12. return 0;
  13. }

该代码位于system/core/init/init_parser.cpp中。该函数读取init.rc文件。并将数据传入到parse_config(path, data)函数中。

我们来看一下parse_config函数:


  1. static void parse_config(const char *fn, const std::string& data)
  2. {
  3. struct listnode import_list;
  4. struct listnode *node;
  5. char *args[INIT_PARSER_MAXARGS];
  6. int nargs = 0;
  7. parse_state state;
  8. state.filename = fn;
  9. state.line = 0;
  10. state.ptr = strdup(data.c_str()); // TODO: fix this code!
  11. state.nexttoken = 0;
  12. state.parse_line = parse_line_no_op;
  13. list_init(&import_list);
  14. state.priv = &import_list;
  15. for (;;) {
  16. switch (next_token(&state)) {
  17. case T_EOF:
  18. state.parse_line(&state, 0, 0);
  19. goto parser_done;
  20. case T_NEWLINE:
  21. state.line++;
  22. if (nargs) {
  23. int kw = lookup_keyword(args[0]);
  24. if (kw_is(kw, SECTION)) {
  25. state.parse_line(&state, 0, 0);
  26. parse_new_section(&state, kw, nargs, args);
  27. } else {
  28. state.parse_line(&state, nargs, args);
  29. }
  30. nargs = 0;
  31. }
  32. break;
  33. case T_TEXT:
  34. if (nargs < INIT_PARSER_MAXARGS) {
  35. args[nargs++] = state.text;
  36. }
  37. break;
  38. }
  39. }
  40. parser_done:
  41. list_for_each(node, &import_list) {
  42. struct import *import = node_to_item(node, struct import, list);
  43. int ret;
  44. ret = init_parse_config_file(import->filename);
  45. if (ret)
  46. ERROR("could not import file '%s' from '%s'\n",
  47. import->filename, fn);
  48. }
  49. }

该函数和刚刚那个函数位于同一个文件里,非常明显,该函数用于解析读取的init.rc文件的字符串,该函数与文件parse.cpp中的next_token()函数配合。进行字符串的解析,然后通过调用parse_new_section()函数将services和actions等加入到执行队列中,等待trigger触发器的触发执行。

有关与Android init language(Android初始化语言)我在博客Android Init Language(android初始化语言)

中已经进行了具体的介绍。

以下我们接着看init进程中的main函数中所做的工作:

init.rc解析完毕之后,全部的启动项目都被放入到action_add_queue_tail中,接着调用action_for_each_trigger("early-init", action_add_queue_tail)。触发early-init触发器来出发这些相关services和actions的执行。

我们来看一下,在init.rc中,我们看early-init相关启动的services和actions。在这里基本上是恢复某些文件或文件夹的安全上下文,然后调用init_zygote32_64.rc文件里的命令启动zygote。


  1. service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
  2. class main
  3. socket zygote stream 660 root system
  4. onrestart write /sys/android_power/request_state wake
  5. onrestart write /sys/power/state on
  6. onrestart restart media
  7. onrestart restart netd

这里的意思是通过/system/bin/app_process32程序启动zygote进程。參数为--zygote--start-system-server。这两个參数在后面我们会用到的。

以下我们来看一下Zygote的main函数。


  1. public static void main(String argv[]) {
  2. try {
  3. RuntimeInit.enableDdms();
  4. // Start profiling the zygote initialization.
  5. SamplingProfilerIntegration.start();
  6. boolean startSystemServer = false;
  7. String socketName = "zygote";
  8. String abiList = null;
  9. for (int i = 1; i < argv.length; i++) {
  10. if ("start-system-server".equals(argv[i])) {
  11. startSystemServer = true;
  12. } else if (argv[i].startsWith(ABI_LIST_ARG)) {
  13. abiList = argv[i].substring(ABI_LIST_ARG.length());
  14. } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
  15. socketName = argv[i].substring(SOCKET_NAME_ARG.length());
  16. } else {
  17. throw new RuntimeException("Unknown command line argument: " + argv[i]);
  18. }
  19. }
  20. if (abiList == null) {
  21. throw new RuntimeException("No ABI list supplied.");
  22. }
  23. registerZygoteSocket(socketName);
  24. EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
  25. SystemClock.uptimeMillis());
  26. preload();
  27. EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
  28. SystemClock.uptimeMillis());
  29. // Finish profiling the zygote initialization.
  30. SamplingProfilerIntegration.writeZygoteSnapshot();
  31. // Do an initial gc to clean up after startup
  32. gcAndFinalize();
  33. // Disable tracing so that forked processes do not inherit stale tracing tags from
  34. // Zygote.
  35. Trace.setTracingEnabled(false);
  36. if (startSystemServer) {
  37. startSystemServer(abiList, socketName);
  38. }
  39. Log.i(TAG, "Accepting command socket connections");
  40. runSelectLoop(abiList);
  41. closeServerSocket();
  42. } catch (MethodAndArgsCaller caller) {
  43. caller.run();
  44. } catch (RuntimeException ex) {
  45. Log.e(TAG, "Zygote died with exception", ex);
  46. closeServerSocket();
  47. throw ex;
  48. }
  49. }

该代码位于frameworks/base/core/java/com/android/internal/os/ZygiteInit.java中。

在代码中。他首先通过參数推断是否启动systemServer,也就是通过刚刚我们记录下来的參数--start-system-server判定startSystemServertrue。接着通过调用registerZygoteSocket(socketName)函数来注冊zygote套接字。进行进程间的通信,我们来看一下这个函数:


  1. /**
  2. * Registers a server socket for zygote command connections
  3. *
  4. * @throws RuntimeException when open fails
  5. */
  6. private static void registerZygoteSocket(String socketName) {
  7. if (sServerSocket == null) {
  8. int fileDesc;
  9. final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
  10. try {
  11. String env = System.getenv(fullSocketName);
  12. fileDesc = Integer.parseInt(env);
  13. } catch (RuntimeException ex) {
  14. throw new RuntimeException(fullSocketName + " unset or invalid", ex);
  15. }
  16. try {
  17. FileDescriptor fd = new FileDescriptor();
  18. fd.setInt$(fileDesc);
  19. sServerSocket = new LocalServerSocket(fd);
  20. } catch (IOException ex) {
  21. throw new RuntimeException(
  22. "Error binding to local socket '" + fileDesc + "'", ex);
  23. }
  24. }
  25. }

该代码位于同一个文件里,通过代码我们能够看到,创建zygote套接字的方式是通过创建一个LocalServerSocket对象来建立进程间的通信。在凝视中有说明。注冊一个服务套接字用于zygote命令连接。

创建完毕zygote套接字之后,执行preload()函数来进行资源文件的预载入工作。


  1. static void preload() {
  2. Log.d(TAG, "begin preload");
  3. preloadClasses();
  4. preloadResources();
  5. preloadOpenGL();
  6. preloadSharedLibraries();
  7. preloadTextResources();
  8. // Ask the WebViewFactory to do any initialization that must run in the zygote process,
  9. // for memory sharing purposes.
  10. WebViewFactory.prepareWebViewInZygote();
  11. Log.d(TAG, "end preload");
  12. }

在这里,载入类,载入资源文件。载入OPenGl,载入共享库。文本资源以及准备WebView。这里不在所说,我们接着往下看。

接着便调用startSystemServer(abiList, socketName)方法启动系统服务。我们来看一下这个函数:


  1. /**
  2. * Prepare the arguments and fork for the system server process.
  3. */
  4. private static boolean startSystemServer(String abiList, String socketName)
  5. throws MethodAndArgsCaller, RuntimeException {
  6. long capabilities = posixCapabilitiesAsBits(
  7. OsConstants.CAP_BLOCK_SUSPEND,
  8. OsConstants.CAP_KILL,
  9. OsConstants.CAP_NET_ADMIN,
  10. OsConstants.CAP_NET_BIND_SERVICE,
  11. OsConstants.CAP_NET_BROADCAST,
  12. OsConstants.CAP_NET_RAW,
  13. OsConstants.CAP_SYS_MODULE,
  14. OsConstants.CAP_SYS_NICE,
  15. OsConstants.CAP_SYS_RESOURCE,
  16. OsConstants.CAP_SYS_TIME,
  17. OsConstants.CAP_SYS_TTY_CONFIG
  18. );
  19. /* Hardcoded command line to start the system server */
  20. String args[] = {
  21. "--setuid=1000",
  22. "--setgid=1000",
  23. "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
  24. "--capabilities=" + capabilities + "," + capabilities,
  25. "--nice-name=system_server",
  26. "--runtime-args",
  27. "com.android.server.SystemServer",
  28. };
  29. ZygoteConnection.Arguments parsedArgs = null;
  30. int pid;
  31. try {
  32. parsedArgs = new ZygoteConnection.Arguments(args);
  33. ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
  34. ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
  35. /* Request to fork the system server process */
  36. pid = Zygote.forkSystemServer(
  37. parsedArgs.uid, parsedArgs.gid,
  38. parsedArgs.gids,
  39. parsedArgs.debugFlags,
  40. null,
  41. parsedArgs.permittedCapabilities,
  42. parsedArgs.effectiveCapabilities);
  43. } catch (IllegalArgumentException ex) {
  44. throw new RuntimeException(ex);
  45. }
  46. /* For child process */
  47. if (pid == 0) {
  48. if (hasSecondZygote(abiList)) {
  49. waitForSecondaryZygote(socketName);
  50. }
  51. handleSystemServerProcess(parsedArgs);
  52. }
  53. return true;
  54. }

在这里准备系统服启动的參数,并通过forkSystemServer来创建系统服务进程,接着调用handleSystemServerProcess(parsedArgs)来进行系统服务的处理。


  1. /**
  2. * Finish remaining work for the newly forked system server process.
  3. */
  4. private static void handleSystemServerProcess(
  5. ZygoteConnection.Arguments parsedArgs)
  6. throws ZygoteInit.MethodAndArgsCaller {
  7. closeServerSocket();
  8. // set umask to 0077 so new files and directories will default to owner-only permissions.
  9. Os.umask(S_IRWXG | S_IRWXO);
  10. if (parsedArgs.niceName != null) {
  11. Process.setArgV0(parsedArgs.niceName);
  12. }
  13. final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
  14. if (systemServerClasspath != null) {
  15. performSystemServerDexOpt(systemServerClasspath);
  16. }
  17. if (parsedArgs.invokeWith != null) {
  18. String[] args = parsedArgs.remainingArgs;
  19. // If we have a non-null system server class path, we'll have to duplicate the
  20. // existing arguments and append the classpath to it. ART will handle the classpath
  21. // correctly when we exec a new process.
  22. if (systemServerClasspath != null) {
  23. String[] amendedArgs = new String[args.length + 2];
  24. amendedArgs[0] = "-cp";
  25. amendedArgs[1] = systemServerClasspath;
  26. System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
  27. }
  28. WrapperInit.execApplication(parsedArgs.invokeWith,
  29. parsedArgs.niceName, parsedArgs.targetSdkVersion,
  30. VMRuntime.getCurrentInstructionSet(), null, args);
  31. } else {
  32. ClassLoader cl = null;
  33. if (systemServerClasspath != null) {
  34. cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
  35. Thread.currentThread().setContextClassLoader(cl);
  36. }
  37. /*
  38. * Pass the remaining arguments to SystemServer.
  39. */
  40. RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
  41. }
  42. /* should never reach here */
  43. }

在这里完毕对创建的系统服务剩余的工作。最后调用RuntimeInit.zygoteInit将剩余的參数传递到系统服务中。


  1. public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
  2. throws ZygoteInit.MethodAndArgsCaller {
  3. if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
  4. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
  5. redirectLogStreams();
  6. commonInit();
  7. nativeZygoteInit();
  8. applicationInit(targetSdkVersion, argv, classLoader);
  9. }

该函数位于同文件夹以下的RuntimeInit.java文件里。这里的nativeZygoteInit()用于进程间通信的初始化操作,applicationInit函数用于服务的启动。我们来看一下:


  1. private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
  2. throws ZygoteInit.MethodAndArgsCaller {
  3. // If the application calls System.exit(), terminate the process
  4. // immediately without running any shutdown hooks. It is not possible to
  5. // shutdown an Android application gracefully. Among other things, the
  6. // Android runtime shutdown hooks close the Binder driver, which can cause
  7. // leftover running threads to crash before the process actually exits.
  8. nativeSetExitWithoutCleanup(true);
  9. // We want to be fairly aggressive about heap utilization, to avoid
  10. // holding on to a lot of memory that isn't needed.
  11. VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
  12. VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
  13. final Arguments args;
  14. try {
  15. args = new Arguments(argv);
  16. } catch (IllegalArgumentException ex) {
  17. Slog.e(TAG, ex.getMessage());
  18. // let the process exit
  19. return;
  20. }
  21. // The end of of the RuntimeInit event (see #zygoteInit).
  22. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  23. // Remaining arguments are passed to the start class's static main
  24. invokeStaticMain(args.startClass, args.startArgs, classLoader);
  25. }

该函数主要是一些执行时的一些工作,同一时候调用invokeStaticMain来进行main函数的执行,看这种方法的凝视,意思便是剩下的參数被传递,用于启动类的静态main函数。这里传递的类參数便是SystemServer类,所以这个函数的工作便是启动SystemServer的main函数。


  1. /**
  2. * The main entry point from zygote.
  3. */
  4. public static void main(String[] args) {
  5. new SystemServer().run();
  6. }

该函数位于frameworks/base/services/java/com/android/server/SystemServer.java类中,这里执行SystemServer的run函数,该函数环境变量设置,启动其它服务。后面就不再赘述了。

接着返回到ZygoteInit.java的main函数中,这个函数最后执行runSelectLoop函数。这里执行一个循环,接收新的连接,读取来自连接的命令。

至此。Android系统的启动过程就到了启动系统服务以及其它服务阶段了,后面我们会在别的博客中进行学习和解说。

Android 6.0启动过程具体解析的更多相关文章

  1. Android程序启动过程深入解析

    当按下Android设备电源键时究竟发生了什么? Android的启动过程是怎么样的? 什么是Linux内核? 桌面系统linux内核与Android系统linux内核有什么区别? 什么是引导装载程序 ...

  2. Android启动过程深入解析

    本文由 伯乐在线 - 云海之巅 翻译.未经许可,禁止转载!英文出处:kpbird.欢迎加入翻译小组. 当按下Android设备电源键时究竟发生了什么? Android的启动过程是怎么样的? 什么是Li ...

  3. Android启动过程深入解析【转】

    转自:http://www.open-open.com/lib/view/open1403250347934.html 当按下Android设备电源键时究竟发生了什么? Android的启动过程是怎么 ...

  4. 【凯子哥带你学Framework】Activity启动过程全解析

    It’s right time to learn Android’s Framework ! 前言 学习目标 写作方式 主要对象功能介绍 主要流程介绍 zygote是什么有什么作用 SystemSer ...

  5. Android 8.0编译过程

    Android编译系统中的Android.bp.Blueprint与Soonghttp://note.qidong.name/2017/08/android-blueprint/ 工具链关系 Andr ...

  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 AudioPolicyService服务启动过程

    AudioPolicyService是策略的制定者,比如什么时候打开音频接口设备.某种Stream类型的音频对应什么设备等等.而AudioFlinger则是策略的执行者,例如具体如何与音频设备通信,如 ...

随机推荐

  1. 【转】 OpenGL使用libPng读取png图片

    觉得自己越来越无耻了呢?原文:http://laoyin.blog.51cto.com/4885213/895554 我复制到windows下也可以正常跑出来. #include<stdarg. ...

  2. Eclipse下maven部署web项目到tomcat7(兼容tomcat8)

    1.下载tomcat7并配置好JAVA_HOME,tomcat7\webapps目录除了manager之外,其它都可以删除(删除没用的,可加速tomcat的启动). 2.新建系统变量CATALINA_ ...

  3. Linux RPM 命令参数使用详解 查看 rpm包依赖性

    转载自:http://blog.csdn.net/deutschester/article/details/6309521 rpm 执行安装包 二进制包(Binary)以及源代码包(Source)两种 ...

  4. eclipse 启动报share library load faild

      eclipse 与 jdk 版本要一致 *32 - 对应32位 *64 - 对应64位

  5. RTX——第10章 任务调度-抢占式、时间片和合作式

    以下内容转载自安富莱电子: http://forum.armfly.com/forum.php 本章教程为大家将介绍 RTX 操作系统支持的任务调度方式,抢占式,时间片和合作式,这部分算是RTX 操作 ...

  6. MFC文档(SDI)应用:画图程序(画圆、画线、鼠标事件)

    要求 1. 在客户区输出一条顺时针45度的直线.一个正方形.一个大圆: 2. 在客户区输出一个图标: 3. 当按下鼠标左键时,将以鼠标坐标为圆心画直径为20个单位的小圆. 首先设置两个变量,用来保存颜 ...

  7. ThreadStart中如何带参数

    1.ThreadStart 线程执行带参数的方法,new Thread(new ThreadStart(delegate { ThreadTask(firstPage, lastPage); })); ...

  8. 记录日志框架:log4net使用

    一.log4net简介 Log4net是Apache下一个开放源码的项目,我们可以控制日志信息的输出目的地.Log4net中定义了多种日志信息输出模式.在做项目的时候最头疼的是在程序发布到正式环境之后 ...

  9. 关于Cocos2d-x中文乱码问题的解决

    方法一: 1.首先,复制下面的代码,创建一个icov,h的头文件,并放在项目目录下 #include "stdlib.h"#include "string.h" ...

  10. 正则表达式”\d+\.?\d*”在匹配下列字符串时结果是失败的是?

    A 12.5 B 1.25 C 以上都成功 D 以上都失败 解答:B \d+ 表示可以出现1次或是n次数字 \. .? 表示可以“.”可以出现一次,也可以不出现 \d* 表示可以出现0次或是n次数字