Launcher進程啓動

文章已同步Github博客:Launcher進程啓動

使用到的相關源碼:https://github.com/JesusYoung/AndroidResourceCode9.0/tree/master

基於Android 9.0

1、Launcher

Launcher作爲Android系統的桌面,它的作用有兩點:

  • 作爲Android系統的啓動器,用於啓動應用程序;
  • 作爲Android系統的桌面,用於顯示和管理應用程序的快捷圖標或者其它桌面組件;

2、Launcher進程啓動流程

2.1、SystemServer調用

在SystemServer進程啓動之後,執行其run()函數,在裏面執行了大量的配置設置操作,並且啓動了各種引導服務、核心服務以及其他服務等,包括AMS、PMS、WMS、電量管理服務等一系列服務,以及創建主線程Looper,並循環等待消息;

其中在啓動引導服務方法中,啓動了ActivityManagerService,並且在啓動其他服務的方法中,調用AMS的systemReady()方法,Launcher進程就是從這兒開始啓動的;

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啓動的時候,執行startOtherServices()方法中,裏面調用了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執行

在AMS中執行systemReady()方法,在其中執行startHomeActivityLocked()方法,傳入當前用戶ID;

public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
  ...
  synchronized (this) {
    ...
    startHomeActivityLocked(currentUserId, "systemReady");
    ...
  }
  ...
}

2.2.1、獲取Launcher的Intent

在startHomeActivityLocked()方法中,首先通過getHomeIntent()方法,獲取到要啓動的HomeActivity的intent對象,其中mTopAction默認爲INTENT.ACTION_MAIN,並添加CATEGORY_HOME的category標誌;

得到Intent對象,通過PackageManager去獲取對應符合的Activity,獲取對應的ActivityInfo,並獲取對應的進程記錄,此時對應的進程還沒啓動,後面繼續執行,爲intent添加FLAG_ACTIVITY_NEW_TASK啓動參數,開啓新棧,隨後調用ActivityStartController類的startHomeActivity()方法去執行啓動;

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()方法中,調用obtainStarter()方法獲取到一個ActivityStarter對象,setCallingUid()方法設置當前調用的Uid=0,然後執行其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默認爲false,執行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();
  }
}

這裏進入了Activity的啓動流程,Launcher本身就是一個系統APP,用於顯示桌面等,LauncherApp啓動之後會執行其生命週期方法初始化桌面佈局;

2.3、初始化桌面圖標

2.3.1、執行onCreate()方法

@Override
protected void onCreate(Bundle savedInstanceState) {
  ...
  LauncherAppState app = LauncherAppState.getInstance(this);
  ...
}

獲取LauncherAppState,通過LauncherAppState的getInstance()方法獲取,該方法裏面會判斷當前線程是否爲主線程,在主線程時還會直接new出對象,不在主線程時,通過MainThreadExecutor的submit()方法向主線程提交一個任務去獲取該對象;

// 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的構造方法中,會新建InvariantDeviceProfile對象,這個類主要是存儲App的基本配置信息,例如App圖標的尺寸大小,文字大小,每個工作空間或文件夾能顯示多少App等;

在LauncherAppState的構造方法中,會獲取WindowManager,並獲取屏幕的尺寸,解析桌面佈局文件,獲取默認尺寸信息等;

@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變化的回調,以及設置Filter並註冊廣播,用於監聽桌面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佈局

繼續回到Launcher的onCreate()方法,將Launcher添加到LauncherModel中,是以弱引用的方式添加,初始化一些其工作,解析Launcher的佈局,

@Override
protected void onCreate(Bundle savedInstanceState) {
  ...
  mModel = app.setLauncher(this);
  ...
  mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
  ...
}

# LauncherModel
public void initialize(Callbacks callbacks) {
  synchronized (mLock) {
    Preconditions.assertUIThread();
    mCallbacks = new WeakReference<>(callbacks);
  }
}

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()方法創建出一個LoaderTask的Runnable任務,將其在工作線程中執行起來;

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()方法中,去加載手機已安裝的App的信息,查詢數據庫獲取已安裝的App的相關信息,加載Launcher佈局,並將數據轉化爲View,綁定到界面上,由此我們就可以看到桌面顯示的宮格列表的桌面圖標了;

public void run() {
  ...
  try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
  	// 查詢數據庫整理App信息,轉化爲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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章