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



上一篇博客 【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 . 從進程角度分析冷啓動與熱啓動 :


① 冷啓動 : 運行程序後 , 應用啓動 , 會爲該應用啓動一個新進程 ; 這次啓動是冷啓動 ;

② 退出應用 進程保留 : 點擊回退鍵 , 應用退出 , 此時該進程進入後臺 , 不會馬上被殺死 ;

③ 熱啓動 : 再次啓動該應用時 , 就會重新啓用之前的進程 , 這次啓動就是熱啓動 ;


這也是安卓手機爲什麼越用越卡的原因 , 進程進入後臺 , 沒有及時殺死 ; 蘋果手機進程進入後臺 , 會放入一個與運行時不相關的內存中 ;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章