文章目錄
上一篇博客 【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 . 從進程角度分析冷啓動與熱啓動 :
① 冷啓動 : 運行程序後 , 應用啓動 , 會爲該應用啓動一個新進程 ; 這次啓動是冷啓動 ;
② 退出應用 進程保留 : 點擊回退鍵 , 應用退出 , 此時該進程進入後臺 , 不會馬上被殺死 ;
③ 熱啓動 : 再次啓動該應用時 , 就會重新啓用之前的進程 , 這次啓動就是熱啓動 ;
這也是安卓手機爲什麼越用越卡的原因 , 進程進入後臺 , 沒有及時殺死 ; 蘋果手機進程進入後臺 , 會放入一個與運行時不相關的內存中 ;