原文链接:http://www.embedu.org/Column/Column682.htm

作者:唐老师,华清远见嵌入式学院讲师。

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. 2218              /** 
                        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. 2224              @SdkConstant(SdkConstantType.INTENT_CATEGORY) 
                        2. 2225              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里,标签里添加上响应CATEGORY_FS_HOME的代码:

[html] view plaincopyprint?

                1.  style="FONT-SIZE: 10px" android:name=".MyLauncherActivity" > 
                        2.               
                        3.                             android:name="android.intent.action.MAIN" /> 
                        4.                             android:name="android.intent.category.FS_HOME" /> 
                        5.                             android:name="android.intent.category.DEFAULT" /> 
                        6.               
                        7.

重新编译frameworks/base、frameworks/ex:

[plain] view plaincopyprint?

                1. $ source build/envsetup.sh
                        2. $ make

重新运行模拟器,使用新编译的Android系统,可以看到系统启动起来就进入我们的MyLauncher应用,从实现实现桌面的替换。

更多相关文章

  1. android service 详解
  2. Framework篇 - Android(安卓)系统介绍和架构一览
  3. 将服务器端字符读取至android的文本控件,换行符变成了黑方块的原
  4. 定制android启动界面
  5. okhttp的应用详解与源码解析--okhttp客户端应用
  6. android之Http协议编程01
  7. 在Ubuntu中和Android中添加开机自启动的守护进程
  8. .net平台借助第三方推送服务在推送Android消息(极光推送)
  9. 《Android移动应用基础教程》(Android(安卓)Studio)(第二版)黑马教程

随机推荐

  1. 做android 开发遇到的问题及解决办法记录
  2. Android Sqlite Failed to open database
  3. android学习——activity的生命周期
  4. Android 九宫格图片展示的实现
  5. Android(安卓)之如何优化 UI 渲染(上)
  6. 【极光推送Jpush】Android集成极光推送及
  7. 获取Android设备唯一标识(唯一序列号)
  8. Android 2.1 源码结构分析 转载
  9. Android植物大战僵尸附源码
  10. GitHub上受欢迎的Android UI Library