Launcher进程的启动到用户界面的呈现,揭示每个阶段的关键步骤

移动开发 Android
随着Android系统的不断发展和更新,Launcher进程的启动流程也可能会发生相应的变化和优化。Android系统还支持多种启动Launcher的方式,如开机后自动启动、短按Home键启动以及异常崩溃后自动重启等。

Launcher启动器

Launcher(启动器、桌面) 是 Android 操作系统上用于展示应用图标、搜索应用、管理桌面快捷方式以及执行其他与设备主屏幕相关任务的用户界面。设备的主屏幕布局和外观是用户与设备交互的主要方式。

Launcher特点:

  1. 「自定义性」:大多数 Launcher 都允许用户自定义图标、壁纸、桌面布局等。
  2. 「性能」:高效的 Launcher 可以提高设备的整体性能,因为它需要快速响应触摸输入并加载图标和布局。
  3. 「兼容性」:随着 Android 版本的更新,Launcher 需要确保与最新版本的 Android 兼容。
  4. 「安全性」:Launcher 必须确保用户数据的安全,并防止恶意软件的攻击。
  5. 「多样性」:市场上有许多不同的 Launcher 应用,每个应用都有其独特的功能和界面设计。

Launcher进程启动流程

  1. 「SystemServer进程启动」:

SystemServer是Android系统中的一个核心进程,负责启动和初始化各种系统服务。

在SystemServer的启动过程中,会调用其他服务,如PackageManagerService(PMS)和ActivityManagerService(AMS)的初始化方法。

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);
}
  1. 「PMS服务初始化」:

PMS服务会完成系统中应用程序的安装和管理工作。

PMS会扫描/data/app目录,加载已经安装的应用程序信息。

  1. 「AMS服务初始化」:

AMS是Android系统中负责管理应用程序生命周期和活动(Activity)状态的服务。

在AMS的初始化过程中,会注册各种系统广播接收器,包括与Launcher启动相关的广播。

4.「Launcher应用程序的注册」:

Launcher应用程序是一个特殊的系统应用,它在AndroidManifest.xml文件中配置了特定的Intent Filter,以便系统能够识别并启动它。

通常,Launcher应用程序的Action被设置为Intent.ACTION_MAIN,而Category被设置为Intent.CATEGORY_HOME。

5.「SystemReady阶段」:

当系统完成初始化并准备好启动桌面时,AMS会调用其systemReady()方法。

在systemReady()方法中,AMS会检查系统是否准备好启动Launcher,并调用相关方法来启动。

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

在startHomeActivityLocked()方法中,通过getHomeIntent()方法获取到要启动的HomeActivity的intent对象,mTopAction默认为INTENT.ACTION_MAIN,并添加CATEGORY_HOME的category标志。通过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;
}
  1. 「启动Launcher进程」:

AMS启动Launcher进程。

该方法会创建一个新的进程(如果Launcher尚未运行)来启动Launcher应用程序。

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();
  }
}

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();
  }
}
  1. 「Launcher进程启动后的操作」:

Launcher进程启动后,会向PMS请求已安装应用程序的信息,并将这些信息展示在桌面上。

用户可以通过点击桌面上的应用程序图标来启动相应的应用程序。

@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;
}

InvariantDeviceProfile对象主要是存储App的基本配置信息,例如App图标的尺寸大小,文字大小,每个工作空间或文件夹能显示多少App等。

在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);
}

随着Android系统的不断发展和更新,Launcher进程的启动流程也可能会发生相应的变化和优化。Android系统还支持多种启动Launcher的方式,如开机后自动启动、短按Home键启动以及异常崩溃后自动重启等。这些启动方式的实现流程也有所不同,但基本流程都与上述步骤相似。

责任编辑:武晓燕 来源: 沐雨花飞蝶
相关推荐

2014-04-10 09:21:22

Windows Ser

2021-02-05 10:27:23

转型计划项目负责人CIO

2018-09-07 10:14:58

2023-07-31 11:19:16

2009-12-25 14:52:49

2021-11-24 14:46:06

云计算云迁移数据中心

2023-02-15 14:09:57

云托管云退出策略

2019-06-12 14:34:42

云平台云迁移云计算

2023-12-21 11:59:29

2020-09-28 06:32:53

VDI测试清单虚拟化

2024-03-26 08:58:55

集成测试软件开发Python

2020-03-09 22:10:46

工业物联网IIoT人工智能

2019-01-02 05:05:12

物联网网络物联网IOT

2022-12-22 14:47:50

数据治理数字化转型

2023-01-17 10:37:40

2023-09-21 16:01:26

数字化转型数据管理

2020-12-02 10:56:39

灾难恢复网络中断DRP

2011-08-25 10:39:19

云计算

2024-04-19 13:21:55

2012-03-20 14:03:23

点赞
收藏

51CTO技术栈公众号