歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android提高啟動速度

Android提高啟動速度

日期:2017/3/1 11:15:53   编辑:Linux編程
大家都知道啟動速度慢是智能操作系統的一個通病,Android也不例外,啟動速度大概在1分鐘左右,雖然日本有一個叫quick boot的一秒啟動android的產品,但是畢竟是旁門左道。所以從常規來提高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 Set<String> EXCLUDED_CLASSES
= new HashSet<String>(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 Set<String> SERVICES = new HashSet<String>(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的類,試過了也是可以提高速度。

Copyright © Linux教程網 All Rights Reserved