Launcher進(jìn)程啟動流程

1、Launcher

Launcher作為Android系統(tǒng)的桌面涂身,它的作用有兩點(diǎn):
作為Android系統(tǒng)的啟動器,用于啟動應(yīng)用程序搓蚪;
作為Android系統(tǒng)的桌面蛤售,用于顯示和管理應(yīng)用程序的快捷圖標(biāo)或者其它桌面組件;

2妒潭、Launcher進(jìn)程啟動流程

2.1悴能、SystemServer調(diào)用

在SystemServer進(jìn)程啟動之后,執(zhí)行其run()函數(shù)雳灾,在里面執(zhí)行了大量的配置設(shè)置操作漠酿,并且啟動了各種引導(dǎo)服務(wù)、核心服務(wù)以及其他服務(wù)等谎亩,包括AMS炒嘲、PMS、WMS匈庭、電量管理服務(wù)等一系列服務(wù)夫凸,以及創(chuàng)建主線程Looper,并循環(huán)等待消息阱持;
其中在啟動引導(dǎo)服務(wù)方法中夭拌,啟動了ActivityManagerService,并且在啟動其他服務(wù)的方法中衷咽,調(diào)用AMS的systemReady()方法啼止,Launcher進(jìn)程就是從這兒開始啟動的;

public final class SystemServer {
    private void run() {
    ...
    startBootstrapServices();
    startOtherServices();
    ...
  }
  
  private void startBootstrapServices() {
    ...
    mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
    mActivityManagerService.setInstaller(installer);
    ...
  }
  
  private void startOtherServices() {
    ...
    mActivityManagerService.systemReady(() -> { 
      
    }, BOOT_TIMINGS_TRACE_LOG);
  }
}

在SystemServer啟動的時候兵罢,執(zhí)行startOtherServices()方法中献烦,里面調(diào)用了AMS的systemReady()方法,通過該方法來啟動Launcher卖词;

// Tag for timing measurement of main thread.
private static final String SYSTEM_SERVER_TIMING_TAG = "SystemServerTiming";
private static final TimingsTraceLog BOOT_TIMINGS_TRACE_LOG
            = new TimingsTraceLog(SYSTEM_SERVER_TIMING_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);

private void startOtherServices() {
  ...
  mActivityManagerService.systemReady(() -> {
    Slog.i(TAG, "Making services ready");
    traceBeginAndSlog("StartActivityManagerReadyPhase");
    mSystemServiceManager.startBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
    ...
  }, BOOT_TIMINGS_TRACE_LOG);
}

2.2巩那、AMS執(zhí)行

在AMS中執(zhí)行systemReady()方法吏夯,在其中執(zhí)行startHomeActivityLocked()方法,傳入當(dāng)前用戶ID即横;

public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
  ...
  synchronized (this) {
    ...
    startHomeActivityLocked(currentUserId, "systemReady");
    ...
  }
  ...
}
2.2.1噪生、獲取Launcher的Intent

在startHomeActivityLocked()方法中,首先通過getHomeIntent()方法东囚,獲取到要啟動的HomeActivity的intent對象跺嗽,其中mTopAction默認(rèn)為INTENT.ACTION_MAIN,并添加CATEGORY_HOME的category標(biāo)志页藻;
得到Intent對象桨嫁,通過PackageManager去獲取對應(yīng)符合的Activity,獲取對應(yīng)的ActivityInfo份帐,并獲取對應(yīng)的進(jìn)程記錄璃吧,此時對應(yīng)的進(jìn)程還沒啟動,后面繼續(xù)執(zhí)行废境,為intent添加FLAG_ACTIVITY_NEW_TASK啟動參數(shù)畜挨,開啟新棧,隨后調(diào)用ActivityStartController類的startHomeActivity()方法去執(zhí)行啟動噩凹;

boolean startHomeActivityLocked(int userId, String reason) {
  ...
  Intent intent = getHomeIntent(); 
  ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
  if (aInfo != null) {
    intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
    // Don't do this if the home app is currently being instrumented.
    aInfo = new ActivityInfo(aInfo);
    aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
    ProcessRecord app = getProcessRecordLocked(aInfo.processName, aInfo.applicationInfo.uid, true);
    if (app == null || app.instr == null) {
      intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
      final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
      // For ANR debugging to verify if the user activity is the one that actually launched.
      final String myReason = reason + ":" + userId + ":" + resolvedUserId;
      mActivityStartController.startHomeActivity(intent, aInfo, myReason);
    }
  }
  ...
  return true;
}

Intent getHomeIntent() {
  Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
  intent.setComponent(mTopComponent);
  intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
  if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
    intent.addCategory(Intent.CATEGORY_HOME);
  }
  return intent;
}
2.2.2巴元、啟動Launcher

在startHomeActivity()方法中,調(diào)用obtainStarter()方法獲取到一個ActivityStarter對象驮宴,setCallingUid()方法設(shè)置當(dāng)前調(diào)用的Uid=0务冕,然后執(zhí)行其execute()方法;

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
  mSupervisor.moveHomeStackTaskToTop(reason);
  mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
    .setOutActivity(tmpOutRecord)
    .setCallingUid(0)
    .setActivityInfo(aInfo)
    .execute();
  mLastHomeActivityStartRecord = tmpOutRecord[0];
  if (mSupervisor.inResumeTopActivity) {
    // If we are in resume section already, home activity will be initialized, but not
    // resumed (to avoid recursive resume) and will stay that way until something pokes it
    // again. We need to schedule another resume.
    mSupervisor.scheduleResumeTopActivities();
  }
}

在ActivityStarter的execute()方法中幻赚,mayWait默認(rèn)為false禀忆,執(zhí)行startActivity()方法;

int execute() {
  try {
    // TODO(b/64750076): Look into passing request directly to these methods to allow
    // for transactional diffs and preprocessing.
    if (mRequest.mayWait) {
      return startActivityMayWait(mRequest.caller, mRequest.callingUid,  ...);
    } else {
      return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, ...);
    }
  } finally {
    onExecutionComplete();
  }
}

這里進(jìn)入了Activity的啟動流程落恼,Launcher本身就是一個系統(tǒng)APP箩退,用于顯示桌面等,LauncherApp啟動之后會執(zhí)行其生命周期方法初始化桌面布局佳谦;

2.3戴涝、初始化桌面圖標(biāo)

2.3.1、執(zhí)行onCreate()方法
@Override
protected void onCreate(Bundle savedInstanceState) {
  ...
  LauncherAppState app = LauncherAppState.getInstance(this);
  ...
}

獲取LauncherAppState钻蔑,通過LauncherAppState的getInstance()方法獲取啥刻,該方法里面會判斷當(dāng)前線程是否為主線程,在主線程時還會直接new出對象咪笑,不在主線程時可帽,通過MainThreadExecutor的submit()方法向主線程提交一個任務(wù)去獲取該對象;

// We do not need any synchronization for this variable as its only written on UI thread.
private static LauncherAppState INSTANCE;

public static LauncherAppState getInstance(final Context context) {
  if (INSTANCE == null) {
    if (Looper.myLooper() == Looper.getMainLooper()) {
      INSTANCE = new LauncherAppState(context.getApplicationContext());
    } else {
      try {
        return new MainThreadExecutor().submit(new Callable<LauncherAppState>() {
          @Override
          public LauncherAppState call() throws Exception {
            return LauncherAppState.getInstance(context);
          }
        }).get();
      } catch (InterruptedException|ExecutionException e) {
        throw new RuntimeException(e);
      }
    }
  }
  return INSTANCE;
}
2.3.2窗怒、讀取安裝APP信息

在LauncherAppState的構(gòu)造方法中映跟,會新建InvariantDeviceProfile對象蓄拣,這個類主要是存儲App的基本配置信息,例如App圖標(biāo)的尺寸大小努隙,文字大小球恤,每個工作空間或文件夾能顯示多少App等;
在LauncherAppState的構(gòu)造方法中荸镊,會獲取WindowManager咽斧,并獲取屏幕的尺寸,解析桌面布局文件躬存,獲取默認(rèn)尺寸信息等张惹;

@TargetApi(23)
public InvariantDeviceProfile(Context context) {
  WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  Display display = wm.getDefaultDisplay();
  DisplayMetrics dm = new DisplayMetrics();
  display.getMetrics(dm);
  ...
  ArrayList<InvariantDeviceProfile> closestProfiles = findClosestDeviceProfiles(minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context));
  ...
}

ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(Context context) {
  ArrayList<InvariantDeviceProfile> profiles = new ArrayList<>();
  try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
    final int depth = parser.getDepth();
    int type;
    while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
      if ((type == XmlPullParser.START_TAG) && "profile".equals(parser.getName())) {
        TypedArray a = context.obtainStyledAttributes(Xml.asAttributeSet(parser), R.styleable.InvariantDeviceProfile);
        int numRows = a.getInt(R.styleable.InvariantDeviceProfile_numRows, 0);
        int numColumns = a.getInt(R.styleable.InvariantDeviceProfile_numColumns, 0);
        float iconSize = a.getFloat(R.styleable.InvariantDeviceProfile_iconSize, 0);
        profiles.add(new InvariantDeviceProfile(
          a.getString(R.styleable.InvariantDeviceProfile_name),
          a.getFloat(R.styleable.InvariantDeviceProfile_minWidthDps, 0),
          a.getFloat(R.styleable.InvariantDeviceProfile_minHeightDps, 0),
          numRows,
          numColumns,
          a.getInt(R.styleable.InvariantDeviceProfile_numFolderRows, numRows),
          a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
          iconSize,
          a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
          a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
          a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
          a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0),
          a.getResourceId(R.styleable.InvariantDeviceProfile_demoModeLayoutId, 0)));
        a.recycle();
      }
    }
  } catch (IOException|XmlPullParserException e) {
    throw new RuntimeException(e);
  }
  return profiles;
}
2.3.3、注冊Intent廣播

新建LauncherModel對象优构,該對象是一個BroadcastReceiver,并添加App變化的回調(diào)雁竞,以及設(shè)置Filter并注冊廣播钦椭,用于監(jiān)聽桌面App的變化;

private LauncherAppState(Context context) {
  ...
  mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
  LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
  // Register intent receivers
  IntentFilter filter = new IntentFilter();
  filter.addAction(Intent.ACTION_LOCALE_CHANGED);
  // For handling managed profiles
  filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
  filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
  ...
  mContext.registerReceiver(mModel, filter);
  ...
}

public class LauncherModel extends BroadcastReceiver ... {}
2.3.4碑诉、解析Launcher布局

繼續(xù)回到Launcher的onCreate()方法彪腔,將Launcher添加到LauncherModel中,是以弱引用的方式添加进栽,初始化一些其工作德挣,解析Launcher的布局,

2.3.5快毛、加載桌面

onCreate()方法中格嗅,通過LauncherModel的startLoader()來加載桌面App;

@Override
protected void onCreate(Bundle savedInstanceState) {
  ...
  if (!mModel.startLoader(currentScreen)) {
    if (!internalStateHandled) {
      // If we are not binding synchronously, show a fade in animation when
      // the first page bind completes.
      mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).setValue(0);
    }
  } else {
    // Pages bound synchronously.
    mWorkspace.setCurrentPage(currentScreen);
    setWorkspaceLoading(true);
  }
  ...
}

在LauncherModel的startLoader()方法中唠帝,新建了一個LoaderResults對象屯掖,并通過startLoaderForResults()方法創(chuàng)建出一個LoaderTask的Runnable任務(wù),將其在工作線程中執(zhí)行起來襟衰;

public boolean startLoader(int synchronousBindPage) {
  ...
  synchronized (mLock) {
    // Don't bother to start the thread if we know it's not going to do anything
    if (mCallbacks != null && mCallbacks.get() != null) {
      ...
      LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel, mBgAllAppsList, synchronousBindPage, mCallbacks);
      if (mModelLoaded && !mIsLoaderTaskRunning) {
        ...
        return true;
      } else {
        startLoaderForResults(loaderResults);
      }
    }
  }
  return false;
}

public void startLoaderForResults(LoaderResults results) {
  synchronized (mLock) {
    stopLoader();
    mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
    runOnWorkerThread(mLoaderTask);
  }
}

private static void runOnWorkerThread(Runnable r) {
  if (sWorkerThread.getThreadId() == Process.myTid()) {
    r.run();
  } else {
    // If we are not on the worker thread, then post to the worker handler
    sWorker.post(r);
  }
}

在LoaderTask的run()方法中贴铜,去加載手機(jī)已安裝的App的信息,查詢數(shù)據(jù)庫獲取已安裝的App的相關(guān)信息瀑晒,加載Launcher布局绍坝,并將數(shù)據(jù)轉(zhuǎn)化為View,綁定到界面上苔悦,由此我們就可以看到桌面顯示的宮格列表的桌面圖標(biāo)了轩褐;

public void run() {
  ...
  try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
    // 查詢數(shù)據(jù)庫整理App信息,轉(zhuǎn)化為View綁定到界面
    loadWorkspace();
    mResults.bindWorkspace();
    loadAllApps();
    mResults.bindAllApps();
    loadDeepShortcuts();
    mResults.bindDeepShortcuts();
    mBgDataModel.widgetsModel.update(mApp, null);
    mResults.bindWidgets();
    transaction.commit();
  } catch (CancellationException e) {
    // Loader stopped, ignore
    TraceHelper.partitionSection(TAG, "Cancelled");
  }
  TraceHelper.endSection(TAG);
}

來自:https://www.yuque.com/jesus_yangshijie/ruafsa/yl7sey

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末玖详,一起剝皮案震驚了整個濱河市灾挨,隨后出現(xiàn)的幾起案子邑退,更是在濱河造成了極大的恐慌,老刑警劉巖劳澄,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件地技,死亡現(xiàn)場離奇詭異,居然都是意外死亡秒拔,警方通過查閱死者的電腦和手機(jī)莫矗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來砂缩,“玉大人作谚,你說我怎么就攤上這事♀职牛” “怎么了妹懒?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長双吆。 經(jīng)常有香客問我眨唬,道長,這世上最難降的妖魔是什么好乐? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任匾竿,我火速辦了婚禮,結(jié)果婚禮上蔚万,老公的妹妹穿的比我還像新娘岭妖。我一直安慰自己,他們只是感情好反璃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布昵慌。 她就那樣靜靜地躺著,像睡著了一般淮蜈。 火紅的嫁衣襯著肌膚如雪废离。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天礁芦,我揣著相機(jī)與錄音蜻韭,去河邊找鬼。 笑死柿扣,一個胖子當(dāng)著我的面吹牛肖方,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播未状,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼俯画,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了司草?” 一聲冷哼從身側(cè)響起艰垂,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤泡仗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后猜憎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娩怎,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年胰柑,在試婚紗的時候發(fā)現(xiàn)自己被綠了截亦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡柬讨,死狀恐怖崩瓤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情踩官,我是刑警寧澤却桶,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蔗牡,受9級特大地震影響颖系,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛋逾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一集晚、第九天 我趴在偏房一處隱蔽的房頂上張望窗悯。 院中可真熱鬧区匣,春花似錦、人聲如沸蒋院。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽欺旧。三九已至姑丑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辞友,已是汗流浹背栅哀。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留称龙,地道東北人留拾。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像鲫尊,于是被迫代替她去往敵國和親痴柔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容