天天看點

【Android 性能優化】應用啟動優化 ( 安卓應用啟動分析 | Launcher 應用啟用普通安卓應用 | 應用程序分析 )

文章目錄

  • 一、 Launcher 應用 startActivitySafely 方法分析
  • 二、 Launcher 中的 startActivity(View v, Intent intent, Object tag) 方法分析
  • 三、 Android 應用程序分析

上一篇部落格 【Android 性能優化】應用啟動優化 ( 安卓應用啟動分析 | Launcher 應用簡介 | Launcher 應用源碼簡介 | Launcher 應用快捷方式圖示點選方法分析 ) 分析了 Launcher 應用中 Launcher.java 界面代碼 , 并分析了圖示點選事件 onClick 方法 , 本篇部落格繼續分析 Launcher 應用中啟動普通 Android 應用的源碼 ;

一、 Launcher 應用 startActivitySafely 方法分析

在 Launcher 應用中 , 點選快捷方式圖示 , 調用 onClick 方法 , 如果判定點選的圖示元件時應用圖示 , 會觸發調用 startActivitySafely 方法 , 啟動該圖示對應的 Android 應用 Activity 界面 ;

boolean startActivitySafely(View v, Intent intent, Object tag) {
        boolean success = false;
        try {
        	// 啟動新的應用
            success = startActivity(v, intent, tag);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
        }
        return success;
    }
           
該段代碼在 \packages\apps\Launcher2\src\com\android\launcher2\Launcher.java 界面中定義 , 該界面是 Launcher 應用的主界面 ;

二、 Launcher 中的 startActivity(View v, Intent intent, Object tag) 方法分析

1 . Launcher 中的啟動方法 : Launcher 應用中啟動 Android 應用 , 調用 startActivity(View v, Intent intent, Object tag) 方法 , 在該方法中 , 啟動 Android 應用的啟動 Activity ;

3 . 實際啟動方法 : 在 startActivity(View v, Intent intent, Object tag) 方法中啟動 Android 應用的核心方法是 startActivity(intent, opts.toBundle()) 和 startActivity(intent) 啟動安卓應用界面 ;

( 該 startActivity(intent) 方法就是我們經常調用的啟動界面的方法 )

4 . Intent 來源 : 該啟動 的 Intent 參數是之前 onClick 方法中從 Launcher 中的圖示元件中擷取的 Tag 标簽 ;

public void onClick(View v) {
	// 該從 View v 元件中擷取的标簽 Tag 就是 Intent
	Object tag = v.getTag();
    if (tag instanceof ShortcutInfo) {
    	// 擷取 Intent 對象 , 可以直接根據該對象啟動應用 Activity 界面
	 	final Intent intent = ((ShortcutInfo) tag).intent;
	}
}
           

5 . Launcher 應用中 startActivity(View v, Intent intent, Object tag) 方法源碼 :

boolean startActivity(View v, Intent intent, Object tag) {
    	// 設定一個啟動标志
    	// 查找目前任務棧中是否有與該 Activity 親和性相同的任務棧
    	// 如果有将該任務棧移動到前台 , 至于是建立新 Activity 還是複用原來 Activity , 按照該 Activity 的啟動模式進行操作
    	// 如果沒有親和性相同任務棧 , 建立任務棧 , 移動到前台
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        try {
            // Only launch using the new animation if the shortcut has not opted out (this is a
            // private contract between launcher and may be ignored in the future).
            boolean useLaunchAnimation = (v != null) &&
                    !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
            UserHandle user = (UserHandle) intent.getParcelableExtra(ApplicationInfo.EXTRA_PROFILE);
            LauncherApps launcherApps = (LauncherApps)
                    this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
            if (useLaunchAnimation) {
                ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
                        v.getMeasuredWidth(), v.getMeasuredHeight());
                if (user == null || user.equals(android.os.Process.myUserHandle())) {
                    // Could be launching some bookkeeping activity
                    // 根據 Intent 啟動點選圖示對應的 Activity 界面
                    startActivity(intent, opts.toBundle());
                } else {
                    launcherApps.startMainActivity(intent.getComponent(), user,
                            intent.getSourceBounds(),
                            opts.toBundle());
                }
            } else {
                if (user == null || user.equals(android.os.Process.myUserHandle())) {
                	// 真實啟動應用的方法
                	// 根據 Intent 啟動點選圖示對應的 Activity 界面
                    startActivity(intent);
                } else {
                    launcherApps.startMainActivity(intent.getComponent(), user,
                            intent.getSourceBounds(), null);
                }
            }
            return true;
        } catch (SecurityException e) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
                    "or use the exported attribute for this activity. "
                    + "tag="+ tag + " intent=" + intent, e);
        }
        return false;
    }
           
該段代碼在 \packages\apps\Launcher2\src\com\android\launcher2\Launcher.java 界面中定義 , 該界面是 Launcher 應用的主界面 ;

三、 Android 應用程序分析

1 . 應用啟動前置操作 : 調用 startActivity(Intent intent) 方法 , 通過程序間通信 , 啟動另外的 Android 應用 , 首先會去查找該 Activity 對應的包名 , 為該應用配置設定記憶體空間 , 并加載新應用對應的 main 函數 , 通過 Zygote 程序 , 孵化出新程序 , 在新程序中有方法區 , 堆區 , 棧區 , 等記憶體分區 ;

2 . 建立新程序過程 : Launcher 應用與 Zygote 程序進行通信後 , 通知 Zygote 程序 fork 一個新的程序 , 該新程序中通過 System Server 執行 ActivityThread , 執行 ActivityThread 中的主函數 ;

該 ActivityThread 中的主函數 main 中 , 有一個 Looper 不停的在不停的輪詢讀取 MessageQueue 中的消息 , 用于接收指令執行應用相關操作 ;

3 . 建立程序依據 : 根據包名查找建立程序 ;

① 根據包名查找建立程序 : 這個 ActivityThread 是指定包名的應用的函數入口 , 不是一個随意的入口 , 需要根據該包名查找對應的程序是否已經存在 ;

② 程序不存在 : 如果這個程序不存在 , 需要重新 fork 程序 , 執行後續一系列操作 , 那麼這次啟動稱為冷啟動 ;

③ 程序存在 : 如果之前該包名對應的應用存在 , 不需要重新建立程序 , 程序可以直接複用 , 那麼這次啟動稱為熱啟動 ;

4 . 從程序角度分析冷啟動與熱啟動 :

① 冷啟動 : 運作程式後 , 應用啟動 , 會為該應用啟動一個新程序 ; 這次啟動是冷啟動 ;