深入浅出 - Android系统移植与平台开发(六)- 为Android启动加速
作者:唐老师,华清远见嵌入式学院讲师。
Android的启动速度一直以来是他的诟病,虽然现在Android设备的硬件速度越来越快,但是随着新 版本的出现,其启动速度一直都比较慢,当然,作为程序员,我们是可以理解的,智能手机本身就要启动很多服务来支持应用的功能,并且Android系统大部 分还是使用的Java语言编写的,必然要运行在Java虚拟机里,这也决定了它运行速度和启动速度都相对较慢。同时,Android系统为了在执行用户应 用程序时提高用户体验,还做了一些预加载机制,这也牺牲了开机启动时间。
根据Android启动过程的分析可知,我们可以进行定制加速如下:
      Ø定制本地服务
      Ø定制Android系统服务
      Ø优化ZygoteInit的类预加载preloadClasses和资源预加载preloadResources机制
      ØPackageManagerService扫描、检查APK安装包信息
下面我们依次来分析其实现步骤。
1. 定制本地服务
由第二章,第2节可知,本地服务都是由C或C++编写,它们都执行在Linux空间,在init进程的启动过程中启动了很多本地服务,如果我们的设备中没有电话模块、蓝牙模块,我们可以将这些没用的本地服务在init.rc里注释掉。
[cpp] view plaincopyprint?
1.	396 #service ril-daemon /system/bin/rild 
 
                      2.	397#        class main  
                      3.	398#        socket rild stream 660 root radio  
                      4.	399#        socket rild-debug stream 660 radio system  
                      5.	400#        user root  
                      6.	401#        group radio cache inet misc audio sdcard_rw log  
                      7.	  
                      8.	435 service dbus /system/bin/dbus-daemon --system --nofork  
                      9.	436        class main  
                      10.	437 #        socket dbus stream 660 bluetooth bluetooth  
                      11.	438        user bluetooth  
                      12.	439        group bluetooth net_bt_admin  
                      13.	  
                      14.	 441 #service bluetoothd /system/bin/bluetoothd -n  
                      15.	442 #        class main  
                      16.	443 #        socket bluetooth stream 660 bluetooth bluetooth  
                      17.	444 #        socket dbus_bluetooth stream 660 bluetooth bluetooth  
                      18.	445        # init.rc does not yet support applying capabilities, so run as root and  
                      19.	446        # let bluetoothd drop uid to bluetooth with the right linux capabilities  
                      20.	447 #        group bluetooth net_bt_admin misc  
                      21.	448 #        disabled
重新编译system.img然后启动模拟器即可。
笔者做了对比,去掉上述几个本地服务与没有去掉本地服务,二者在开机时间上几乎没有减少多少,这也可以理解,因为本地服务就是几个程序,少执行和多执行几个程序对于总体开机时间没有多大影响,不过,去掉没有使用的本地服务,对整个系统性能来说,会有微不足道的提升。
如果去掉开机动画服务可以大大的提高系统的启动速度:
[cpp] view plaincopyprint?
1.	433 #service bootanim /system/bin/bootanimation  
                      2.	434 #        class main  
                      3.	435 #        user graphics  
                      4.	436 #        group graphics  
                      5.	437 #        disabled  
                      6.	438 #        oneshot
2. 定制Android系统服务
由Android的启动过程可知,init进程启动了app_process作为zygote,在
app_process里启动了Dalvik虚拟机,然后加载执行了第一个Java程序ZygoteInit作为Dalvik主线程,在
ZygoteInit里fork了第一个Java程序SystemServer,在SystemServer里启动了大量的Android的核心服务,通
常来说这些服务一般不要去动,如果我们的设备里没有使用过某些服务,并且将来也明确不使用,可以将其去掉。
我们先来看看SystemServer启动了哪些Android服务:
[plain] view plaincopyprint?
1.	EntropyService:熵(shang)服务,用于产生随机数  
                      2.	PowerManagerService:电源管理服务  
                      3.	ActivityManagerService:最核心服务之一,Activity管理服务  
                      4.	TelephonyRegistry:电话服务,电话底层通知服务  
                      5.	PackageManagerService:程序包管理服务  
                      6.	AccountManagerService:联系人帐户管理服务  
                      7.	ContentService:内容提供器的服务,提供跨进程数据交换  
                      8.	LightsService:光感应传感器服务  
                      9.	BatteryService:电池服务,当电量不足时发广播  
                      10.	VibratorService:震动器服务  
                      11.	AlarmManagerService:闹钟服务  
                      12.	WindowManagerService:窗口管理服务  
                      13.	BluetoothService:蓝牙服务  
                      14.	InputMethodManagerService:输入法服务,打开关闭输入法  
                      15.	AccessibilityManagerService:辅助管理程序截获所有的用户输入,并根据这些输入给用户一些额外的反馈,起到辅助的效果,View的点击、焦点等事件分发管理服务  
                      16.	DevicePolicyManagerService:提供一些系统级别的设置及属性  
                      17.	StatusBarManagerService:状态栏管理服务  
                      18.	ClipboardService:粘贴板服务  
                      19.	NetworkManagementService:手机网络管理服务  
                      20.	TextServicesManagerService:  
                      21.	NetworkStatsService:手机网络状态服务  
                      22.	NetworkPolicyManagerService:  
                      23.	WifiP2pService:Wifi点对点直联服务  
                      24.	WifiService:WIFI服务  
                      25.	ConnectivityService:网络连接状态服务  
                      26.	ThrottleService:modem节流阀控制服务  
                      27.	MountService:磁盘加载服务,通常也mountd和vold服务结合  
                      28.	NotificationManagerService:通知管理服务,通常和StatusBarManagerService  
                      29.	DeviceStorageMonitorService:存储设备容量监听服务  
                      30.	LocationManagerService:位置管理服务  
                      31.	CountryDetectorService:检查当前用户所在的国家  
                      32.	SearchManagerService:搜索管理服务  
                      33.	DropBoxManagerService:系统日志文件管理服务(大部分程序错误信息)  
                      34.	WallpaperManagerService:壁纸管理服务  
                      35.	AudioService:AudioFlinger上层的封装的音量控制管理服务  
                      36.	UsbService:USB Host和device管理服务  
                      37.	UiModeManagerService:UI模式管理服务,监听车载、座机等场合下UI的变化  
                      38.	BackupManagerService:备份服务  
                      39.	AppWidgetService:应用桌面部件服务  
                      40.	RecognitionManagerService:身份识别服务  
                      41.	DiskStatsService:磁盘统计服务  
                      42.	SamplingProfilerService:性能统计服务
 
                      43.	NetworkTimeUpdateService:网络时间更新服务
天啊,43个Android服务,
      除了对原生的Android系统定制外,还可以通过引入外部技术来提升开机速度,目前有人使用Berkeley Lab
Checkpoint/Restart(BLCR)技术给系统做一个快照,类似于Vmware里用的SnapeShot和Windows里的休眠机制,它
能将当前Android环境保存在一个文件里,当下次启动的时候从该文件恢复Android运行环境,这种方式可以明显的提升启动速度,其实现过程比较复
杂。
      BLCR技术细节后续再完善。。。
3. PackageManagerService
PackageManagerService是Android的包管理器,主要用来管理Android系统中的APK文件,在SystemServer里,通过ServerThread调用PackageManagerService.main()启动了包管理服务。
PackageManagerService的主要作用有:
3.1 @PackageManagerService.main()
生成一个IPackageManager接口,也就是PackageManagerService。
[cpp] view plaincopyprint?
1.	public static final IPackageManager main(Context context, boolean factoryTest) {  
                      2.              PackageManagerService m = new PackageManagerService(context, factoryTest);  
                      3.              ServiceManager.addService("package", m);  
                      4.              return m;  
                      5.	}
3.2 PackageManagerService构造方法中,首先会进行一些成员变量的初始化,比如mContext, mFactoryTest, mMetrics, mSettings等。
最重要的是初始化mInstaller这个变量。Installer是一个很重要的类,所有对apk的install, uninstall,move等操作,都是通过它进行的。
[cpp] view plaincopyprint?
1.	Installer installer = new Installer();  
                      2.              if (installer.ping() &&Process.supportsProcesses()) {  
                      3.                            mInstaller = installer;  
                      4.              } else {  
                      5.                            mInstaller = null;  
                      6.              }
3.3 建立PackageHandler消息循环,用于处理外部的apk安装请求消息,如adb install,packageinstaller安装apk时会发送消息
[cpp] view plaincopyprint?
1.	mHandlerThread.start(); 
 
                      2.              mHandler = new PackageHandler(mHandlerThread.getLooper());
3.4 解析/system/etc/permission下xml文件(framework/base/data/etc/),包括platform.xml和系统支持的各种硬件模块的feature。
主要工作:
      (1)建立底层user ids和group ids 同上层permissions之间的映射;可以指定一个权限与几个组ID对应。当一个APK被授予这个权限时,它也同时属于这几个组。
      (2)给一些底层用户分配权限,如给shell授予各种permission权限;把一个权限赋予一个UID,当进程使用这个UID运行时,就具备了这个权限。
      (3) library,系统增加的一些应用需要link的扩展jar库;
      (4) feature,系统每增加一个硬件,都要添加相应的feature.将解析结果放入
mSystemPermissions,mSharedLibraries,mSettings.mPermissions,mAvailableFeatures等几个集合中供系统查询和权限配置使用。
3.5
检查/data/system/packages.xml是否存在,这个文件是在解析apk时由writeLP()创建的,里面记录了系统的
permissions,以及每个apk的name,codePath,flags,ts,version,uesrid等信息,这些信息主要通过apk
的AndroidManifest.xml解析获取,解析完apk后将更新信息写入这个文件并保存到flash,下次开机直接从里面读取相关信息添加到内
存相关列表中。当有apk升级,安装或删除时会更新这个文件。
3.6 检查BootClassPath,mSharedLibraries及/system/framework下的jar是否需要dexopt,需要的则通过dexopt进行优化。
3.7
启动AppDirObserver线程监测/system/framework,/system/app,/data/app,/data/app-
private目录的事件,主要监听add和remove事件。对于目录监听底层通过inotify机制实现,inotify
是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知,它为用户态监视文件系统的变化提供了强大的支持。
- 当有add event时调用scanPackageLI(File , int , int)处理;
- 当有remove event时调用removePackageLI()处理;
3.8
对于以上几个目录下的apk逐个解析,主要是解析每个apk的AndroidManifest.xml文件,处理asset/res等资源文件,建立起每
个apk的配置结构信息,并将每个apk的配置信息添加到全局列表进行管理。调用installer.install()进行安装工作,检查apk里的
dex文件是否需要再优化,如果需要优化则通过辅助工具dexopt进行优化处理;将解析出的componet添加到pkg的对应列表里;
对apk进行签名和证书校验,进行完整性验证。
3.9
将解析的每个apk的信息保存到packages.xml和packages.list文件里,packages.list记录了如下数
据:pkgName,userId,debugFlag,dataPath(包的数据路径)
其中对/system/framework,/system/app,/data/app,/data/app-private目录中的APK扫描耗费了
大量的时间,通过下面的笔者的实验结果可以看得出来。

无优化时的APK总计有50个,减少APK时APK只有5个,包扫描的时间减少了4秒,Android服务启动的时间减少了近10秒,整个系统的启动时间
减少了15秒左右在减少APK的情况下,然后将开机动画关闭了,本地服务启动时间略有减少,预加载类与资源时减少了近10秒,包扫描时间减少了0.5秒左
右,Android服务启动时间减少近4秒,整个系统启动时间减少了近15秒。
由此可见,减少APK的数量关闭开机动画可以明显的提高Android的启动速度。
至于关闭其它本地服务对启动时间影响不大的原因,我的感觉是本地服务注册到Binder之后就等待框
架客户端的连接,而关闭开机动画对启动速度影响之大,通过分析bootanim可知,在开启动画时要开启一个线程,mask图片在前面作为幕
布,shine图片在后面每隔一定时间重新绘制一次,每次绘制时通过调用OPENGL的api调用2D加速来绘制图片,而我们使用的模拟器,全部操作通过
软件来模拟,所以对系统怕启动速度影响比较大。如果在具体的设备上执行对启动速度的影响应该比这要小,具体硬件设备上优化,后续会有博文推出。
4. Android系统企业级定制
在做Android产品开发的过程中,经常会有客户说:“我们不想让我们的产品开机后看到Android系
统的桌面,我们希望设备开机会直接进入我们自己的程序“。“我们的产品就是直接面对最终用户的,其它的无关的功能不需要。”比如说,之前我们开发的一款社
区医疗软件,通过蓝牙和心电仪、血糖仪等采集外设通信,客户要求开机进入医疗软件,通过底层蓝牙通信,我们采用的就是定制的Android平板的桌面。
通常定制应用程序开机启动有两种实现方式:
      Ø 接收系统启动广播:BOOT_COMPLETED,在广播接收器代码里实现应用程序的启动。
      Ø 编写一个桌面程序,替换掉系统默认的Launcher应用。
第一种方式实现简单,但是这个应用程序是在Android系统桌面出现之后再启动的,不是真正意义上的产品定制,并且,如果当前的应用程序崩溃了退出后,会回到桌面上,当然我们可以实现崩溃后再次重新启动,但是,这终究不是真正意义上的定制。
第二种实现方式思路:当系统启动完毕之后,系统会发出一个Action为
android.intent.action.MAIN,category为android.intent.category.HOME的Intent,
默认系统的Launcher桌面会响应这个Intent,那么,我们可以编写一个应用程序,让它去响应这个Intent,然后我们将这个应用程序替换掉系
统默认的Launcher桌面。系统重新启动之后,会自动启动我们自己的应用。但是,如果一些非法程序也响应这个Intent,那么照样不能达到客户的目
的。这个时候,我们就要去修改系统框架代码,让Android系统启动完毕之后发送我们自己定义的Intent而不是公开的category为HOME的
 Intent。
思路如下:
      Ø添加一个新的category:android.intent.category.FS_HOME
      Ø将框架里面所有发送android.intent.category.HOME的Intent全部改成android.intent.category.FS_HOME
      category的定义文件在:
      @ frameworks/base/core/java/android/content/Intent.java
[java] view plaincopyprint?
1.	              /** 
                      2.	2219              * This is the home activity, that is the first activity that is displayed 
                      3.	2220              * when the device boots. 
                      4.	2221              */  
                      5.	2222               @SdkConstant(SdkConstantType.INTENT_CATEGORY)  
                      6.	2223               public static final String CATEGORY_HOME = "android.intent.category.HOME";
在CATEGORY_HOME后面添加下面两行代码:
[java] view plaincopyprint?
1.	               @SdkConstant(SdkConstantType.INTENT_CATEGORY) 
 
                      2.	               public static final String CATEGORY_FS_HOME = "android.intent.category.FS_HOME";
通过grep CATEGORY_HOME frameworks/ -R命令查找所有使用CATEGORY_HOME的文件:
[plain] view plaincopyprint?
1.	./frameworks/ex/carousel/test/src/com/android/carouseltest/TaskSwitcherActivity.java  
                      2.	./frameworks/base/services/java/com/android/server/am/ActivityManagerService.java  
                      3.	./frameworks/base/services/java/com/android/server/am/ActivityRecord.java 
                      4.	./frameworks/base/services/java/com/android/server/UiModeManagerService.java  
                      5.	./frameworks/base/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java 
                      6.	./frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java  
                      7.	./frameworks/base/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
                      8.	./frameworks/base/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
将文件里使用CATEGORY_HOME常量的地方全部改为CATEGORY_FS_HOME。
新建一个Android应用:MyLauncher,在其AndroidManifest.xml里,<intent-filter>标签里添加上响应CATEGORY_FS_HOME的代码:
[html] view plaincopyprint?
1.	 <SPAN style="FONT-SIZE: 10px" ><activity android:name=".MyLauncherActivity" > 
 
                      2.              <intent-filter>  
                      3.                            <action android:name="android.intent.action.MAIN"  />  
                      4.                            <category android:name="android.intent.category.FS_HOME"  />  
                      5.                            <category android:name="android.intent.category.DEFAULT"  />  
                      6.              </intent-filter>  
                      7.	 </activity></SPAN>
重新编译frameworks/base、frameworks/ex:
[plain] view plaincopyprint?
1.	$ source build/envsetup.sh
 
                      2.	$ make
重新运行模拟器,使用新编译的Android系统,可以看到系统启动起来就进入我们的MyLauncher应用,从实现实现桌面的替换。

文章来源:华清远见嵌入式学院,原文地址:http://www.embedu.org/Column/Column682.htm
深入浅出 - Android系统移植与平台开发(六)- 为Android启动加速的更多相关文章
- 深入浅出 - Android系统移植与平台开发(一)
		深入浅出 - Android系统移植与平台开发(一) 分类: Android移植2012-09-05 14:16 16173人阅读 评论(12) 收藏 举报 androidgitgooglejdkub ... 
- 深入浅出 - Android系统移植与平台开发(三)- 编译并运行Android4.0模拟器
		作者:唐老师,华清远见嵌入式学院讲师. 1. 编译Android模拟器 在Ubuntu下,我们可以在源码里编译出自己的模拟器及SDK等编译工具,当然这个和在windows里下载的看起来没有什么区别 ... 
- 深入浅出 - Android系统移植与平台开发(五)- 编译Android源码(转)
		2.3编译Android源码 Android源码体积非常庞大,由Dalvik虚拟机.Linux内核.编译系统.框架代码.Android定制C库.测试套件.系统应用程序等部分组成,在编译Android源 ... 
- 深入浅出 - Android系统移植与平台开发(九)- Android系统system_server及Home启动
		3.3 Zygote守护进程与system_server进程 Android的执行环境和Java执行环境有着本质的差别,在Android系统中每一个应用程序都是一独立的进程,当一个进程死掉时,不会影响 ... 
- 深入浅出-Android系统移植与平台开发(一)- Android4.0系统的下载与编译
		作者:唐老师,华清远见嵌入式学院讲师. 一.Android4.0系统的下载与编译 Android系统的下载与编译,Google的官方网站上已经给出了详细的说明,请参照Android的官方网址: htt ... 
- 深入浅出 - Android系统移植与平台开发(十)- Android编译系统与定制Android平台系统(瘋耔修改篇二)
		第四章.Android编译系统与定制Android平台系统 4.1Android编译系统 Android的源码由几十万个文件构成,这些文件之间有的相互依赖,有的又相互独立,它们按功能或类型又被放到不同 ... 
- 深入浅出 - Android系统移植与平台开发(十一)- Android系统的定制(瘋耔修改篇一)
		首先非常感谢原文作者为我们提供的知识库,因为有你们的贡献,我们的开发难度更显简单 原文 : http://blog.csdn.net/mr_raptor/article/details/30113 ... 
- 深入浅出 - Android系统移植与平台开发(二) - 准备Android开发环境
		作者:唐老师,华清远见嵌入式学院讲师. 编译Android源码 关于android系统的编译,Android的官方网站上也给出了详细的说明.http://source.android.com/sour ... 
- 深入浅出 - Android系统移植与平台开发(七)- 初识HAL
		作者:唐老师,华清远见嵌入式学院讲师. 1. HAL的module与stub HAL(Hardware AbstractLayer)硬件抽象层是Google开发的Android系统里上层应用对底层硬件 ... 
随机推荐
- hdu 1251:统计难题(字典树,经典题)
			统计难题 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131070/65535 K (Java/Others)Total Submi ... 
- JavaScript是如何实现继承的(六种方式)
			大多OO语言都支持两种继承方式: 接口继承和实现继承 ,而ECMAScript中无法实现接口继承,ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现,下文给大家技术js实现继承的 ... 
- QQ互联OAuth
			/** * QQ互联 oauth * @author dyllen * */ class Oauth { //取Authorization Code Url const PC_CODE_URL = ' ... 
- vector的主要操作
			vector常用方法 assign() 对Vector中的元素赋值 void assign( input_iterator start, input_iterator end ); // void a ... 
- JAVA安装,环境变量配置
			JAVA环境变量设置 PATH %JAVA_HOME%\bin JAVA_HOME D:\ProgramFiles\Java\jdk1.6.0_10 CLASSPATH .;%JAVA_HOME%\l ... 
- HDU 5807 Keep In Touch DP
			Keep In Touch Problem Description There are n cities numbered with successive integers from 1 to n ... 
- Codeforces Beta Round #95 (Div. 2) D.Subway
			题目链接:http://codeforces.com/problemset/problem/131/D 思路: 题目的意思是说给定一个无向图,求图中的顶点到环上顶点的最短距离(有且仅有一个环,并且环上 ... 
- hdu3709 Balanced Number (数位dp+bfs)
			Balanced Number Problem Description A balanced number is a non-negative integer that can be balanced ... 
- mvc-6依赖管理
			CommonJS CommonJS规范,主要解决命名空间管理模块和用一套标准的编程模式来加载模块: 很快成为了JavaScript模块写法的事实标准: 它包含IO接口,底层的套接字流,以及单元测试等标 ... 
- 04 DOM一窥
			BOM 浏览器对象模型 * window alert(); 弹出框 confirm() 询问框 setInterval("run()",1000); 每隔1秒执行run ... 
