当前位置: 首页 > news >正文

Android 窗口结构(三) Home Task 添加Home ActivityRecord

  Android系统起来之后,会启动Launcher的主Activity。
  看一下调用链
SystemServer startOtherServices(……)
 --> ActivityManagerService systemReady(……)
  --> LocalService startHomeOnAllDisplays(……) LocalService是ActivityTaskManagerService 类的内部类
   -> RootWindowContainer startHomeOnAllDisplays(……)
    -> RootWindowContainer startHomeOnAllDisplays(int userId, String reason, int displayId)
     --> RootWindowContainer startHomeOnAllDisplays(int userId, String reason, int displayId, boolean allowInstrumenting, boolean fromHomeKey)
      --> DisplayContent reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea, allowInstrumenting, fromHomeKey),
false /* initValue */)
       --> RootWindowContainer startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea, boolean allowInstrumenting, boolean fromHomeKey)
        --> ActivityStartController. startHomeActivity(……)
         --> ActivityStarter execute()

  这里直接从RootWindowContainer startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea, boolean allowInstrumenting, boolean fromHomeKey)看,代码如下:

    boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,boolean allowInstrumenting, boolean fromHomeKey) {// Fallback to top focused display area if the provided one is invalid.if (taskDisplayArea == null) {final Task rootTask = getTopDisplayFocusedRootTask();taskDisplayArea = rootTask != null ? rootTask.getDisplayArea(): getDefaultTaskDisplayArea();}Intent homeIntent = null;ActivityInfo aInfo = null;if (taskDisplayArea == getDefaultTaskDisplayArea()) {homeIntent = mService.getHomeIntent();aInfo = resolveHomeActivity(userId, homeIntent);} else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea);aInfo = info.first;homeIntent = info.second;}if (aInfo == null || homeIntent == null) {return false;}if (!canStartHomeOnDisplayArea(aInfo, taskDisplayArea, allowInstrumenting)) {return false;}// Updates the home component of the intent.homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);// Updates the extra information of the intent.if (fromHomeKey) {homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);if (mWindowManager.getRecentsAnimationController() != null) {mWindowManager.getRecentsAnimationController().cancelAnimationForHomeStart();}}homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason);// Update the reason for ANR debugging to verify if the user activity is the one that// actually launched.final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(aInfo.applicationInfo.uid) + ":" + taskDisplayArea.getDisplayId();mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,taskDisplayArea);return true;}

  taskDisplayArea是不为null的,它的构建可以参考看一下这篇文章 Android 窗口结构(一) 窗口层级构造。
  Android是支持多屏幕的,这里咱们针对单屏幕来说。也即是满足第一个if分支的情况。下面是得到homeIntent,然后通过homeIntent再得到aInfo。
  这里mService是ActivityTaskManagerService对象,它的getHomeIntent()得到的Intent对象,action为Intent.ACTION_MAIN,Category为Intent.CATEGORY_HOME。这个是Launcher的主Activity配置。
  接着通过resolveHomeActivity(userId, homeIntent)解析该Intent对象,得到对应的Activity信息对象aInfo。这块如果只存在一个Launcher,它就返回对应Launcher主Activity的信息;如果是配置了多个Launcher,它会返回ResolverActivity的信息,如果是这样,等下面启动ResolverActivity时,它会显示一个界面 ,让选择运行哪个Launcher。这里咱们就说一个Launcher的情况。
  接下来,它会配置Intent对象的ComponentName。它包括包名和Activity名字。还会加上FLAG_ACTIVITY_NEW_TASK标识,它代表启动一个新的Task。
  再接下来就是调用ActivityStartController类对象startHomeActivity()方法去启动Activity。mService.getActivityStartController()是ActivityTaskManagerService对象的成员mActivityStartController,它是ActivityStartController类型。

  ActivityStartController类的startHomeActivity方法如下:

    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,TaskDisplayArea taskDisplayArea) {final ActivityOptions options = ActivityOptions.makeBasic();options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);if (!ActivityRecord.isResolverActivity(aInfo.name)) {// The resolver activity shouldn't be put in root home task because when the// foreground is standard type activity, the resolver activity should be put on the// top of current foreground instead of bring root home task to front.options.setLaunchActivityType(ACTIVITY_TYPE_HOME);}final int displayId = taskDisplayArea.getDisplayId();options.setLaunchDisplayId(displayId);options.setLaunchTaskDisplayArea(taskDisplayArea.mRemoteToken.toWindowContainerToken());// The home activity will be started later, defer resuming to avoid unnecessary operations// (e.g. start home recursively) when creating root home task.mSupervisor.beginDeferResume();final Task rootHomeTask;try {// Make sure root home task exists on display area.rootHomeTask = taskDisplayArea.getOrCreateRootHomeTask(ON_TOP);} finally {mSupervisor.endDeferResume();}mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason).setOutActivity(tmpOutRecord).setCallingUid(0).setActivityInfo(aInfo).setActivityOptions(options.toBundle()).execute();mLastHomeActivityStartRecord = tmpOutRecord[0];if (rootHomeTask.mInResumeTopActivity) {// 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();}}

  首先设置参数ActivityOptions类型options,先设置窗口模式全屏(WINDOWING_MODE_FULLSCREEN),如果启动的Activity不是ResolverActivity,设置启动Activty类型为ACTIVITY_TYPE_HOME,设置显示屏Id。
  taskDisplayArea.mRemoteToken是RemoteToken类型对象,它继承自IWindowContainerToken.Stub,用来实现Binder通信。taskDisplayArea.mRemoteToken.toWindowContainerToken()则是WindowContainerToken(实现Parcelable接口,用来序列化)对象。这里也将它设置到options中。
  再接下来就从taskDisplayArea中获取RootHomeTask。因为RootHomeTask的初始化在DisplayContent初始化中,DisplayContent初始化是在RootWindowContainer的setWindowManager(WindowManagerService wm)中,它又在ActivityManagerService的setWindowManager(WindowManagerService wm)中,所以现在它已经创建了。创建的过程参考 Android 窗口结构(二) 添加Home Task。
  obtainStarter(intent, "startHomeActivity: " + reason)得到的是ActivityStarter对象,接着会向其设置intent对象,还会调用setCallingUid(0)、setActivityInfo(aInfo)、setActivityOptions(options.toBundle())分别设置调用Uid,启动的Activity信息、设置参数。最后调用ActivityStarter对象的execute()方法,执行。
  tmpOutRecord是一个数组,是一个输出参数,所以上述方法调用完毕,之后,将它设置到mLastHomeActivityStartRecord中,作为最后一次启动的Home Activity。
  如果rootHomeTask.mInResumeTopActivity为true,代表Home Task已经在执行恢复Activity的操作了,导致现在的Home Activity没有执行resume。所以这里接着调用mSupervisor.scheduleResumeTopActivities()继续执行Home Activity的resume。
  下面进入ActivityStarter类中看一下它的execute()方法,如下:

    /*** Resolve necessary information according the request parameters provided earlier, and execute* the request which begin the journey of starting an activity.* @return The starter result.*/int execute() {try {// Refuse possible leaked file descriptorsif (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) {throw new IllegalArgumentException("File descriptors passed in Intent");}final LaunchingState launchingState;synchronized (mService.mGlobalLock) {final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo);final int callingUid = mRequest.realCallingUid == Request.DEFAULT_REAL_CALLING_UID?  Binder.getCallingUid() : mRequest.realCallingUid;launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mRequest.intent, caller, callingUid);}// If the caller hasn't already resolved the activity, we're willing// to do so here. If the caller is already holding the WM lock here,// and we need to check dynamic Uri permissions, then we're forced// to assume those permissions are denied to avoid deadlocking.if (mRequest.activityInfo == null) {mRequest.resolveActivity(mSupervisor);}// Add checkpoint for this shutdown or reboot attempt, so we can record the original// intent action and package name.if (mRequest.intent != null) {String intentAction = mRequest.intent.getAction();String callingPackage = mRequest.callingPackage;if (intentAction != null && callingPackage != null&& (Intent.ACTION_REQUEST_SHUTDOWN.equals(intentAction)|| Intent.ACTION_SHUTDOWN.equals(intentAction)|| Intent.ACTION_REBOOT.equals(intentAction))) {ShutdownCheckPoints.recordCheckPoint(intentAction, callingPackage, null);}}int res;synchronized (mService.mGlobalLock) {final boolean globalConfigWillChange = mRequest.globalConfig != null&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();if (rootTask != null) {rootTask.mConfigWillChange = globalConfigWillChange;}ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config "+ "will change = %b", globalConfigWillChange);final long origId = Binder.clearCallingIdentity();res = resolveToHeavyWeightSwitcherIfNeeded();if (res != START_SUCCESS) {return res;}res = executeRequest(mRequest);Binder.restoreCallingIdentity(origId);if (globalConfigWillChange) {// If the caller also wants to switch to a new configuration, do so now.// This allows a clean switch, as we are waiting for the current activity// to pause (so we will not destroy it), and have not yet started the// next activity.mService.mAmInternal.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,"updateConfiguration()");if (rootTask != null) {rootTask.mConfigWillChange = false;}ProtoLog.v(WM_DEBUG_CONFIGURATION,"Updating to new configuration after starting activity.");mService.updateConfigurationLocked(mRequest.globalConfig, null, false);}// The original options may have additional info about metrics. The mOptions is not// used here because it may be cleared in setTargetRootTaskIfNeeded.final ActivityOptions originalOptions = mRequest.activityOptions != null? mRequest.activityOptions.getOriginalOptions() : null;// If the new record is the one that started, a new activity has created.final boolean newActivityCreated = mStartActivity == mLastStartActivityRecord;// Notify ActivityMetricsLogger that the activity has launched.// ActivityMetricsLogger will then wait for the windows to be drawn and populate// WaitResult.mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,newActivityCreated, mLastStartActivityRecord, originalOptions);if (mRequest.waitResult != null) {mRequest.waitResult.result = res;res = waitResultIfNeeded(mRequest.waitResult, mLastStartActivityRecord,launchingState);}return getExternalResult(res);}} finally {onExecutionComplete();}}

  如果Intent中含有文件描述符,是会抛出IllegalArgumentException异常的。
  mSupervisor.getActivityMetricsLogger()是ActivityMetricsLogger对象,这个类是用来监听Activity启动、过渡、可见性改变、窗口绘制进行回调的。它的notifyActivityLaunching()就是通知Activity启动了。在这里它会记录启动的时间。
  如果mRequest.activityInfo == null,会需要解析它,这里前面已经设置它了,不需要解析它。
  如果intentAction和调用的包名callingPackage不为null,并且是关机或者重启的请求,会添加检查点。
  变量globalConfigWillChange是全局的配置是否发生了变化,是用mRequest.globalConfig(不能为null)和mService.getGlobalConfiguration()进行比较,如果不同即发生了变化。这里因为没有设置mRequest.globalConfig,所以它为null。globalConfigWillChange也为false。
  mRootWindowContainer.getTopDisplayFocusedRootTask()里面目前也就只有一个Home Task。所以这里就得到它。
  resolveToHeavyWeightSwitcherIfNeeded()是在必要的情况下(需要满足配置特色条件),在请求的进程uid和当前的重量级进程不同的情况下,需要跳转到HeavyWeightSwitcherActivity中去操作处理。目前这个暂时忽略。
  executeRequest(mRequest)就是去执行启动Activity的操作。这个可以参考 Android Activity的启动器ActivityStarter入口。其中Home ActivityRecord添加到Task中,也是由它实现的。这里,它主要是由addOrReparentStartingActivity方法实现的。
  下面globalConfigWillChange为false,所以不用更新配置。
  mStartActivity、mLastStartActivityRecord都是在executeRequest(mRequest)中赋值的,只不过赋值的地方不同。mLastStartActivityRecord是在ActivityRecord对象刚创建的时候设置的,而mStartActivity是在启动Activity时设置的,所以它俩一致,代表Activity创建成功,所以newActivityCreated为true。
  接下来就是调用ActivityMetricsLogger对象的notifyActivityLaunched方法,通知Activity已经启动完。
  如果设置了mRequest.waitResult,则需要对它进行处理。并将它的处理结果返回给res。
  getExternalResult(res)是在res为START_ABORTED时,也返回START_SUCCESS。
  onExecutionComplete()主要是处理启动器类ActivityStarter对象的回收。

http://www.dtcms.com/a/434346.html

相关文章:

  • 峨边网站建设网站iis安全配置
  • CMU与谷歌提出FM-SIREN:受奈奎斯特定理启发,让神经元“各司其职”,特征冗余降低50%
  • 【软件安全】fgets / strncpy / gets(不安全) / snprintf的对比
  • 济南免费做网站四平网站建设联系方式
  • 向量数据库前沿:Faiss 向量数据库的配置与使用
  • 机床铸铁底座在高端机床行业中的核心作用
  • 我为您整理出了 Coolify 可以添加的所有服务类型,并附上其用途说明。
  • 《回溯 C++98:string 核心机制拆解 —— 从拷贝策略到高效 swap》
  • JAVA过时了吗?
  • fnos安装并更新最新版sunpanel(显示为套件)
  • 资阳网站建设资阳河南郑州地图
  • 【Java初学基础11】Annotation-注解
  • OSPF LSA Type 2(Network LSA)概念及题目
  • OSPF 伪节点(Transit Node) 概念及题目
  • Android 开发 | 提取已安装应用的安装包 apk 的方式
  • 商务网站建设步骤犀牛云网站怎么建设
  • Vite 前端构建工具入门教程
  • 一种利用 qBittorrent 的 WebUI API 实现的检查BT种子的磁力链接是否可用的程序
  • nodejs换源管理工具nrm
  • async/await的基本使用以及fetchAPI的部分细节
  • MySQL新学知识(一)
  • 小迪web自用笔记47
  • 前端如何优雅地生成唯一标识?——一份跨环境 UUID 工具函数的封装与实战
  • iBizModel 应用程序(PSSYSAPP)模型体系详解
  • iis 网站 起不来 temp文件夹html网站建设心得体会
  • Ubuntu防火墙端口管理指南
  • Ubuntu离线安装软件包
  • 山东电力建设网站泉州专业建站品牌
  • 微服务项目部署配置文件示例:从开发到生产的完整指南
  • 声卡驱动解决方案(电脑没有声音,麦克风没有声音)win11