前言

我们知道当startActivity的时候,除了创建Activity相关实体,系统会根据需要是否创建进程,此进程就是指的Linux层面的进程实体;后面会说到,其实创建的过程是fork,意为从父进程“copy”出一个新的进程。此过程必定经历启动者、ams、zygote等相互的进程间通信,本文主要梳理ams在此过程的重要环节,涉及虚拟机的继承、Binder线程池、消息循环是怎么建立的等等,而Activity的启动细节则放在其他章节另作探讨。

步骤

step1 Ams发起请求startProcessLocked

ActivityManagerService.java

直接从Ams中向zygote发起请求的入口开始分析,当然真正的开始是Ams收到其他应用的Binder请求,所以应该在onTransact里面才是真正的开始,待会儿会打出trace看。(这儿一个小细节,源码中有很多方法带Locked后缀,说明使用该方法需要加锁,因为其是非线程安全的)。

  1. // Start the process. It will either succeed and return a result containing
  2. // the PID of the new process, or else throw a RuntimeException.
  3. boolean isActivityProcess = (entryPoint == null);
  4. if (entryPoint == null) entryPoint = "android.app.ActivityThread";
  5. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
  6. app.processName);
  7. checkTime(startTime, "startProcess: asking zygote to start proc");
  8. Process.ProcessStartResult startResult = Process.start(entryPoint,
  9. app.processName, uid, uid, gids, debugFlags, mountExternal,
  10. app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
  11. app.info.dataDir, entryPointArgs);
  12. checkTime(startTime, "startProcess: returned from zygote!");
  13. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

比较重要的是这一段话,调用Process类的start方法,并指定了入口类为ActivityThread。

  1. system 671 641 2348412 179992 0 0000000000 S Binder:641_1
  2. 641-671/system_process W: at android.os.Process.zygoteSendArgsAndGetResult(Process.java:605)
  3. 641-671/system_process W: at android.os.Process.startViaZygote(Process.java:738)
  4. 641-671/system_process W: at android.os.Process.start(Process.java:521)
  5. 641-671/system_process W: at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:4214)
  6. 641-671/system_process W: at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:4069)
  7. 641-671/system_process W: at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:3909)
  8. 641-671/system_process W: at com.android.server.am.ActivityStackSupervisor.startSpecificActivityLocked(ActivityStackSupervisor.java:1441)
  9. 641-671/system_process W: at com.android.server.am.ActivityStack.resumeTopActivityInnerLocked(ActivityStack.java:2741)
  10. 641-671/system_process W: at com.android.server.am.ActivityStack.resumeTopActivityUncheckedLocked(ActivityStack.java:2213)
  11. 641-671/system_process W: at com.android.server.am.ActivityStackSupervisor.resumeFocusedStackTopActivityLocked(ActivityStackSupervisor.java:1859)
  12. 641-671/system_process W: at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1393)
  13. 641-671/system_process W: at com.android.server.am.ActivityStack.activityPausedLocked(ActivityStack.java:1237)
  14. 641-671/system_process W: at com.android.server.am.ActivityManagerService.activityPaused(ActivityManagerService.java:7393)
  15. 641-671/system_process W: at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:571)
  16. 641-671/system_process W: at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3169)
  17. 641-671/system_process W: at android.os.Binder.execTransact(Binder.java:565)

从如上的trace可以直接看出,在收到startActivity的请求后,执行了对当前Activity的pause操作,然后通过Process.java这个类对Zygote进程发起请求。注意,通过对线程号的观察可以看到,这一切都在一次binder线程中操作的。

注意Ams与Zygote之间是通过socket连接的,封装了zygoteState类去做通信相关的任务。从下图fd的占用可以明确地看到,zygote进程是不使用binder的。

  1. device:/proc/341/fd # ls -l
  2. ls -l
  3. total 0
  4. lrwx------ 1 root root 64 2021-03-16 17:49 0 -> /dev/null
  5. lrwx------ 1 root root 64 2021-03-16 17:49 1 -> /dev/null
  6. lr-x------ 1 root root 64 2021-03-16 17:49 10 -> /system/framework/core-oj.jar
  7. lr-x------ 1 root root 64 2021-03-16 17:49 11 -> /system/framework/core-libart.jar
  8. lr-x------ 1 root root 64 2021-03-16 17:49 12 -> /system/framework/conscrypt.jar
  9. lr-x------ 1 root root 64 2021-03-16 17:49 13 -> /system/framework/okhttp.jar
  10. lr-x------ 1 root root 64 2021-03-16 17:49 14 -> /system/framework/core-junit.jar
  11. lr-x------ 1 root root 64 2021-03-16 17:49 15 -> /system/framework/bouncycastle.jar
  12. lr-x------ 1 root root 64 2021-03-16 17:49 16 -> /system/framework/ext.jar
  13. lr-x------ 1 root root 64 2021-03-16 17:49 17 -> /system/framework/framework.jar
  14. lr-x------ 1 root root 64 2021-03-16 17:49 18 -> /system/framework/telephony-common.jar
  15. lr-x------ 1 root root 64 2021-03-16 17:49 19 -> /system/framework/voip-common.jar
  16. lrwx------ 1 root root 64 2021-03-16 17:49 2 -> /dev/null
  17. lr-x------ 1 root root 64 2021-03-16 17:49 20 -> /system/framework/ims-common.jar
  18. lr-x------ 1 root root 64 2021-03-16 17:49 21 -> /system/framework/apache-xml.jar
  19. lr-x------ 1 root root 64 2021-03-16 17:49 22 -> /system/framework/org.apache.http.legacy.boot.jar
  20. lrwx------ 1 root root 64 2021-03-16 17:49 23 -> socket:[9842]
  21. lr-x------ 1 root root 64 2021-03-16 17:49 24 -> /system/framework/framework-res.apk
  22. lr-x------ 1 root root 64 2021-03-16 17:49 25 -> /dev/urandom
  23. l-wx------ 1 root root 64 2021-03-16 17:49 5 -> /sys/kernel/debug/tracing/trace_marker
  24. lrwx------ 1 root root 64 2021-03-16 17:49 6 -> /dev/null
  25. lrwx------ 1 root root 64 2021-03-16 17:49 7 -> /dev/null
  26. lrwx------ 1 root root 64 2021-03-16 17:49 8 -> /dev/null
  27. lrwx------ 1 root root 64 2021-03-16 17:49 9 -> socket:[9289]

Ams的调用栈到上面就截止了,接下来进入到Zygote进程中。

step2 Zygote收到请求

猜想Zygote进程初始化完成后,一定有个无限循环在等待其他进程的请求,直接定位到代码。

  1. private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
  2. ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
  3. ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
  4. fds.add(sServerSocket.getFileDescriptor());
  5. peers.add(null);
  6. while (true) {
  7. StructPollfd[] pollFds = new StructPollfd[fds.size()];
  8. for (int i = 0; i < pollFds.length; ++i) {
  9. pollFds[i] = new StructPollfd();
  10. pollFds[i].fd = fds.get(i);
  11. pollFds[i].events = (short) POLLIN;
  12. }
  13. try {
  14. Os.poll(pollFds, -1);
  15. } catch (ErrnoException ex) {
  16. throw new RuntimeException("poll failed", ex);
  17. }
  18. for (int i = pollFds.length - 1; i >= 0; --i) {
  19. if ((pollFds[i].revents & POLLIN) == 0) {
  20. continue;
  21. }
  22. if (i == 0) {
  23. ZygoteConnection newPeer = acceptCommandPeer(abiList);
  24. peers.add(newPeer);
  25. fds.add(newPeer.getFileDesciptor());
  26. } else {
  27. boolean done = peers.get(i).runOnce();
  28. if (done) {
  29. peers.remove(i);
  30. fds.remove(i);
  31. }
  32. }
  33. }
  34. }
  35. }

可以看到,当接收到请求后,调用ZygoteConnection类的runOnce来处理。

  1. boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
  2. .......省略代码
  3. pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
  4. parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
  5. parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
  6. parsedArgs.appDataDir);
  7. ......省略代码
  8. try {
  9. /*子进程执行pid==0情况,父进程执行else情况*/
  10. if (pid == 0) {
  11. /*子进程*/
  12. // in child
  13. IoUtils.closeQuietly(serverPipeFd);
  14. serverPipeFd = null;
  15. handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
  16. // should never get here, the child is expected to either
  17. // throw ZygoteInit.MethodAndArgsCaller or exec().
  18. return true;
  19. } else {
  20. // in parent...pid of < 0 means failure
  21. IoUtils.closeQuietly(childPipeFd);
  22. childPipeFd = null;
  23. /*Process中的io流监听的pid等信息都是通过下面的代码发出去的*/
  24. return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
  25. }
  26. } finally {
  27. IoUtils.closeQuietly(childPipeFd);
  28. IoUtils.closeQuietly(serverPipeFd);
  29. }
  30. }

主要玄机是forkAndSpecialize这个方法,能fork出native的子进程,然后再返回到调用的地方;但是会返回两次,一次是子进程,一次是父进程,这个通过返回值来判断,和linux的fork方法类似;如果是子进程,那么执行handleChildProc,注意下面的注释,意味着不会执行到return的语句,也就是上层的selectloop(因为子进程不用管zygote的接受命令循环了,直接去执行app进程的任务了);而父进程会调用handleParentProc后返回。

子进程的返回有个小技巧,是通过抛一个异常达到的,那么回到了哪里?我们再看一下ZygoteInit.java的main方法。

  1. public static void main(String argv[]) {
  2. // Mark zygote start. This ensures that thread creation will throw
  3. // an error.
  4. ...
  5. try {
  6. Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
  7. RuntimeInit.enableDdms();
  8. // Start profiling the zygote initialization.
  9. SamplingProfilerIntegration.start();
  10. registerZygoteSocket(socketName);
  11. preload();
  12. gcAndFinalize();
  13. // Zygote process unmounts root storage spaces.
  14. Zygote.nativeUnmountStorageOnInit();
  15. ...
  16. Log.i(TAG, "Accepting command socket connections");
  17. /*这儿调用的runSelectLoop开启等待循环的*/
  18. runSelectLoop(abiList);
  19. closeServerSocket();
  20. } catch (MethodAndArgsCaller caller) {
  21. caller.run();
  22. } catch (Throwable ex) {
  23. Log.e(TAG, "Zygote died with exception", ex);
  24. closeServerSocket();
  25. throw ex;
  26. }
  27. }

是不是看到了熟悉的身影runSelectLoop,所以是Zygote进入java世界后,先进入main方法做了一些准备工作,然后开启selectLoop进行等待循环,而收到子进程抛出的MethodAndArgsCaller异常后会执行caller.run,这是我们能从调用栈中看到子进程生命的开始,之所以这样做,还可以通过抛异常的方式清空之前的调用栈,去除一些fork、设置等,看起来app的生命更清爽。如下图到onCreate的流程。

  1. 3199-3199/com.android.myapplication W: at com.android.myapplication.MainActivity.onCreate(MainActivity.java:42)
  2. 3199-3199/com.android.myapplication W: at android.app.Activity.performCreate(Activity.java:6709)
  3. 3199-3199/com.android.myapplication W: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
  4. 3199-3199/com.android.myapplication W: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2624)
  5. 3199-3199/com.android.myapplication W: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2732)
  6. 3199-3199/com.android.myapplication W: at android.app.ActivityThread.-wrap12(ActivityThread.java)
  7. 3199-3199/com.android.myapplication W: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1483)
  8. 3199-3199/com.android.myapplication W: at android.os.Handler.dispatchMessage(Handler.java:102)
  9. 3199-3199/com.android.myapplication W: at android.os.Looper.loop(Looper.java:154)
  10. 3199-3199/com.android.myapplication W: at android.app.ActivityThread.main(ActivityThread.java:6141)
  11. 3199-3199/com.android.myapplication W: at java.lang.reflect.Method.invoke(Native Method)
  12. 3199-3199/com.android.myapplication W: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912)
  13. 3199-3199/com.android.myapplication W: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:802)

哈哈,扯远了,继续看一下刚才子进程调用的handleChildProc到底做了什么事儿。

step3 handleChildProc -- 进入子进程的世界

.frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

按照惯例,先看调用栈。

  1. private void handleChildProc(Arguments parsedArgs,
  2. FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
  3. throws ZygoteInit.MethodAndArgsCaller {
  4. closeSocket();
  5. ZygoteInit.closeServerSocket();
  6. if (descriptors != null) {
  7. try {
  8. Os.dup2(descriptors[0], STDIN_FILENO);
  9. Os.dup2(descriptors[1], STDOUT_FILENO);
  10. Os.dup2(descriptors[2], STDERR_FILENO);
  11. for (FileDescriptor fd: descriptors) {
  12. IoUtils.closeQuietly(fd);
  13. }
  14. newStderr = System.err;
  15. } catch (ErrnoException ex) {
  16. Log.e(TAG, "Error reopening stdio", ex);
  17. }
  18. }
  19. ......省略代码
  20. if (parsedArgs.runtimeInit) {
  21. if (parsedArgs.invokeWith != null) {
  22. WrapperInit.execApplication(parsedArgs.invokeWith,
  23. parsedArgs.niceName, parsedArgs.targetSdkVersion,
  24. pipeFd, parsedArgs.remainingArgs);
  25. } else {
  26. RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
  27. parsedArgs.remainingArgs, null /* classLoader */);
  28. }
  29. } else {
  30. ......省略代码
  31. }
  32. }

一开始进来有一些资源回收的工作,比如关闭socket等,因为子进程用不到了,避免占用fd。dup2那三个操作是把原来的标准输入、标准输出和错误输出(fd分别为0、1、2)用参数传来的descriptors代替,实际上传来的就是“/dev/null”,可以随便进入一个app的fd可以看到0、1、2一定是/dev/null。接下来,Ams传来的参数中有“--runtime-init”并且invokeWith的是ActivityThead,然后执行RuntimeInit.zygoteInit这个方法。

step4 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. /*设置时区、log、http代理等一些信息*/
  7. commonInit();
  8. nativeZygoteInit();
  9. applicationInit(targetSdkVersion, argv, classLoader);
  10. }

commonInit是一些通用的初始化,不用去深究,下面重点看一下nativeZygoteInit(比较重要的方法一般都是放在native去干的)和applicationInit。

nativeZygoteInit

通过一系列继承、重写的技巧,总之最后调用到了app_main.cpp中

.frameworks/base/cmds/app_process/app_main.cpp

  1. virtual void onZygoteInit()
  2. {
  3. sp<ProcessState> proc = ProcessState::self();
  4. ALOGV("App process: starting thread pool.\n");
  5. proc->startThreadPool();
  6. }

如果你熟悉native binder进程的写法的话,对这两句话一定不陌生,又看到了熟悉的套路。这两句话在app启动之前帮忙打开了/dev/binder,同时启动了binder线程池。之后app在使用binder时就不用关心binder驱动以及线程池相关的事儿了。

applicationInit

  1. private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
  2. throws ZygoteInit.MethodAndArgsCaller {
  3. Class<?> cl;
  4. try {
  5. cl = Class.forName(className, true, classLoader);
  6. } catch (ClassNotFoundException ex) {
  7. throw new RuntimeException(
  8. "Missing class when invoking static main " + className,
  9. ex);
  10. }
  11. Method m;
  12. try {
  13. m = cl.getMethod("main", new Class[] { String[].class });
  14. } catch (NoSuchMethodException ex) {
  15. throw new RuntimeException(
  16. "Missing static main on " + className, ex);
  17. } catch (SecurityException ex) {
  18. throw new RuntimeException(
  19. "Problem getting static main on " + className, ex);
  20. }
  21. int modifiers = m.getModifiers();
  22. if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
  23. throw new RuntimeException(
  24. "Main method is not public and static on " + className);
  25. }
  26. /*
  27. * This throw gets caught in ZygoteInit.main(), which responds
  28. * by invoking the exception's run() method. This arrangement
  29. * clears up all the stack frames that were required in setting
  30. * up the process.
  31. */
  32. throw new ZygoteInit.MethodAndArgsCaller(m, argv);
  33. }

程序比较简单,主要是通过反射的方式找到ActivityThread的main方法,并且抛出异常把该方法作为参数传递到zygote的main方法中去(前面讲了zygote的main方法除了开启selectLoop还捕获了MethodAndArgsCaller这个异常)。

  1. /**
  2. * Helper exception class which holds a method and arguments and
  3. * can call them. This is used as part of a trampoline to get rid of
  4. * the initial process setup stack frames.
  5. */
  6. public static class MethodAndArgsCaller extends Exception
  7. implements Runnable {
  8. /** method to call */
  9. private final Method mMethod;
  10. /** argument array */
  11. private final String[] mArgs;
  12. public MethodAndArgsCaller(Method method, String[] args) {
  13. mMethod = method;
  14. mArgs = args;
  15. }
  16. public void run() {
  17. try {
  18. mMethod.invoke(null, new Object[] { mArgs });
  19. } catch (IllegalAccessException ex) {
  20. throw new RuntimeException(ex);
  21. } catch (InvocationTargetException ex) {
  22. Throwable cause = ex.getCause();
  23. if (cause instanceof RuntimeException) {
  24. throw (RuntimeException) cause;
  25. } else if (cause instanceof Error) {
  26. throw (Error) cause;
  27. }
  28. throw new RuntimeException(ex);
  29. }
  30. }
  31. }

MethodAndArgsCaller的run方法很简单,其实就是去执行ActivityThread的main方法。现在我们来捋一下,zygote饶了这么大一圈,就是为了fork出子进程,做一些binder线程池等的准备工作,最后执行ActivityThread的main方法。

通过调用栈来总结一下子进程创建生命的过程

  1. 1896-1896/com.android.myapplication W: at com.android.internal.os.RuntimeInit.invokeStaticMain(RuntimeInit.java:237)
  2. 1896-1896/com.android.myapplication W: at com.android.internal.os.RuntimeInit.applicationInit(RuntimeInit.java:338)
  3. 1896-1896/com.android.myapplication W: at com.android.internal.os.RuntimeInit.zygoteInit(RuntimeInit.java:290)
  4. 1896-1896/com.android.myapplication W: at com.android.internal.os.ZygoteConnection.handleChildProc(ZygoteConnection.java:757)
  5. 1896-1896/com.android.myapplication W: at com.android.internal.os.ZygoteConnection.runOnce(ZygoteConnection.java:243)
  6. 1896-1896/com.android.myapplication W: at com.android.internal.os.ZygoteInit.runSelectLoop(ZygoteInit.java:876)
  7. 1896-1896/com.android.myapplication W: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:798)

注意虽然看pid这是子进程的pid号,但是继承了父进程的调用栈,从handleChildProc开始是子进程,之前是父进程zygote。

step5 ActivityThread -- app的入口

.frameworks/base/core/java/android/app/ActivityThread.java

这次我们主要看一下ActivityThread的静态main方法,因为整个文件有6000多行,关系到Activity实例的各种生命周期和管理、以及viewroot的创建等等。

  1. public static void main(String[] args) {
  2. ...
  3. Looper.prepareMainLooper();
  4. ActivityThread thread = new ActivityThread();
  5. thread.attach(false);
  6. if (sMainThreadHandler == null) {
  7. sMainThreadHandler = thread.getHandler();
  8. }
  9. // End of event ActivityThreadMain.
  10. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  11. Looper.loop();
  12. throw new RuntimeException("Main thread loop unexpectedly exited");
  13. }

主要是调用Looper的静态方法创建消息循环,然后创建ActivityThread实例,最后进入到消息循环中。

总结

通过本文大致理了从Ams开始创建一个app进程的大致流程,如果以执行的进程实体来看,其实主要分三个部分,一个是ams中,一个是zygote父进程中,一个是子进程中。有了这个大致的脉络,再根据调用栈来看,实际还是比较清晰的。ams负责接收binder请求并与zygote联系,zygote父进程收到socket消息后去fork出子进程,子进程负责准备好binder环境和消息循环,然后开始Activity的生命周期。

Android应用程序的进程创建过程的更多相关文章

  1. Android应用程序内部启动Activity过程(startActivity)的源代码分析

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

  2. Android应用程序的Activity启动过程简要介绍和学习计划

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6685853 在Android系统中,Activ ...

  3. linux内核学习之六 进程创建过程学习

    一 关于linux进程概念的补充 关于进程的基本概念这里不多说,把自己的学习所得作一些补充: 1. 在linux内核中,系统最多可以有64个进程同时存在. 2.linux进程包含的关键要素:一段可执行 ...

  4. Android MediaPlayer架构 -- MediaPlayer的创建过程

    本文系作者自己学习之所用,文章内容仅出自作者拙劣之思考,问题之处烦请不吝指教. MediaPlayer 能被用来控制音/视频文件或流媒体的回放.Android中以MediaPlayer类作为音视频播放 ...

  5. 进程创建过程详解 CreateProcess

    转载请您注明出处:http://www.cnblogs.com/lsh123/p/7405796.html 0x01 CreateProcessW CreateProcess的使用有ANSI版本的Cr ...

  6. Win32之进程创建过程

    0x01. 什么是进程? 进程提供程序所需要的资源,如:数据.代码等等 进程扮演的角色仅仅是为当前程序提供资源,或者代码,这就是进程所提供的,当时程序运行的状态和进程没有关系,进程可以看做空间的概念 ...

  7. Android应用程序与SurfaceFlinger服务之间的共享UI元数据(SharedClient)的创建过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/7867340 在前面一篇文章中,我们分析了And ...

  8. Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析(转)

    在前文中,我们分析了Android应用程序窗口的运行上下文环境的创建过程.由此可知,每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一 ...

  9. Android应用程序窗口(Activity)的窗口对象(Window) 的创建过程分析

    每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一个具体的应用程序窗口. 每一个Activity组件都有一个关联的ContextImp ...

随机推荐

  1. In_array()函数弱比较

    0x01 定义 (PHP 4, PHP 5, PHP 7) in_array - 检查数组中是否存在某个值 说明 in_array ( mixed $needle , array $haystack ...

  2. sqlmap在https情况下的一个错误

    对于https网站,使用sqlmap可能会出现如下错误.使用–force-ssl无效. https证书有问题 方法 本地建立proxy.php,内容为 <?php $url = "ht ...

  3. 链接脚本再探和VMA与LMA

    链接脚本简单描述 连接脚本的描述都是以节(section)的单位的,网上也有很多描述链接脚本语法的好文章,再不济还有官方的说明文档可以用来学习,其实主要就是对编译构建的整个过程有了深入的理解后就能对链 ...

  4. JavaScript 注释规范

    JavaScript 注释规范 总原则 As short as possible(如无必要,勿增注释).尽量提高代码本身的清晰性.可读性. As long as necessary(如有必要,尽量详尽 ...

  5. TypeScript tuple 元组

    TypeScript tuple 元组 元组类型允许您用固定数量的元素表示数组,这些元素的类型是已知的,但不必相同. "use strict"; /** * * @author x ...

  6. toString()[0]

    toString()[0] https://prepack.io/ x = `A` //"A" x.toString()[0] //"A" x.toString ...

  7. flutter & plugins

    flutter & plugins https://pub.dev/ https://juejin.im/post/5c206b4ff265da61327f52f4

  8. 详解SSH 框架中对象调用流程

    摘要:SSH=Struts+Spring+Hibernate SSH不是一个框架,而是多个框架(struts+spring+hibernate)的集成,是目前较流行的一种Web应用程序开源集成框架,用 ...

  9. [JAVA学习笔记]JAVA基本程序设计结构

    一个简单的Java应用程序 public class FirstSample { public static void main(String[] args) { System.out.println ...

  10. 关于各种Formatting context

    Formatting context 我们把网页看作是由很多个盒子组成的,而这些盒子的展示方式,就是由display这个属性来决定的. 这里出现了一个概念,叫做Formatting context(格 ...