1      Zygote简介

Android的应用程序一般都是由Java语言编写而成的,这样的应用程序需要运行在独自的Dalvik虚拟机之上(当然,5.0好像默认了ART了)。但是,如果在每一个进程启动时都在物理内存中创建和初始化一个Dalvik虚拟机,这无疑对系统的性能造成很大的影响。Zygote是Android系统中的一个非常重要的守护进程,所有其他应用程序的Dalvik虚拟机都是通过Zygote孵化出来的。通过这种方式,虚拟机的内存和框架层的资源被所有应用程序共享,从而提高了应用程序的启动和运行速度。下图为Android启动的大致流程。

图1. Android系统启动的大致流程

2     Zygote启动流程分析

2.1   Init进程分析

抛开硬件层的有关内容后,Android的源头应该为init进程。这个linux进程在操作系统的内核启动的后期就被启动了,并且之后所有的进程都是它的子孙进程。Init进程由main()开始,截取的部分代码如下:

int main(int argc, char **argv)

{

……

//创建并挂载了一些基本的系统文件

mkdir("/dev", 0755);

mkdir("/proc", 0755);

mkdir("/sys", 0755);

mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");

mkdir("/dev/pts", 0755);

mkdir("/dev/socket", 0755);

mount("devpts", "/dev/pts", "devpts", 0, NULL);

mount("proc", "/proc", "proc", 0, NULL);

mount("sysfs", "/sys", "sysfs", 0, NULL);

……

//初始化了部分属性

property_init();

……

property_load_boot_defaults();

//加载了启动配置文件,即init.rc文件

init_parse_config_file("/init.rc");

//触发Action

action_for_each_trigger("early-init", action_add_queue_tail);

queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");

queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

queue_builtin_action(keychord_init_action, "keychord_init");

queue_builtin_action(console_init_action, "console_init");

……

queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

queue_builtin_action(property_service_init_action, "property_service_init");

queue_builtin_action(signal_init_action, "signal_init");

……

for(;;) {

int nr, i, timeout = -1;

execute_one_command();//执行当前Command中的一个命令,Action和Service被启动

restart_processes(); //重新启动设置了重新启动标志位的进程

//监听来自属性服务的事件

if (!property_set_fd_init && get_property_set_fd() > 0) {

ufds[fd_count].fd = get_property_set_fd();

ufds[fd_count].events = POLLIN;

ufds[fd_count].revents = 0;

fd_count++;

property_set_fd_init = 1;

}

//监控singal,如果子进程异常退出,内核将抛出SIGCHILD信号,此时将可以对进程进行处理

//或是回收系统资源,或是重启子进程

if (!signal_fd_init && get_signal_fd() > 0) {

ufds[fd_count].fd = get_signal_fd();

ufds[fd_count].events = POLLIN;

ufds[fd_count].revents = 0;

fd_count++;

signal_fd_init = 1;

}

//监听来自keychord设备的事件,这是个组合按键设备

if (!keychord_fd_init && get_keychord_fd() > 0) {

ufds[fd_count].fd = get_keychord_fd();

ufds[fd_count].events = POLLIN;

ufds[fd_count].revents = 0;

fd_count++;

keychord_fd_init = 1;

}

……

nr = poll(ufds, fd_count, timeout);//利用poll监听以上事件

if (nr <= 0)

continue;

for (i = 0; i < fd_count; i++) {

if (ufds[i].revents & POLLIN) {

if (ufds[i].fd == get_property_set_fd())

handle_property_set_fd();//处理属性服务相关事件

else if (ufds[i].fd == get_keychord_fd())

handle_keychord();//处理keychord事件

else if (ufds[i].fd == get_signal_fd())

handle_signal();//处理singal事件

}

}

}

return 0;

Zygote作为一个服务也被定义在了init.rc文件中,由init进程派生出来,配置代码截取在如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

class main

socket zygote stream 660 root system

onrestart write /sys/android_power/request_state wake

onrestart write /sys/power/state on

onrestart restart media

onrestart restart netd

由于Zygote是由java的服务进程,其启动过程和其他在init.rc文件中定义的service启动不太一样。

2.2   App_main.cpp

通过配置信息可以定位Zygote的入口函数为app_main.cpp中的main,其代码截取如下:

int main(int argc, char* const argv[])

{

……

//此类是AndroidRuntime的派生类

AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));

……

while (i < argc) {//遍历Zygote的配置参数

const char* arg = argv[i++];

if (strcmp(arg, "--zygote") == 0) {//为进程更换名字

zygote = true;

niceName = ZYGOTE_NICE_NAME;

} else if (strcmp(arg, "--start-system-server") == 0) {//设置systemServer为true

startSystemServer = true;

} else if (strcmp(arg, "--application") == 0) {//设置application启动为true

application = true;

} else if (strncmp(arg, "--nice-name=", 12) == 0) {

……

}

}

……

if (zygote) {//如果配置参数中有参数显示需要启动Zygote,则ZygoteInit启动

runtime.start("com.android.internal.os.ZygoteInit", args);

} else if (className) {//否则RuntimeInit启动

runtime.start("com.android.internal.os.RuntimeInit", args);

} else {

……

}

}

可见,app_main.cpp中的main()最为主要的是创建了一个AppRuntime变量,接着调用了它的start()成员函数。AppRuntime类继承自AndroidRuntime类,它并没有实现自己的start()方法,因此调用的是AndroidRuntime ::start()。而根据之前对App_main.cpp中main函数的分析,传入到AndroidRuntime.start()的参数存在两种情况:

1)runtime.start("com.android.internal.os.ZygoteInit",args)

2)runtime.start("com.android.internal.os.RuntimeInit",args)

因此可能存在两种不同的启动方式, “RuntimeInit”式启动时序大致如下图所示:

图2.  “RuntimeInit”式启动时序

由于在init.rc文件的Zygote服务已经配置了相关参数,因此这里只对ZygoteInit进行分析。AndroidRuntime.start()的主要作用是启动Android系统运行时库,代码截取如下:

void AndroidRuntime::start(const char* className, const Vector<String8>& options)

{

……//配置虚拟机的一些参数

JniInvocation jni_invocation;

jni_invocation.Init(NULL);

JNIEnv* env;

//开启虚拟机

if (startVm(&mJavaVM, &env) != 0) {

return;

}

//这里实际上是调用了子类AppRuntime的onVmCreate(env)

onVmCreated(env);

//注册Android JNI函数

if (startReg(env) < 0) {

ALOGE("Unable to register all android natives\n");

return;

}

//在开始调用main之前需要将参数转化成java可识别的类型,并将其全部存入一个数组

jclass stringClass;

jobjectArray strArray;

jstring classNameStr;

stringClass = env->FindClass("java/lang/String");

assert(stringClass != NULL);

strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);

assert(strArray != NULL);

classNameStr = env->NewStringUTF(className);

assert(classNameStr != NULL);

env->SetObjectArrayElement(strArray, 0, classNameStr);

for (size_t i = 0; i < options.size(); ++i) {

jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());

assert(optionsStr != NULL);

env->SetObjectArrayElement(strArray, i + 1, optionsStr);

}

char* slashClassName = toSlashClassName(className);

//找到需要启动的java类

jclass startClass = env->FindClass(slashClassName);

if (startClass == NULL) {

ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);

} else {

//得到指定类中指定方法的ID,这里得到的是ZygoteInit.main()的方法ID

jmethodID startMeth = env->GetStaticMethodID(startClass, "main",

"([Ljava/lang/String;)V");

if (startMeth == NULL) {

ALOGE("JavaVM unable to find main() in '%s'\n", className);

} else {

//调用上面得到的方法ID和相关参数,即调用Java类ZygoteInit.main();

env->CallStaticVoidMethod(startClass, startMeth, strArray);

……

}

}

……//回收资源关闭虚拟机等操作,这个函数除非进程退出,否则不会返回

}

AndroidRuntime.start()实际上主要做了3件事:1,startVM开启虚拟机;2,startReg()注册了JNI方法;3,调用Java方法。

2.3   ZygoteInit分析

ZygoteInit相关的类图结构大致如下:

图3.  ZygoteInit相关类图结构

根据init.rc中Zygote的配置参数,AndroidRuntime.start()最终调用的是ZgoteInit.main()。其代码截取如下:

public static void main(String argv[]) {

try {

……

registerZygoteSocket(socketName);//初始化了server(zygote)的一个localsocket

EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,

SystemClock.uptimeMillis());

preload();//加载了framwork.jar的class和资源到内存,这部分很占CPU

EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,

SystemClock.uptimeMillis());

SamplingProfilerIntegration.writeZygoteSnapshot();

gc();//由于后续需要fork出应用程序的进程,需要进行垃圾回收以确保应用程序的性能

Trace.setTracingEnabled(false);

if (startSystemServer) {

startSystemServer(abiList, socketName);//开启System server服务

}

Log.i(TAG, "Accepting command socket connections");

runSelectLoop(abiList);//开启循环

closeServerSocket();

} catch (MethodAndArgsCaller caller) {

caller.run();//这里实际上是真正开启了systemServer进程

} catch (RuntimeException ex) {

Log.e(TAG, "Zygote died with exception", ex);

closeServerSocket();

throw ex;

}

}

在ZygoteInit.main()中首先是调用了registerZygoteSocket(..)方法,该方法主要是为Zygote服务创建了一个localSocket。接着调用preload将class资源和系统的Resource加载到了内存中。之后利用gc()对垃圾进行回收。由于linux的写时复制机制,在fork出应用程序之前对垃圾进行回收可以最小化子进程的垃圾内存。之后,ZygoteInit.main()分别调用了startSystemServer()、runSelectLoop()方法。startSystemServer()开启了SystemServer,此进程是Zygote孵化出来的第一个java进程。而runSelectLoop()进入select的循环,用来响应其他应用程序(AMS)的请求。

2.4   Zygote启动SystemServer服务简要分析

SystemServer进程是Zygote 孵化出来的第一个进程,此进程有很多的系统进程,提供了所有核心的系统服务。与孵化其他进程不同,Zygote单独对SystemServer进程做了孵化工作。ZygoteInit.main()中调用了startSystemServer(),代码如下:

private static boolean startSystemServer(String abiList, String socketName)

throws MethodAndArgsCaller, RuntimeException {

long capabilities = posixCapabilitiesAsBits(

OsConstants.CAP_BLOCK_SUSPEND,

OsConstants.CAP_KILL,

OsConstants.CAP_NET_ADMIN,

OsConstants.CAP_NET_BIND_SERVICE,

OsConstants.CAP_NET_BROADCAST,

OsConstants.CAP_NET_RAW,

OsConstants.CAP_SYS_MODULE,

OsConstants.CAP_SYS_NICE,

OsConstants.CAP_SYS_RESOURCE,

OsConstants.CAP_SYS_TIME,

OsConstants.CAP_SYS_TTY_CONFIG

);

//SystemServer的启动参数设置

String args[] = {

"--setuid=1000",

"--setgid=1000",

"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",

"--capabilities=" + capabilities + "," + capabilities,

"--runtime-init",

"--nice-name=system_server",

"com.android.server.SystemServer",

};

ZygoteConnection.Arguments parsedArgs = null;

int pid;

try {

parsedArgs = new ZygoteConnection.Arguments(args);

ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);

ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

//向Zygote发送fork请求

pid = Zygote.forkSystemServer(

parsedArgs.uid, parsedArgs.gid,

parsedArgs.gids,

parsedArgs.debugFlags,

null,

parsedArgs.permittedCapabilities,

parsedArgs.effectiveCapabilities);

} catch (IllegalArgumentException ex) {

throw new RuntimeException(ex);

}

//以下代码仅对子进程SystemServer有效

if (pid == 0) {

if (hasSecondZygote(abiList)) {

waitForSecondaryZygote(socketName);

}

handleSystemServerProcess(parsedArgs);

}

return true;

}

进入handleSystemServerProcess(parsedArgs),从代码中可以看到Zygote根据请求创建了SystemServer进程,这个进程运行handleSystemServerProcess()。此函数对部分参数做了处理,并将剩余的参数传递给了SystemServer。

private static void handleSystemServerProcess(

ZygoteConnection.Arguments parsedArgs)

throws ZygoteInit.MethodAndArgsCaller {

closeServerSocket();

//设置默认权限

Os.umask(S_IRWXG | S_IRWXO);

if (parsedArgs.niceName != null) {

Process.setArgV0(parsedArgs.niceName);

}

final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");

if (systemServerClasspath != null) {

performSystemServerDexOpt(systemServerClasspath);

}

if (parsedArgs.invokeWith != null) {

String[] args = parsedArgs.remainingArgs;

if (systemServerClasspath != null) {

String[] amendedArgs = new String[args.length + 2];

amendedArgs[0] = "-cp";

amendedArgs[1] = systemServerClasspath;

System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);

}

//一种可能的启动方式

WrapperInit.execApplication(parsedArgs.invokeWith,

parsedArgs.niceName, parsedArgs.targetSdkVersion,

null, args);

} else {//更为常见的启动方式

ClassLoader cl = null;

if (systemServerClasspath != null) {

cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());

Thread.currentThread().setContextClassLoader(cl);

}

//将余下的参数传递给RuntimeInit.zygoteInit处理

RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);

}

//永远不会执行到这里

}

进入RuntimeInit.zygoteInit()

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)

throws ZygoteInit.MethodAndArgsCaller {

if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

//重定向log

redirectLogStreams();

//初始化运行环境

commonInit();

//启动binder线程池

nativeZygoteInit();

//调用应用程序的入口函数

applicationInit(targetSdkVersion, argv, classLoader);

}

进入applicationInit()

private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller{

nativeSetExitWithoutCleanup(true);

……

invokeStaticMain(args.startClass, args.startArgs, classLoader);

}

进入invokeStaticMain ()

private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)

throws ZygoteInit.MethodAndArgsCaller {

//解析类名

Class<?> cl;

try {

cl = Class.forName(className, true, classLoader);

} catch (ClassNotFoundException ex) {

throw new RuntimeException(

"Missing class when invoking static main " + className,

ex);

}

//解析main方法

Method m;

try {

m = cl.getMethod("main", new Class[] { String[].class });

} catch (NoSuchMethodException ex) {

throw new RuntimeException(

"Missing static main on " + className, ex);

} catch (SecurityException ex) {

throw new RuntimeException(

"Problem getting static main on " + className, ex);

}

int modifiers = m.getModifiers();

if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {

throw new RuntimeException(

"Main method is not public and static on " + className);

}

//这里并没有直接去调用SystemServer.main()而是抛出了一个异常,这个异常将在//ZygoteInit.main()中被捕捉

throw new ZygoteInit.MethodAndArgsCaller(m, argv);

}

这里非常奇怪,invokeStaticMain并没有像我们想象的那样直接去调用SystemServer类的有关代码,它只是获取了SystemServer.main方法体和有关参数,并将其封装在一个MethodAndArgsCaller异常中,这个异常将在ZygoteInit.main中被捕捉。具体可以查看之前的代码。先查看一下这个异常类的设计,进入MethodAndArgsCaller类查看代码。

public static class MethodAndArgsCaller extends Exception

implements Runnable {

//调用方法体

private final Method mMethod;

//调用方法所需要的参数

private final String[] mArgs;

public MethodAndArgsCaller(Method method, String[] args) {

mMethod = method;

mArgs = args;

}

public void run() {

try {

//最终还是调用了main()方法

mMethod.invoke(null, new Object[] { mArgs });

} catch (IllegalAccessException ex) {

throw new RuntimeException(ex);

} catch (InvocationTargetException ex) {

Throwable cause = ex.getCause();

if (cause instanceof RuntimeException) {

throw (RuntimeException) cause;

} else if (cause instanceof Error) {

throw (Error) cause;

}

throw new RuntimeException(ex);

}

}

}

}

抛出MethodAndArgsCaller异常分析:MethodAndArgsCaller异常是在startSystemSerer()中抛出的,从单个文件的代码来看,程序的执行流跳过runSelectLoop(..)。而runSelectLoop()是Zygote的服务的一个非常重要的方法。这显然不合理。考虑到startSystemServer中开启了名字为SystemServer的进程(此时,这个子进程程序流还未流入SystemServer.main()),抛出这个异常MethodAndArgsCaller的实际上是handleSystemServerProcess()的,即SystemServer进程,而父进程Zygote并未受影响。因此跳过runSelectLoop(..)是合理的。通过这样的方式,可以摆脱在启动初期时设置的堆栈桢。这些应该都和linux的fork机制有关。

以上SystemServer的启动流程大致时序如下图所示。

图4.  SystemServer启动大致时序

2.5   runSelectLoop()分析

在分析runSelectLoop之前,需要对应用程序的启动流程作一下简要的分析。应用程序的主入口是在ActivityThread的main函数,activity的startActivity最终是在ActivityManagerService中执行的。

首先调用的是ActivityManagerService.startProcessLocked()方法,这个方法有多个重载方法,但是都会调用Process.startViaZygote(),这个方法内部实际上是开启一个localSocket并连接Zygote进程(此时的Zygote正处于runSelectLoop之中监听事件)。建立连接之后首先是将参数通过localsocket对应的writer对象发送了所要启动APP的相关参数给Zygote,并等待localsocke对应的inputStream返回一个int型的pid。以及另一个boolean型的参数usingWrapper。

图5.  启动应用程序大致时序

之前已经描述,Zygote在建立ServerSystem服务之后一直处于runSelectLoop循环中,等待AMS的fork请求,runSelectLoop代码截取如下:

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {

ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();

ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

FileDescriptor[] fdArray = new FileDescriptor[4];

fds.add(sServerSocket.getFileDescriptor());

peers.add(null);

int loopCount = GC_LOOP_COUNT;

while (true) {

int index;

if (loopCount <= 0) {

gc();//在比较空闲的时候执行gc

loopCount = GC_LOOP_COUNT;

} else {

loopCount--;

}

try {

fdArray = fds.toArray(fdArray);

//这是一个Native方法,其方法内部调用了Select系统函数等待客户端连接请求

index = selectReadable(fdArray);//注意这里的返回值和select()的返回值是不//同的概念,这里Index是活动fd的索引

} catch (IOException ex) {

throw new RuntimeException("Error in select()", ex);

}

if (index < 0) {

throw new RuntimeException("Error in select()");

} else if (index == 0) {//有连接进入,放入监听fd队列

ZygoteConnection newPeer = acceptCommandPeer(abiList);

peers.add(newPeer);

fds.add(newPeer.getFileDescriptor());

} else {//有请求

boolean done;

done = peers.get(index).runOnce();//处理请求

if (done) {

peers.remove(index);

fds.remove(index);

}

}

}

}

进入runOnce()

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

String args[];

Arguments parsedArgs = null;

FileDescriptor[] descriptors;

long startTime = SystemClock.elapsedRealtime();

try {

args = readArgumentList();//读取传递进来的请求参数

descriptors = mSocket.getAncillaryFileDescriptors();

}……

……//fork出应用进程

pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,

parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,

parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,

parsedArgs.appDataDir);

……

try {

if (pid == 0) {

// 子进程中

IoUtils.closeQuietly(serverPipeFd);

serverPipeFd = null;

handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

return true;

} else {

// 父进程中

IoUtils.closeQuietly(childPipeFd);

childPipeFd = null;

//发送pid和usingWrapper给AMS

return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);

}

} finally {

IoUtils.closeQuietly(childPipeFd);

IoUtils.closeQuietly(serverPipeFd);

}

}

进入handleChildProc()

private void handleChildProc(Arguments parsedArgs,

FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)

throws ZygoteInit.MethodAndArgsCaller {

closeSocket();

ZygoteInit.closeServerSocket();

……

if (parsedArgs.niceName != null) {//设置进程名字

Process.setArgV0(parsedArgs.niceName);

}

//参数中配置了RuntimeInit,这里为true

if (parsedArgs.runtimeInit) {

if (parsedArgs.invokeWith != null) {//Android中另一种可能的启动方式

WrapperInit.execApplication(parsedArgs.invokeWith,

parsedArgs.niceName, parsedArgs.targetSdkVersion,

pipeFd, parsedArgs.remainingArgs);

} else {//程序最终会走到这里

RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,

parsedArgs.remainingArgs, null);

}

} else {

……

}

}

可看到,handleChildProc最终调用RuntimeInit.ZygoteInit()。这个方法在分析SystemServer启动过程时已经分析过了,它依次调用了redirectLogStreams()、commonInit()、zygoteInitNative()  applicationInit()并最终调用了invokeStaticMain()抛出异常,在catch块中运行指定类的main函数。

 

3        分析与总结

本文主要分析了Android5.0中Zygote的启动,以及Zygote孵化SystemServer进程和普通应用进程的过程。从以上的分析可以得知,Android充分利用了Linux的一些特性,在保证了内存被尽可能地共享的同时,又保证了系统的性能和速度

原文地址: http://blog.csdn.net/a34140974/article/details/49783781

Android5.0源码分析—— Zygote进程分析相关推荐

  1. Android6.0源码分析—— Zygote进程分析(补充)

    原文地址: http://blog.csdn.net/a34140974/article/details/50915307 此博文为<Android5.0源码分析-- Zygote进程分析> ...

  2. Android4.0源码Launcher启动流程分析【android源码Launcher系列一】

    最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程. Launcher其实是贯彻于手机的整个系统的,时时刻刻都 ...

  3. Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】

    最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程.Launcher其实是贯彻于手机的整个系统的,时时刻刻都在 ...

  4. android5.0源码开发之NVRam存储,恢复出厂设置数据不被擦除--上篇

    MTK有一个特殊的存储区域,叫NVRam,这部分存储器用来保存重要的数据,如:校准参数.IMEI写号.FactoryMode等,即使刷机,只要不格式化下载,这些数据还在,恢复出厂设置也不会被擦除.今天 ...

  5. Android 7.0 源码分析项目一期竣工啦

    从 Android 入行开始,因为工作需求和解决疑难bug的原因陆陆续续的看过一些源码,但都不成系统,从2016年年底开始,在Github上建了一个Android Open Source Projec ...

  6. android6.0源码分析之Zygote进程分析

    在android6.0源码分析之Runtime的初始化一文中,对Zygote进程的初期的Runtime初始化过程进行了分析,在Runtime启动结束后,会对Zygote进程进行初始化,其它Java进程 ...

  7. 第一次作业:对于Linux2.6.0源码中进程模型的分析

    摘要: 作为第一次写博客,可能在排版,页面布局等方面会有大大小小的失误和不足,希望阅读者可以指出,笔者会继续学习,锻炼自己的博客水平:作为第一次分析Linux操作系统,基于进程模型的理解,在不是很熟悉 ...

  8. android6.0源码分析之Activity启动过程

    Activity最为Android开发者最熟悉的组件,由ActivityManagerService服务进行调度管理,而ActivityManagerService的启动过程在activitymana ...

  9. android6.0源码分析之AMS服务源码分析

    activitymanagerservice服务源码分析 1.ActivityManagerService概述 ActivityManagerService(以下简称AMS)作为Android中最核心 ...

最新文章

  1. Swift中空合运算符、闭区间运算符、单侧区间、半开区间
  2. 长沙网络推广教你如何在网站优化中让URL标准化?
  3. LeetCode 213. 打家劫舍 II
  4. 回溯法(其实是递归)
  5. 问题步骤记录器——“懒教师”的好帮手
  6. python 合并word文件,在Python上的WordCloud中,我想合并两种语言
  7. 前端学习(2462):打包优化
  8. python获取mac窗口程序内容_在Mac OS X中获取当前活动窗口/文档的标题
  9. Java中的抽象类和接口(interface),abstract关键字的用法
  10. 批量修改Dell服务器远程管理卡iDRAC密码
  11. Maven学习总结(58)—— 常用的 Maven 镜像地址和中央仓库地址汇总
  12. Cygwin下cscope的配置
  13. r710 linux网卡驱动,Dell R710更换网卡驱动linux
  14. java 从socket读数据,从数据读取TcpClient不如socket
  15. 27岁,大专学历,女程序员内心的感受和行业焦虑
  16. pathrewrite不生效_webpack配置proxyTable时pathRewrite无效的解决方法
  17. 防火墙阻止tftp_H3C防火墙常见问题汇总
  18. ocr文字识别技术有什么意义
  19. 史上最猛“员工”,疯狂吐槽亿万富翁老板小扎:那么有钱,还总穿着同样的衣服!
  20. 矩阵乘法 基础训练-蓝桥杯

热门文章

  1. ASP.NETserver控件使用之Reportviewer 报表
  2. TensorFlow 中文文档 介绍
  3. 编程方法学8:信息隐藏
  4. 【转】玩转git分支
  5. Caffe常用层参数介绍
  6. caffe源码分析:softmax_layer.cpp softmax_loss_layer.cpp
  7. 【随笔】卷积神经网络中的卷积怎么卷?
  8. [云炬创业学笔记]第三章商业创意的发掘与评估测试2
  9. 科大星云诗社动态20210429
  10. [我的1024开源程序]350元写的HTML5程序