前言

本文是通过学习了卢俊系列博客做的一篇学习记录文章。记录个人觉得比较有用的部分内容,该系列博文主要讲解了Android系统的开机过程和优化的分析处理。最近也在处理开机优化的问题,特此记录之。

Android内核开发:序

Android内核开发:开发板选购

Android内核开发:理解和掌握repo工具

下载manifest仓库

repo init -u https://android.googlesource.com/platform/manifest
如果要选择特定版本的Android源码,或者在已下载的源码基础上切换到其他版本,则可以使用-b选项:
repo init -u git://git.omapzoom.org/platform/manifest.git -b android-5.0.2_r1
然后使用repo sync同步代码
repo sync

mainfest.xml文件清单

上面提到了repo init需要初始化一个manifest仓库,仓库中含有一个很重要的manifest.xml文件清单,其实这个manifest.xml并不复杂的,它就是用XML文件的格式记录了本项目依赖的各个Git仓库的名称、地址,以及分支等信息。常用的元素如下所示:

  • manifest 最顶层的XML元素
  • remote 设置远程git服务器的属性,如名称、根URL地址等
  • project 需要clone的Git仓库,path表示本机路径,name表示远程版本库的相对路径
  • copyfile 执行拷贝操作,把URL/src地址的文件拷贝到./dest

repo几个常用的命令

  • repo init // 初始化repo仓库
  • repo sync // 下载源码
  • repo start // 创建分支
  • repo checkout //切换分支
  • repo branches //查看分支
  • repo status //查看文件状态

Android内核开发:源码的版本与分支详解

查看android源码分支和版本

git –git-dir .repo/manifests/.git/ branch -a

或者

cd .repo/manifests
git branch -a | cut -d / -f 3

如果要切换的其他分支,执行下面的命令即可

repo init -b android-4.2.2_r1
repo sync

Android内核开发:系统编译输出的镜像文件

Android内核开发:系统分区与镜像文件的烧写

Android内核开发:图解Android系统的启动过程

主要分析下init程序,它是分析Android启动过程中最核心的程序。
代码路径:system/core/init/init.c
init程序最核心的工作主要有3点:
1. 创建和挂载一些系统目录/设备节点,设置权限,如:/dev, /proc, and /sys
2. 解析 init.rc 和 init.hardware.rc,并启动属性服务,以及一系列的服务和进程。
3. 显示boot logo,默认是“Android”字样

最重要的是第二步,这个过程中会启动系统所需功能的各种服务。主要分为本地服务和Android服务,它们都会在ServiceManager中进行注册。

  • 本地服务
    本地服务是指运行在C++层的系统守护进程,一部分有init进程直接启动,它们定义在init.rc脚本中,还有一部分本地服务是由这些本地服务启动,如果mediaserver服务会启动AudioFlinger,MediaPlayerService,以及CameraService等本地服务。
    每一个由init直接启动的本地服务都是一个独立的Linux进程,可以通过adb shell top查看相应的进程。
  • Android服务
    Android服务是指运行在Dalvik虚拟机进程中的服务。
    init进程会执行app_process程序,创建Zygote进程,它是Android系统最重要的进程,后续所有的Android应用程序都是由它fork出来的。
    Zygote进程会先fork出SystemServer进程,SystemServer进程就会启动所有的Android核心服务。例如:Activity Manager、Window Manager、Power Manager等等服务。当所服务启动完毕后,SystemServer会打印出”Making services ready”,然后通过Activity Manager启动Home界面,并发送“ACTION_BOOT_COMPLETED”广播消息。
    SystemServer进程添加的服务都属于SystemServer进程。

下图为Android系统启动过程:

Android内核书 安卓内核开发_Android

Android内核开发:如何统计系统的启动时间

2个概念:

  1. Android是基于Linux内核的系统,因此Android的启动过程是分为两个阶段的,第一个阶段就是Linux内核的启动,第二个阶段就是Android框架的启动(包括核心服务和程序)。
  2. Android的log系统是独立于Linux内核的log系统的。Linux内核通过printk打印的log信息,这些log写入到了/dev/kmsg文件中,在Shell终端可以通过dmesg命令查看这些log信息。Android框架则是通过Logger驱动打印log信息,这些log并没有归并到kmesg文件中,而是单独存储的,位于/dev/log目录下,在Shell终端可以通过logcat命令来查看。

通过dmesg命令抓取Linux内核的log信息

adb shell dmesg > D:\dmesg.txt

Android系统把Log分为了四类,不同的类别记录不同的Log信息:

main - 主要的Log信息,大部分应用级别的Log信息都在这里
events - 系统事件相关的Log信息
radio - 无线/电话相关的Log信息
system - 低级别的系统调试Log信息

logcat默认抓取的是main信息,如果想抓取指定类别的log信息的方法,在logcat命令后加-b参数,例如:

$ adb logcat -d -v time -b "main"   >  D:\main.txt
$ adb logcat -d -v time -b "events" >  D:\events.txt
$ adb logcat -d -v time -b "system" >  D:\system.txt
$ adb logcat -d -v time -b "radio"  >  D:\radio.txt

查看系统启动时间

adb logcat -d -b events | grep “boot”

Android内核开发:学会分析系统的启动log

Android内核书 安卓内核开发_内核开发_02

通过dmesg抓取log信息

adb shell dmesg > D:\dmesg.txt

在dmesg.txt中搜索几个关键字
Zygote、system_server、mediaserver等关键字可以获取相关模块的开机耗时信息。

Android内核开发:系统启动速度优化

Android系统的启动优化主要分为三大部分:
1. Bootloader优化
2. Linux Kernel的剪裁与优化
3. Android OS部分的剪裁与优化

下面主要讲Android os部分的优化
1. 精简preload的classes和resource
frameworks/base/preload-classes
frameworks/base/core/res/res/values/arrays.xml
2. 精简native service和java service
system/core/rootdir/init.rc
frameworks/base/services/java/com/android/server/SystemServer.java
3. 精简预装的apk文件
build/target/product/xxxx.mk
device///xxxx.mk
vendor/…./xxxx.mk
4. 减少内核的log打印级别
system/core/rootdir/init.rc
5. 其他优化手段
- 优化启动动画,降低帧率和图片尺寸
- 精简系统,减小boot.img文件大小,可以显著减少启动过程中加载和解压boot.img的时间
- 预先创建一些目录和文件,而不是在init过程中创建

Android内核开发:浅析APK的安装过程

Android内核开发:从源码树中删除出厂的app应用

Android内核开发:在源码树中添加新的app应用

  • 不带jni本地代码
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := HelloWorld
include $(BUILD_PACKAGE)
  • 含有jni本地代码
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := HelloWorld
LOCAL_JNI_SHARED_LIBRARIES := libmynative
include $(BUILD_PACKAGE)

include $(call all-makefiles-under,$(LOCAL_PATH))

JNI目录下的Android.mk文件(假设jni目录下有inc和src目录)

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := libmynative
LOCAL_SRC_FILES := src/mynative.c
LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/inc

include $(BUILD_SHARED_LIBRARY)
  • 含有jni本地代码,并且本地代码依赖第三方库(.a或者so)
    假设本地代码依赖的第三方库为:encoder.a 和 decoder.so
    修改上述jni目录下的Android.mk
LOCAL_STATIC_LIBRARIES := libencoder
LOCAL_SHARED_LIBRARIES := libdecoder

修改HelloWorld目录下的Android.mk文件(encoder.a 和 decoder.so 都拷贝到工程根目录)

include $(CLEAR_VARS)
LOCAL_MODULE := libencoder
LOCAL_SRC_FILES := encoder.a
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE_SUFFIX := .a
include $(BUILD_PREBUILT)

include $(CLEAR_VARS)
LOCAL_MODULE := libdecoder
LOCAL_SRC_FILES := decoder.so
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
include $(BUILD_PREBUILT)

Android内核开发:为什么刷机后系统第一次启动会很慢?