Www.eoeandroid.com first launched by: Tigertang2@gmail.com Everyone knows that slow startup speed is a common problem in the smart operating system. Android is no exception. The boot speed is about 1 minute. Although a product called quick boot in Japan starts android one second, but after all, it is the opposite path. Therefore, improving the startup speed of android has become the focus and difficulty of research. We will share with you some preliminary research experience. First, let's take a look at the android startup process: Bootloader Bootstrap program Kernel Kernel Init Init initialization (this is familiar to everyone, not to mention) Loads several daemons and services, including zygote see/init. rc and init. . Rc
Zygote
This is the most time-consuming and focuses on repairing objects preloads classes. Loaded more than one thousand classes, Mom !!! Starts package manager scans a package (details below)Service manager Start services (start multiple services)From the actual test data, there are two places that are the most time-consuming. One is the process of loading more than one thousand classes and initializing stacks by zygote, which takes about 20 seconds. Another is scanning. /System/app, /System/framework, /Data/app, /Data/app-private. The packages under these directories took about 10 seconds, so the two bosses can be repaired. I. The first step is the use of debugging tools. You can test which classes and how long the processes take, The main tool is Stopwatch Message loggers Grabserial Reference http://elinux.org/Grabserial Printk times reference http://elinux.org/Printk_Times logcat Android built-in
Bootchart reference http://elinux.org/Bootchart and Http://elinux.org/Bootchart Strace Part of AOSP (Eclair and later)
Example In init. rc, to debug zygote Change 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 * Detailed use of available documentation and webpage Introduction If the above tool is not used for detailed analysis, you can also use logcat. Adding a bit of computing time and debugging information of some classes in the code can also achieve good results. 2. zygote loads more than 1 thousand classes First, we can add a bit of debugging information for detailed reprinting. 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. Setinclutivegroup (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 (); The above + indicates the added code, so that you can easily get the classes that are loaded during the loading process, and how long it takes. The loaded classes are in the file platform/frameworks/base/preloaded-classes. The content is similar: 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 This file is automatically generated by the WritePreloadedClassFile. java WritePreloadedClassFile class. /** * Writes/frameworks/base/preloaded-classes. Also updates
* {@ Link LoadedClass # preloaded} fields and writes over compiled log file.
*/
Public ClassWritePreloadedClassFile /** * Preload any class that takes longer to load than MIN_LOAD_TIME_MICROS us. */ Static final int MIN_LOAD_TIME_MICROS = 1250; // This indicates that classes whose loading time is smaller than 1250us (1.25ms) will not be loaded. Maybe you can modify this parameter to reduce the loading of classes. // Here we can see what kind of class will be loaded
A: classes that must be loaded at startup, such as system-level classes.
B: The loading time is later than 1.25ms.
C: Classes used more than once or loaded by applications
A closer look at the specific implementation of the filtering class helps us to understand which classes are important and which can be removed. The filter rule is First isPreloadable, /** Reports if the given class shoshould be preloaded .*/ Public static boolean isPreloadable (LoadedClass clazz ){ Return clazz. systemClass &&! EXCLUDED_CLASSES.contains (clazz. name ); } This indicates all classes loaded by the system except the classes contained in EXCLUDED_CLASSES. EXCLUDED_CLASSES include /** * Classes which we shouldn't load from the Zygote. */ Private static final Set EXCLUDED_CLASSES = New HashSet (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 )); Currently, Binders related to Threads are not pre-loaded.
Clazz. medianTimeMicros ()> MIN_LOAD_TIME_MICROS has a loading time greater than 1.25 ms. The third names. size ()> 1 is processed more than once. The above all refer to the system class, and some application classes need to be loaded. The rule is fromZygote and is not a service. Proc. fromZygote ()&&! Policy. isService (proc. name)
FromZygote refers to the zygote class except com. android. development. Public boolean fromZygote (){ Return parent! = Null & parent. name. equals (zygote) &&! Name. equals (com. android. development ); }
/Besides resident memory services /** * Long running services. These are restricted in their contribution to * Preloader because their launch time is less critical. */ // TODO: Generate this automatically from package manager. Private static final Set SERVICES = new HashSet (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. javasclock, Com. google. process. gapps, Android. tts ));
Okay. These classes are to be reproduced. Although preloaded-classes has been identified when downloading the source code, that is, the WritePreloadedClassFile class is useless for us. What we can do is in the preloaded-classes file, remove the non-pre-loaded class and try to remove all classes. The startup will skip the place very quickly, but it will be slow when the HOME is started. So the best way is to remove those that are not used, but be careful. We are still exploring what to remove and will share it with you later. If you are interested, you can first clear all the files in the preloaded-classes file, which is much faster to start, but it will be slower to start the apk. Of course, you can also remove all android-related classes, and the rest of the java classes can also improve the speed.
3. System Service initialization and package Scanning When init2 () of the system service is started, all services at the application layer (Java layer) are started. Public static void main (String [] args ){ System. loadLibrary (android_servers ); Init1 (args); // init1 initialization. After initialization, init2 () is called back () }
In init2 (), a thread is started to start all services. 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 (){ ... Key services: 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 scan Service 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 );
The above are all key services and we do not recommend cropping them.
The following are not very important. You can crop the code. You must modify the code of the framework. The workload is large and complex. I removed 20 services and needed to modify more than 20 files accordingly.
StatusBar = new StatusBarService (context ); ServiceManager. addService (statusbar, statusBar );
ServiceManager. addService (clipboard, new ClipboardService (context ));
Imm = new InputMethodManagerService (context, statusBar ); ServiceManager. addService (Context. INPUT_METHOD_SERVICE, imm );
ServiceManager. addService (netstat, new NetStatService (context ));
Connectivity = ConnectivityService. getInstance (context );
ServiceManager. addService (Context. CONNECTIVITY_SERVICE, connectivity ); ServiceManager. addService (Context. ACCESSIBILITY_SERVICE, New AccessibilityManagerService (context ));
Notification = new icationicationmanagerservice (context, statusBar, hardware ); ServiceManager. addService (Context. icationication_service, notification );
ServiceManager. addService (mount, new MountService (context ));
ServiceManager. addService (DeviceStorageMonitorService. SERVICE, New DeviceStorageMonitorService (context ));
ServiceManager. addService (Context. LOCATION_SERVICE, new LocationManagerService (context ));
ServiceManager. addService (Context. SEARCH_SERVICE, new SearchManagerService (context ));
If (INCLUDE_DEMO ){ Log. I (TAG, Installing demo data ...); (New DemoThread (context). start (); } Intent intent = new Intent (). setComponent (new ComponentName ( Com. google. android. server. checkin, Com. google. android. server. checkin. CheckinService ));
ServiceManager. addService (checkin, new FallbackCheckinService (context ));
Wallpaper = new WallpaperManagerService (context ); ServiceManager. addService (Context. WALLPAPER_SERVICE, wallpaper );
ServiceManager. addService (Context. AUDIO_SERVICE, new AudioService (context ));
Headset = new HeadsetObserver (context );
Dock = new DockObserver (context, power );
ServiceManager. addService (Context. BACKUP_SERVICE, new BackupManagerService (context ));
ServiceManager. addService (Context. APPWIDGET_SERVICE, appWidget );
Package scan:
The final zip file (apk) is read in the following two functions:
/* * Open the specified file read-only. We memory-map the entire thing and * Close the file before returning. */ Status_t ZipFileRO: open (const char * zipFileName) { Int fd =-1; Off_t length; Assert (mFileMap = NULL );
LOGD (opening zip '% s', zipFileName ); /* * Open and map the specified file. */ Fd =: open (zipFileName, O_RDONLY ); If (fd <0 ){ LOGW (Unable to open zip '% s': % s, zipFileName, strerror (errno )); Return NAME_NOT_FOUND; }
Length = lseek (fd, 0, SEEK_END ); If (length <0 ){ Close (fd ); Return UNKNOWN_ERROR; }
MFileMap = new FileMap ();
If (mFileMap = NULL ){ Close (fd ); Return NO_MEMORY; } If (! MFileMap-> create (zipFileName, fd, 0, length, true )){ LOGW (Unable to map '% s': % s, zipFileName, strerror (errno )); Close (fd ); Return UNKNOWN_ERROR; }
MFd = fd; /* * Got it mapped, verify it and create data structures for fast access. */ If (! ParseZipArchive ()){ MFileMap-> release (); MFileMap = NULL; Return UNKNOWN_ERROR; }
LOGD (done opening zip ); Return OK; } /* * Parse the Zip archive, verifying its contents and initializing internal * Data structures. */ Bool ZipFileRO: parseZipArchive (void) {
# Define CHECK_OFFSET (_ off ){ If (unsigned int) (_ off)> = maxOffset ){ LOGE (ERROR: bad offset % u (max % d): % s, (Unsigned int) (_ off), maxOffset, # _ off ); Goto bail; } } Const unsigned char * basePtr = (const unsigned char *) mFileMap-> getDataPtr (); Const unsigned char * ptr; Size_t length = mFileMap-> getDataLength (); Bool result = false; Unsigned int I, numEntries, cdOffset; Unsigned int val;
/* * The first 4 bytes of the file will either be the local header * Signature for the first file (klhsignature) or, if the archive doesn' t * Have any files in it, the end-of-central-directory signature * (KEOCDSignature ). */ Val = get4LE (basePtr ); If (val = kEOCDSignature ){ LOGI (Found Zip archive, but it looks empty ); Goto bail; } Else if (val! = Klhsignature ){ LOGV (Not a Zip archive (found 0x % 08x), val ); Goto bail; } /* * Find the EOCD. We'll find it immediately unless they have a file * Comment. */ Ptr = basePtr + length-kEOCDLen; While (ptr> = basePtr ){ If (* ptr = (kEOCDSignature & 0xff) & get4LE (ptr) = kEOCDSignature) Break; Ptr --; } If (ptr <basePtr ){ LOGI (cocould not find end-of-central-directory in Zip ); Goto bail; } /* * There are two interesting items in the EOCD block: the number * Entries in the file, and the file offset of the start of * Central directory. * * (There's actually a count of the # of entries in this file, and * All files which comprise a spanned archive, but for our purposes * We're only interested in the current file. Besides, we recommend CT * Two to be equivalent for our stuff .) */ NumEntries = get2LE (ptr + kEOCDNumEntries ); CdOffset = get4LE (ptr + kEOCDFileOffset ); /* Valid offsets are [0, EOCD] */ Unsigned int maxOffset; MaxOffset = (ptr-basePtr) + 1; LOGV (++ numEntries = % d cdOffset = % d, numEntries, cdOffset ); If (numEntries = 0 | cdOffset> = length ){ LOGW (Invalid entries = % d offset = % d (len = % zd ), NumEntries, cdOffset, length ); Goto bail; } /* * Create hash table. We have a minimum 75% load factor, possibly * Low as 50% after we round off to a power of 2. */ MNumEntries = numEntries; MHashTableSize = roundUpPower2 (1 + (numEntries * 4)/3 )); MHashTable = (HashEntry *) calloc (1, sizeof (HashEntry) * mHashTableSize ); /* * Walk through the central directory, adding entries to the hash * Table. */ Ptr = basePtr + cdOffset; For (I = 0; I <numEntries; I ++ ){ Unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; Const unsigned char * localHdr; Unsigned int hash; If (get4LE (ptr )! = KCDESignature ){ LOGW (Missed a central dir sig (at % d), I ); Goto bail; } If (ptr + kCDELen> basePtr + length ){ LOGW (Ran off the end (at % d), I ); Goto bail; } LocalHdrOffset = get4LE (ptr + kCDELocalOffset ); CHECK_OFFSET (localHdrOffset ); FileNameLen = get2LE (ptr + kCDENameLen ); ExtraLen = get2LE (ptr + kCDEExtraLen ); CommentLen = get2LE (ptr + kCDECommentLen ); // LOGV (++ % d: localHdr = % d fnl = % d el = % d cl = % d, // I, localHdrOffset, fileNameLen, extraLen, commentLen ); // LOGV ('%. * s', fileNameLen, ptr + kCDELen ); /* Add the CDE filename to the hash table */ Hash = computeHash (const char *) ptr + kCDELen, fileNameLen ); AddToHash (const char *) ptr + kCDELen, fileNameLen, hash );
// LocalHdr = basePtr + localHdrOffset; // If (get4LE (localHdr )! = Klhsignature ){ // LOGW (Bad offset to local header: % d (at % d ), // LocalHdrOffset, I ); // Goto bail; //} Ptr + = kCDELen + fileNameLen + extraLen + commentLen; CHECK_OFFSET (ptr-basePtr ); } Result = true; Bail: Return result; # Undef CHECK_OFFSET } The red part is the modified Code, which can be compared. (Unfinished ...)
|