posts - 189,comments - 115,trackbacks - 0
          Android提高啟動速度的實現方法 
          http://blog.chinaunix.net/uid-25838286-id-3066799.html

          首先看一下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;//這個代表了裝載時間小于1250us1.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的類,試過了也是可以提高速度。

          三,系統服務初始化和package 掃描


          在啟動系統服務的init2()時會啟動應用層(Java層)的所有服務。

              public static void main(String[] args) {
             

                  System.loadLibrary("Android_servers");
                  init1(args); //init1 初始化,完成之后會回調init2()
              }



          在init2()中會啟動一個線程來啟動所有服務

          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() {

          。。。

          關鍵服務:

            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掃描的服務

             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);


          上面這些都是關鍵服務,不建議進行裁剪。


          下面的這些不是很關鍵,可以進行裁剪,當是必須相應的修改framework部分的代碼,工作量比較大和復雜。我去掉了20個服務,大概需要相應修改大概20多個文件。



                          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 NotificationManagerService(context, statusBar, hardware);
                          ServiceManager.addService(Context.NOTIFICATION_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 掃描部分,整個流程為


          callstatic("com/Android/server/SystemServer","init2")
          init2()
          main(context, factoryTest)
          PackageManagerService(context, factoryTest)
          scanDirLI(dir, flags, mode)
          scanPackageLI(file, file resfile, flags, mode)
          parsePackage(file, dest filename, metrics, flags)
          parsePackage(file, dest, metrics, flags)
          openXmlResourceParser(cookie, filename) (“AndroidManifest.xml“)
          openXmlBlockAsset(cookie, filename)
          openXmlAssetNative(cookie, filename)
          Android_content_AssetManager_openXmlAssetNative
          openNonAsset(cookie, filename, flag)
          openNonAssetInPathLocked(filename, mode, asset_path)
          getZipFileLocked(asset_path)
          getZip(ap.path)
          get(path)
          getZip()
          SharedZip(path, modWhen)
          ZipFileRO::open(path)
          parseZipArchive(void)
          get4LE(ptr)
          (memory access)

          最終的zip文件(apk)讀取是在下面這兩個函數:


          /*
          * 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'\n", zipFileName);

              /*
               * Open and map the specified file.
               */

              fd = ::open(zipFileName, O_RDONLY);


              if (fd < 0) {
                  LOGW("Unable to open zip '%s': %s\n", 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\n", 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\n");

              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\n",                     \
                          (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 (kLFHSignature) 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\n");
                  goto bail;
              } else if (val != kLFHSignature) {
                  LOGV("Not a Zip archive (found 0x%08x)\n", 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("Could not find end-of-central-directory in Zip\n");
                  goto bail;
              }

              /*
               * There are two interesting items in the EOCD block: the number of
               * entries in the file, and the file offset of the start of the
               * central directory.
               *
               * (There's actually a count of the #of entries in this file, and for
               * all files which comprise a spanned archive, but for our purposes
               * we're only interested in the current file.  Besides, we expect the
               * 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\n", numEntries, cdOffset);
              if (numEntries == 0 || cdOffset >= length) {
                  LOGW("Invalid entries=%d offset=%d (len=%zd)\n",
                      numEntries, cdOffset, length);
                  goto bail;
              }

              /*
               * Create hash table.  We have a minimum 75% load factor, possibly as
               * 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)\n", i);
                      goto bail;
                  }
                  if (ptr + kCDELen > basePtr + length) {
                      LOGW("Ran off the end (at %d)\n", 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\n",
                  //    i, localHdrOffset, fileNameLen, extraLen, commentLen);
                  //LOGV(" '%.*s'\n", 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) != kLFHSignature) {
                     // LOGW("Bad offset to local header: %d (at %d)\n",
                       //   localHdrOffset, i);
                    //  goto bail;
               //   }

                  ptr += kCDELen + fileNameLen + extraLen + commentLen;
                  CHECK_OFFSET(ptr - basePtr);
              }

              result = true;

          bail:
              return result;
          #undef CHECK_OFFSET
          }

           

          養成勵志的習慣,一生勵志   

          30本經典Linux學習和開發教程和資料 

          15本經典C、C++、MFC、VC++教程

          10本經典Java教程

          15本經典Android教程和海量Android源碼

           

          posted on 2012-02-08 20:47 MEYE 閱讀(4151) 評論(0)  編輯  收藏 所屬分類: Android3D
          主站蜘蛛池模板: 五峰| 格尔木市| 定陶县| 大埔县| 淄博市| 普兰县| 上高县| 射洪县| 正蓝旗| 凤阳县| 山东省| 株洲市| 赤水市| 呈贡县| 富民县| 荣成市| 巩义市| 盘锦市| 哈密市| 中山市| 铜鼓县| 崇文区| 兴文县| 屏南县| 白朗县| 额尔古纳市| 改则县| 金溪县| 江川县| 彰化市| 兖州市| 石景山区| 招远市| 聂拉木县| 额敏县| 霍林郭勒市| 滨州市| 开化县| 崇礼县| 舟曲县| 千阳县|