android源码启动流程_android启动器开发

hacker|
100

文章目录:

Linux内核怎样启动Android

那么Linux内核和Android什么关系?Linux内核是怎样引导起Android呢?本文进行简单的描述。 Android虽然建立在Linux内核之上,但是他对内核进行了一些扩展,增加了一些驱动。比如Binder,loger等等驱动。可以拿Android内核代码和其Baseline版本进行对比。可以看到Android对Linux内核的所有扩展。 熟悉Linux启动的朋友知道,首先Linux引导完成之后,会启动用户态的init进程(pid为0),这个进程在整个系统运行过程中起着非常重要的作用,如果你对init进程不了解请查相关资料。init完成系统的初始化工作,然后进入shell,接收用户的输入。 Android启动也没有什么神秘的,就是用自己的init进程替换了Linux内核的init进程,完成自己初始化工作(设备,文件系统等等初始化)。然后启动自己的虚拟机,程序等等的东西。Android的init进程的代码位于system/core/init/init.c下面,可以去查看其源码,来了解Android启动详细流程。Android启动流程的资料网上已经比较多,这里就不赘述了。 可以看到移植Android过程中,调试init非常重要。因为所有和硬件平台相关的东西都这里初始化,所以init进程有可能需要移植或者配置。其他的进程都是和硬件无关的,理论上不需要修改就应该能够运行起来。 经过上面的描述可以看出,Android的init进程起着一个承上启下的作用。

自己可以编译安卓源码吗?

用最新的Ubuntu 16.04,请首先确保自己已经安装了Git.没安装的同学可以通过以下命令进行安装:

sudo apt-get install git git config –global user.email “test@test.com” git config –global user.name “test”

其中test@test.com为你自己的邮箱.

简要说明

android源码编译的四个流程:1.源码下载;2.构建编译环境;3.编译源码;4运行.下文也将按照该流程讲述.

源码下载

由于某墙的原因,这里我们采用国内的镜像源进行下载.

目前,可用的镜像源一般是科大和清华的,具体使用差不多,这里我选择清华大学镜像进行说明.(参考:科大源,清华源)

repo工具下载及安装

通过执行以下命令实现repo工具的下载和安装

mkdir ~/binPATH=~/bin:$PATHcurl ~/bin/repochmod a+x ~/bin/repo

补充说明

这里,我来简单的介绍下repo工具,我们知道AOSP项目由不同的子项目组成,为了方便进行管理,Google采用Git对AOSP项目进行多仓库管理.在聊repo工具之前,我先带你来聊聊多仓库项目:

我们有个非常庞大的项目Pre,该项目由很多个子项目R1,R2,...Rn等组成,为了方便管理和协同开发,我们为每个子项目创立自己的仓库,整个项目的结构如下:

这里写图片描述

将一个项目Pre进行分库后会遇到这么一个问题:如果我们想要创建Pre分支来做feature开发,这就意味着,我们需要到每个子项目中分别创建对应的分支,这个过程如果纯粹靠手工做,那简直是个灾难,利索当然我们会想写个自动化处理程序(我们假设这个工具叫做RepoUtil)来帮助我们解决这个问题.这个RepoUtil也会有版本管理之类的需求,因此我们也用Git对其管理,并为其创建对应的仓库.此时整个项目的结构如下:

这里写图片描述

这里RepoUtil知道整个项目Pre下的每个子项目(即维护子项目的列表),同时需要提供对这些子项目的管理功能,比如统一创建分支等.但是从"单一职责"角度来看,RepoUitl这个工具的功能过于复杂,我们完全可以将维护子项目列表这个功能抽取出来作为一个新项目sub_projects,因为子项目也会变化,因此,为其创建对应的仓库,并用Git管理,这样的化,RepoUtil只需要通过简单的对ub_projects进行依赖即可,此时整个项目的结构如下:

这里写图片描述

AOSP项目结构和我上文的描述非常类似.repo工具对应RepoUtil,mainfest对应sub_projects.

总结一下:repo就是这么一种工具,由一系列python脚本组成,通过调用Git命令实现对AOSP项目的管理.

建立源码文件夹

熟悉Git的同学都应该知道,我们需要为项目在本地创建对应的仓库.同样,这里为了方便对代码进行管理,我们为其创建一个文件夹.这里我在当前用户目录下创建了source文件夹,后面所有的下载的源码和编译出的产物也都放在这里,命令如下:

mkdir sourcecd source

初始化仓库

我们将上面的source文件夹作为仓库,现在需要来初始化这个仓库了.通过执行初始化仓库命令可以获取AOSP项目master上最新的代码并初始化该仓库,命令如下:

repo init -u

或者使用:

repo init -u git://aosp.tuna.tsinghua.edu.cn/aosp/platform/manifest

两者实现的效果一致,仅仅只是协议不同.

如果执行该命令的过程中,如果提示无法连接到 gerrit.googlesource.com,那么我们只需要编辑 ~/bin/repo文件,找到REPO_URL这一行,然后将其内容修改为:

REPO_URL = ''

然后重新执行上述命令即可.

补充说明

不带参数的manifest命令用于获取master上最新的代码,但是可以通过-b参数指定获取某个特定的android版本,比如我们想要获取android-4.0.1_r1分支,那么命令如下:

repo init -u -b android-4.0.1_r1

(AOSP项目当前所有的分支列表参看:分支列表)

同步源码到本地

初始化仓库之后,就可以开始正式同步代码到本地了,命令如下:

repo sync

以后如果需要同步最新的远程代码到本地,也只需要执行该命令即可.在同步过程中,如果因为网络原因中断,使用该命令继续同步即可.不出意外,5个小时便可以将全部源码同步到本地.所以呢,这个过程可以放在晚上睡觉期间完成.

(提示:一定要确定代码完全同步了,不然在下面编译过程出现的错误会让你痛不欲生,不确定的童鞋可以多用repo sync同步几次)

构建编译环境

源码下载完成后,就可以构建编译环境了.在开始之前,我们先来看看一些编译要求:

1. 硬件要求:

64位的操作系统只能编译2.3.x以上的版本,如果你想要编译2.3.x以下的,那么需要32位的操作系统.

磁盘空间越多越好,至少在100GB以上.意思就是,你可以去买个大点的硬盘了啊

如果你想要在是在虚拟机运行linux,那么至少需要16GB的RAM/swap.

(实际上,我非常不推荐在虚拟机中编译2.3.x以上的代码.)

2. 软件要求:

1. 操作系统要求

在AOSP开源中,主分支使用Ubuntu长期版本开发和测试的,因此也建议你使用Ubuntu进行编译,下面我们列出不同版本的的Ubuntu能够编译那些android版本:

Android版本

编译要求的Ubuntu最低版本

Android 6.0至AOSP master    Ubuntu 14.04  

Android 2.3.x至Android 5.x    Ubuntu 12.04  

Android 1.5至Android 2.2.x    Ubuntu 10.04  

2. JDK版本要求

除了操作系统版本这个问题外,我们还需要关注JDK版本问题,为了方便,同样我们也列出的不同Android版本的源码需要用到的JDK版本:

Android版本

编译要求的JDK版本

AOSP的Android主线    OpenJDK 8  

Android 5.x至android 6.0    OpenJDK 7  

Android 2.3.x至Android 4.4.x    Oracle JDK 6  

Android 1.5至Android 2.2.x    Oracle JDK 5  

更具体的可以参看:Google源码编译要求

我现在在Ubuntu 16.04下编译AOSP主线代码,因此需要安装OpenJDK 8,执行命令如下:

sudo apt-get install openjdk-8-jdk

如果你需要在Ubuntu 14.04下编译AOSP主线代码,同样需要安装OpenJDK 8,此时需要执行如下命令:

sudo apt-get updatesudo apt-get install openjdk-8-jdk

如果你要编译的是Android 5.x到android 6.0之间的系统版本,需要采用openjdk7.但是在Ubuntu 15.04及之后的版本的在线安装库中只支持openjdk8和openjdk9的安装.因此,如果你想要安装openjdk 7需要首先设置ppa:

sudo add-apt-repository ppa:openjdk-r/ppa sudo apt-get update

然后再执行安装命令:

sudo apt-get install openjdk-7-jdk

有时候,我们需要编译不同版本的android系统,就可能使用不同的jdk版本.关于jdk版本切换,可以使用如下命令:

sudo update-alternative --config javasudo update-alternative --config javac

3. 其他要求

Google官方构建编译环境指南中已经说明了Ubuntu14.04,Ubuntu 12.04,Ubuntu 10.04需要添加的依赖,这里我们就不做介绍了.我原先以为,Ubuntu16.04的设置和Ubuntu14.04的依赖设置应该差不多,但是只能说too young too simple.

下面是Ubuntu16.04中的依赖设置:

sudo apt-get install libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-dev g++-multilib sudo apt-get install -y git flex bison gperf build-essential libncurses5-dev:i386 sudo apt-get install tofrodos python-markdown libxml2-utils xsltproc zlib1g-dev:i386 sudo apt-get install dpkg-dev libsdl1.2-dev libesd0-devsudo apt-get install git-core gnupg flex bison gperf build-essential  sudo apt-get install zip curl zlib1g-dev gcc-multilib g++-multilib sudo apt-get install libc6-dev-i386 sudo apt-get install lib32ncurses5-dev x11proto-core-dev libx11-dev sudo apt-get install libgl1-mesa-dev libxml2-utils xsltproc unzip m4sudo apt-get install lib32z-dev ccache

(其中几个命令中参数是重复的,但不妨碍我们)

初始化编译环境

确保上述过程完成后,接下来我们需要初始化编译环境,命令如下:

source build/envsetup.sh

执行该命令结果如下:

这里写图片描述

不难发现该命令只是引入了其他执行脚本,至于这些脚本做什么,目前不在本文中细说.

该命令执行成功后,我们会得到了一些有用的命令,比如最下面要用到的lunch命令.

编译源码

初始化编译环境之后,就进入源码编译阶段.这个阶段又包括两个阶段:选择编译目标和执行编译.

选择编译目标

通过lunch指令设置编译目标,所谓的编译目标就是生成的镜像要运行在什么样的设备上.这里我们设置的编译目标是aosp_arm64-eng,因此执行指令:

lunch aosp_arm64-eng

编译目标格式说明

编译目标的格式:BUILD-BUILDTYPE,比如上面的aosp_arm-eng的BUILD是aosp_arm,BUILDTYPE是eng.

什么是BUILD

BUILD指的是特定功能的组合的特定名称,即表示编译出的镜像可以运行在什么环境.其中,aosp(Android Open Source Project)代表Android开源项目;arm表示系统是运行在arm架构的处理器上,arm64则是指64位arm架构;处理器,x86则表示x86架构的处理器;此外,还有一些单词代表了特定的Nexus设备,下面是常用的设备代码和编译目标,更多参考官方文档

|受型号|设备代码|编译目标|

|---|----|---|

|Nexus 6P|angler|aosp_angler-userdebug|

|Nexus 5X|bullhead|aosp_bullhead-userdebug|

|Nexus 6|shamu|aosp_shamu-userdebug|

|Nexus 5|hammerhead|aosp_hammerhead-userdebug|

提示:如果你没有Nexus设备,那么通常选择arm或者x86即可

什么是BUILDTYPE

BUILD TYPE则指的是编译类型,通常有三种:

-user:代表这是编译出的系统镜像是可以用来正式发布到市场的版本,其权限是被限制的(如,没有root权限,不鞥年dedug等)

-userdebug:在user版本的基础上开放了root权限和debug权限.

-eng:代表engineer,也就是所谓的开发工程师的版本,拥有最大的权限(root等),此外还附带了许多debug工具

了解编译目标的组成之后,我们就可以根据自己目前的情况选择了.那不知道编译目标怎么办?

我们只需要执行不带参数的lunch指令,稍后,控制台会列出所有的编译目标,如下:

这里写图片描述

接着我们只需要输入相应的数字即可.

来举个例子:你没有Nexus设备,只想编译完后运行看看,那么就可以选择aosp_arm-eng.

(我在ubuntu 16.04(64位)中编译完成后启动虚拟机时,卡在黑屏,尝试编译aosp_arm64-eng解决.因此,这里我使用了aosp_arm64-eng)

开始编译

通过make指令进行代码编译,该指令通过-j参数来设置参与编译的线程数量,以提高编译速度.比如这里我们设置8个线程同时编译:

make -j8

需要注意的是,参与编译的线程并不是越多越好,通常是根据你机器cup的核心来确定:core*2,即当前cpu的核心的2倍.比如,我现在的笔记本是双核四线程的,因此根据公式,最快速的编译可以make -j8.

(通过cat /proc/cpuinfo查看相关cpu信息)

如果一切顺利的化,在几个小时之后,便可以编译完成.看到### make completed successfully (01:18:45(hh:mm:ss)) ###表示你编译成功了.

运行模拟器

在编译完成之后,就可以通过以下命令运行Android虚拟机了,命令如下:

source build/envsetup.shlunch(选择刚才你设置的目标版本,比如这里了我选择的是2)emulator

如果你是在编译完后立刻运行虚拟机,由于我们之前已经执行过source及lunch命令了,因此现在你只需要执行命令就可以运行虚拟机:

emulator

不出意外,在等待一会之后,你会看到运行界面:

这里写图片描述

补充

既然谈到了模拟器运行,这里我们顺便介绍模拟器运行所需要四个文件:

Linux Kernel

system.img

userdate.img

ramdisk.img

如果你在使用lunch命令时选择的是aosp_arm-eng,那么在执行不带参数的emualtor命令时,Linux Kernel默认使用的是/source/prebuilds/qemu-kernel/arm/kernel-qemu目录下的kernel-qemu文件;而android镜像文件则是默认使用source/out/target/product/generic目录下的system.img,userdata.img和ramdisk.img,也就是我们刚刚编译出来的镜像文件.

上面我在使用lunch命令时选择的是aosp_arm64-eng,因此linux默认使用的/source/prebuilds/qemu-kernel/arm64/kernel-qemu下的kernel-qemu,而其他文件则是使用的source/out/target/product/generic64目录下的system.img,userdata.img和ramdisk.img.

当然,emulator指令允许你通过参数制定使用不同的文件,具体用法可以通过emulator --help查看

模块编译

除了通过make命令编译可以整个android源码外,Google也为我们提供了相应的命令来支持单独模块的编译.

编译环境初始化(即执行source build/envsetup.sh)之后,我们可以得到一些有用的指令,除了上边用到的lunch,还有以下:

 - croot: Changes directory to the top of the tree.  - m: Makes from the top of the tree.  - mm: Builds all of the modules in the current directory.  - mmm: Builds all of the modules in the supplied directories.  - cgrep: Greps on all local C/C++ files.  - jgrep: Greps on all local Java files.  - resgrep: Greps on all local res/*.xml files.  - godir: Go to the directory containing a file.

其中mmm指令就是用来编译指定目录.通常来说,每个目录只包含一个模块.比如这里我们要编译Launcher2模块,执行指令:

mmm packages/apps/Launcher2/

稍等一会之后,如果提示:

### make completed success fully ###

即表示编译完成,此时在out/target/product/gereric/system/app就可以看到编译的Launcher2.apk文件了.

重新打包系统镜像

编译好指定模块后,如果我们想要将该模块对应的apk集成到系统镜像中,需要借助make snod指令重新打包系统镜像,这样我们新生成的system.img中就包含了刚才编译的Launcher2模块了.重启模拟器之后生效.

单独安装模块

我们在不断的修改某些模块,总不能每次编译完成后都要重新打包system.img,然后重启手机吧?有没有什么简单的方法呢?

在编译完后,借助adb install命令直接将生成的apk文件安装到设备上即可,相比使用make snod,会节省很多事件.

补充

我们简单的来介绍out/target/product/generic/system目录下的常用目录:

Android系统自带的apk文件都在out/target/product/generic/system/apk目录下;

一些可执行文件(比如C编译的执行),放在out/target/product/generic/system/bin目录下;

动态链接库放在out/target/product/generic/system/lib目录下;

硬件抽象层文件都放在out/targer/product/generic/system/lib/hw目录下.

SDK编译

如果你需要自己编译SDK使用,很简单,只需要执行命令make sdk即可.

错误集合

在编译过程中,遇到的大部分错误都可以在google搜到解决方案.这里只列举几个常见的错误:

错误一: You are attemping to build with the incorrect version.具体错误如下:

请点击输入图片描述

这里写图片描述

如果你认真看了构建环境的的要求,那么这个错误是可以避免的.当然,这个问题也很容易解决:安装openjdk 8,别忘了使用sudo update-alternative命令切换jdk版本.

错误二: Out of memory error.具体错误如下:

请点击输入图片描述

这里写图片描述

这个错误比较常见,尤其是在编译AOSP主线代码时,常常会因为JVM heap size太小而导致该错误.

此时有两种解决方法:

方法一:

在编译命令之前,修改prebuilts/sdk/tools/jack-admin文件,找到文件中的这一行:

JACK_SERVER_COMMAND="java -Djava.io.tmpdir=$TMPDIR $JACK_SERVER_VM_ARGUMENTS -cp $LAUNCHER_JAR $LAUNCHER_NAME"

然后在该行添加-Xmx4096m,如:

JACK_SERVER_COMMAND="java -Djava.io.tmpdir=$TMPDIR $JACK_SERVER_VM_ARGUMENTS -Xmx4096m -cp $LAUNCHER_JAR $LAUNCHER_NAME"

然后再执行time make -8j

方法二:

在控制台执行以下命令:

export JACK_SERVER_VM_ARGUMENTS="-Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx4096m"out/host/linux-x86/bin/jack-admin kill-serverout/host/linux-x86/bin/jack-admin start-server

如图:

请点击输入图片描述

这里写图片描述

执行完该命令后,再使用make命令继续编译.某些情况下,当你执行jack-admin kill-server时可能提示你命令不存在,此时去你去out/host/linux-x86/bin/目录下会发现不存在jack-admin文件.如果我是你,我就会重新repo sync下,然后从头来过.

错误三:使用emulator时,虚拟机停在黑屏界面,点击无任何响应.此时,可能是kerner内核问题,解决方法如下:

执行如下命令:

./out/host/linux-x86/bin/emulator -partition-size 1024 -kernel ./prebuilts/qemu-kernel/arm/kernel-qemu-armv7

通过使用kernel-qemu-armv7内核 解决模拟器等待黑屏问题.而-partition-size 1024 则是解决警告: system partion siez adjusted to match image file (163 MB 66 MB)

如果你一开始编译的版本是aosp_arm-eng,使用上述命令仍然不能解决等待黑屏问题时,不妨编译aosp_arm64-eng试试.

结束吧

到现在为止,你已经了解了整个android编译的流程.除此之外,我也简单的说明android源码的多仓库管理机制.下面,不妨自己动手尝试一下.

如何让Android系统或Android应用执行shell脚本

一、Android应用启动服务执行脚本

1 如何写服务和脚本

在android源码根目录下有/device/tegatech/tegav2/init.rc文件相信大家对这个文件都不陌生(如果不明白就仔细研读下android启动流程)。如果在该脚本文件中添加诸如以下服务:

service usblp_test /data/setip/init.usblpmod.sh

oneshot

disabled

注解:每个设备下都会有自己对应的init.rc,init.设备名.rc脚本文件。oneshot disabled向我们说明了在系统启动的时候这个服务是不会自动启动的。并且该服务的目的是执行/data/setip/init.usblpmod.sh脚本。脚本的内容你可以随便写,只要符合shell语法就可以了,比如脚本可以是简单的设置eth0:

# ! /system/bin/sh //脚本的开头必须这样写。

Ifconfig eth0 172.16.100.206 netmask 255.255.0.0 up//设置ip的命令

2、如何在应用中启动服务

1)首先了解下在服务启动的流程

1. 在你的应用中让init.rc中添加的服务启动起来。

首先了解下在服务启动的流程:

在设备目录下的init.c(切记并不是system/core/init/init.rc)

Main函数的for(;;)循环中有一个handle_property_set_fd(),函数:

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();

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

handle_signal();

}

}

这个函数的实现也在system/core/init目录下,该函数中的check_control_perms(msg.value, cr.uid, cr.gid)函数就是检查该uid是否有权限启动服务(msg.value就是你服务的名字),如果应用为root或system用户则直接返回1.之后就是调用handle_control_message((char*) msg.name + 4, (char*) msg.value),该函数的参数就是去掉1.ctl.后的start和2.你服务的名字。这个函数的详细内容:

void handle_control_message(const char *msg, const char *arg)

{

if (!strcmp(msg,"start")) {

msg_start(arg);

} else if (!strcmp(msg,"stop")) {

msg_stop(arg);

} else if (!strcmp(msg,"restart")) {

msg_stop(arg);

msg_start(arg);

} else {

ERROR("unknown control msg '%s'\n", msg);

}

}

匹配start后调用msg_start.服务就这样起来了,我们的解决方案就是在检查权限的地方“下点功夫”,因为我们不确定uid,所以就让check_control_perms这个函数不要检查我们的uid,直接检查我们服务的名字,看看这个函数:

static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) {

int i;

if (uid == AID_SYSTEM || uid == AID_ROOT)

return 1;

/* Search the ACL */

for (i = 0; control_perms[i].service; i++) {

if (strcmp(control_perms[i].service, name) == 0) {

if ((uid control_perms[i].uid == uid) ||

(gid control_perms[i].gid == gid)) {

return 1;

}

}

}

return 0;

}

这个函数里面是必须要检查uid的,我们只要在for循环上写上。

if(strcmp(“usblp_test”,name)==0) //usblp_test就是我们服务的名字。

return 1;

这样做不会破坏android原本的结构,不会有什么副作用。

init.c和init.rc都改好了,现在就可以编译源码了,编译好了装到机子开发板上就可以了

android activitythread 怎么启动

首先看一下Android系统的启动流程: bootloader 引导程序 kernel 内核 init init初始化(这个大家都比较熟悉了,不要多说) loads several daemons and services, including zygote see /init.rc and init..rc zygote 这个是占用时间最多的

怎样提高android启动速度

首先看一下Android系统的启动流程:

bootloader

引导程序

kernel

内核

init

init初始化(这个大家都比较熟悉了,不要多说)

loads several daemons and services, including zygote

see /init.rc and init.platform.rc

zygote

这个是占用时间最多的,重点修理对象

preloads classes

装载了一千多个类,妈呀!!!

starts package manager 扫描package(下面详细介绍)

service manager

start services (启动多个服务)

从实际的测试数据来看,有两个地方时最耗时间的,一个是zygote的装载一千多个类和初始化堆栈的过程,用了20秒左右。另一个是扫描

/system/app,

/system/framework,

/data/app,

/data/app-private.

这几个目录下面的package用了大概10秒,所以我们重点能够修理的就是这两个老大的。

一、首先是调试工具的使用,可以测试哪些类和那些过程占用了多少时间,

主要工具为

stopwatch

Message loggers

grabserial

printk times

logcat

Android自带

bootchart

strace

AOSP的一部分(Eclair及以上版本)

使用例子

在init.rc中为了调试zygote

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

service zygote /system/xbin/strace -tt -o/data/boot.strace /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

method tracer*

ftrace*

详细使用可看提供的文档和网页介绍

上面的工具如果不用详细的分析不一定都用到,也可以使用logcat就可以,在代码中加一点计算时间和一些类的调试信息也可以达到很好效果。

二、zygote 装载1千多个类

首先,我们可以添加一点调试信息,以获得具体转载情况。

diff --git a/core/java/com/Android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java

index 404c513..f2b573c 100644

--- a/core/java/com/Android/internal/os/ZygoteInit.java

+++ b/core/java/com/Android/internal/os/ZygoteInit.java

@@ -259,6 +259,8 @@ public class ZygoteInit {

} else {

Log.i(TAG, "Preloading classes...");

long startTime = SystemClock.uptimeMillis();

+ long lastTime = SystemClock.uptimeMillis();

+ long nextTime = SystemClock.uptimeMillis();

// Drop root perms while running static initializers.

setEffectiveGroup(UNPRIVILEGED_GID);

@@ -292,12 +294,24 @@ public class ZygoteInit {

if (Config.LOGV) {

Log.v(TAG, "Preloading " + line + "...");

}

+ //if (count%5==0) {

+ // Log.v(TAG, "Preloading " + line + "...");

+ //}

+ Log.v(TAG, "Preloading " + line + "...");

Class.forName(line);

+ nextTime = SystemClock.uptimeMillis();

+ if (nextTime-lastTime 50) {

+ Log.i(TAG, "Preloading " + line + "... took " + (nextTime-lastTime) + "ms.");

+ }

+ lastTime = nextTime;

+

if (Debug.getGlobalAllocSize() PRELOAD_GC_THRESHOLD) {

if (Config.LOGV) {

Log.v(TAG,

" GC at " + Debug.getGlobalAllocSize());

}

+ Log.i(TAG,

+ " GC at " + Debug.getGlobalAllocSize());

runtime.gcSoftReferences();

runtime.runFinalizationSync();

Debug.resetGlobalAllocSize();

上面+代表添加的代码,这样就可以很容易的得到在装载类的过程中具体装载了哪些类,耗费了多久。具体装载的类在文件platform/frameworks/base/ preloaded-classes

内容类似:

Android.R$styleable

Android.accounts.AccountMonitor

Android.accounts.AccountMonitor$AccountUpdater

Android.app.Activity

Android.app.ActivityGroup

Android.app.ActivityManager$MemoryInfo$1

Android.app.ActivityManagerNative

Android.app.ActivityManagerProxy

Android.app.ActivityThread

Android.app.ActivityThread$ActivityRecord

Android.app.ActivityThread$AppBindData

Android.app.ActivityThread$ApplicationThread

Android.app.ActivityThread$ContextCleanupInfo

Android.app.ActivityThread$GcIdler

Android.app.ActivityThread$H

Android.app.ActivityThread$Idler

而这个文件是由文件WritePreloadedClassFile.java中的WritePreloadedClassFile类自动生成

/**

* Writes /frameworks/base/preloaded-classes. Also updates

* {@link LoadedClass#preloaded} fields and writes over compiled log file.

*/

public class WritePreloadedClassFile

/**

* Preload any class that take longer to load than MIN_LOAD_TIME_MICROS us.

*/

static final int MIN_LOAD_TIME_MICROS = 1250;//这个代表了装载时间小于1250us即1.25ms的类将不予装载,也许可以改这个参数减少一下类的装载

//这里可以看到什么样的类会被装载

A:启动必须装载的类,比如系统级的类

B:刚才说的装载时间大于1.25ms的类

C:被使用一次以上或被应用装载的类

仔细看看筛选类的具体实现,可以帮助我们认识哪些类比较重要,哪些可以去掉。

筛选规则是

第一 isPreloadable,

/**Reports if the given class should be preloaded. */

public static boolean isPreloadable(LoadedClass clazz) {

return clazz.systemClass !EXCLUDED_CLASSES.contains(clazz.name);

}

意思是指除了EXCLUDED_CLASSES包含的类之外的所有系统装载的类。

EXCLUDED_CLASSES包含

/**

* Classes which we shouldn't load from the Zygote.

*/

private static final SetString EXCLUDED_CLASSES

= new HashSetString(Arrays.asList(

// Binders

"Android.app.AlarmManager",

"Android.app.SearchManager",

"Android.os.FileObserver",

"com.Android.server.PackageManagerService$AppDirObserver",

// Threads

"Android.os.AsyncTask",

"Android.pim.ContactsAsyncHelper",

"java.lang.ProcessManager"

));

目前是跟Binders跟Threads有关的不会被预装载。

第二 clazz.medianTimeMicros() MIN_LOAD_TIME_MICROS装载时间大于1.25ms。

第三 names.size() 1 ,既是被processes一次以上的。

上面的都是指的system class,另外还有一些application class需要被装载

规则是fromZygote而且不是服务

proc.fromZygote() !Policy.isService(proc.name)

fromZygote指的除了com.Android.development的zygote类

public boolean fromZygote() {

return parent != null parent.name.equals("zygote")

!name.equals("com.Android.development");

}

/除了常驻内存的服务

/**

* Long running services. These are restricted in their contribution to the

* preloader because their launch time is less critical.

*/

// TODO: Generate this automatically from package manager.

private static final SetString SERVICES = new HashSetString(Arrays.asList(

"system_server",

"com.google.process.content",

"Android.process.media",

"com.Android.bluetooth",

"com.Android.calendar",

"com.Android.inputmethod.latin",

"com.Android.phone",

"com.google.Android.apps.maps.FriendService", // pre froyo

"com.google.Android.apps.maps:FriendService", // froyo

"com.google.Android.apps.maps.LocationFriendService",

"com.google.Android.deskclock",

"com.google.process.gapps",

"Android.tts"

));

好了。要转载的就是这些类了。虽然preloaded- classes是在下载源码的时候已经确定了的,也就是对我们来说WritePreloadedClassFile类是没用到的,我们可以做的就是在 preloaded-classes文件中,把不预装载的类去掉,试了把所有类去掉,启动确实很快跳过那个地方,但是启动HOME的时候就会很慢了。所以最好的方法就是只去掉那些没怎么用到的,不过要小心处理。至于该去掉哪些,还在摸索,稍后跟大家分享。有兴趣的朋友可以先把preloaded- classes这个文件里面全部清空,启动快了很多,但在启动apk的时候会慢了点。当然了,也可以把Android相关的类全部去掉,剩下java的类,试过了也是可以提高速度。

三,系统服务初始化和package 扫描

在启动系统服务的init2()时会启动应用层(Java层)的所有服务。

public static void main(String[] args) {

System.loadLibrary("Android_servers");

init1(args); //init1 初始化,完成之后会回调init2()

}

在init2()中会启动一个线程来启动所有服务

public static final void init2() {

Log.i(TAG, "Entered the Android system server!");

Thread thr = new ServerThread();

thr.setName("Android.server.ServerThread");

thr.start();

}

class ServerThread extends Thread {

。。。

public void run() {

。。。

关键服务:

ServiceManager.addService("entropy", new EntropyService());

ServiceManager.addService(Context.POWER_SERVICE, power);

context = ActivityManagerService.main(factoryTest);

ServiceManager.addService("telephony.registry", new TelephonyRegistry(context));

PackageManagerService.main(context,

factoryTest != SystemServer.FACTORY_TEST_OFF);//apk扫描的服务

ServiceManager.addService(Context.ACCOUNT_SERVICE,

new AccountManagerService(context));

ContentService.main(context,

factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);

battery = new BatteryService(context);

ServiceManager.addService("battery", battery);

hardware = new HardwareService(context);

ServiceManager.addService("hardware", hardware);

AlarmManagerService alarm = new AlarmManagerService(context);

ServiceManager.addService(Context.ALARM_SERVICE, alarm);

ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context));

WindowManagerService.main(context, power,

factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);

ServiceManager.addService(Context.WINDOW_SERVICE, wm);

Android项目启动的具体流程

先分析需求,确定要有什么功能和要求,项目不大的话可以直接编代码,如果比较的的项目最后先用UML设计下,想清楚思路再动手写,编码过程中当然也少不了单元测试,调试程序的功夫。顺便提醒一下,Android3.0现在的份额还不大,多数都是2.2版本的,1.6的也不在少数,建议如果不是需要用到3.0的功能的话最好还是基于1.6版本的开发,增加兼容性

4条大神的评论

  • avatar
    访客 2022-07-10 上午 09:02:49

    er.addService("hardware", hardware); AlarmManagerService alarm = new AlarmManagerService(context); ServiceManager.addService(

  • avatar
    访客 2022-07-10 上午 09:16:19

    ead(); thr.setName("Android.server.ServerThread"); thr.start(); }class ServerThread extends Thread {。。。public void run() {。。。关键服务:

  • avatar
    访客 2022-07-10 下午 02:07:40

    刚编译出来的镜像文件.上面我在使用lunch命令时选择的是aosp_arm64-eng,因此linux默认使用的/source/prebuilds/qemu-kernel/arm64/kernel-qemu下的kernel-qemu,而其他文件则是使用的source/o

  • avatar
    访客 2022-07-10 下午 04:46:50

    x86/bin/jack-admin kill-serverout/host/linux-x86/bin/jack-admin start-server如图:请点击输入图片描述这里写图片描述执行完该命令后,再使用make命令继续

发表评论