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

Activity相关学习(二)

Activity启动流程(基于android-13.0.0_r83)

整体流程

启动方式

Activity主要有三种方式

  • 从 Launcher 桌面上点击 App 图标启动一个App。
  • App 启动后,按 Home 键退回到 Launcher 界面,再点击 App 图标。
  • 同个应用内启动,如从 Activity1 跳转到 Activity2。
启动过程涉及到的进程
  • 要启动的Activity所在App 进程
  • SystemServer进程
  • Zygote
  • 启动目标Activity的进程,如Launcher

Activity启动过程跟系统交互主要是通过Binder,其中主要涉及两个server,一个是SystemServer中注册的ActivityTaskManagerService,另外一个是app进程中的ApplicationThread。

ActivityTaskManagerService
  • ActivityTaskManagerService(简称 ATMS)是 Android 系统中负责管理 Activity 和 Task 的核心服务之一,运行在system_server 进程中。
  • 从 Android 10(Q)开始,Google 对 Activity 和 Task 的管理机制进行了重构,将原本在 ActivityManagerService(AMS)中的部分功能拆分到了 ActivityTaskManagerService 中。
  • 负责管理 Activity 的生命周期、任务栈(Task)、多窗口模式(如分屏、自由窗口)等。
ATMS 的作用
  • Activity 生命周期管理: 负责 Activity 的启动、暂停、恢复、销毁等生命周期回调。
  • Task 管理: 管理任务栈(Task),包括 Task 的创建、销毁、重新排序等。
  • 多窗口模式: 支持分屏、画中画、自由窗口等多窗口模式的实现。
  • 启动模式(Launch Mode): 处理 Activity 的启动模式(如 standard、singleTop、singleTask、singleInstance)。
  • 任务栈导航: 管理任务栈的导航行为(如返回栈、最近任务列表)。
  • 与 WindowManagerService 协作: 与 WindowManagerService(WMS)协作,管理 Activity 的窗口显示。
ATMS 的相关架构
  • ActivityManagerService(AMS): 负责进程管理、内存管理等,与 ATMS 协作完成 Activity 的管理。
  • WindowManagerService(WMS): 负责窗口管理,与 ATMS 协作完成 Activity 的显示。
  • ActivityStack: 表示一个任务栈(Task),管理栈中的 Activity。
  • ActivityRecord: 表示一个 Activity 实例,包含 Activity 的状态信息。
ATMS 的核心功能
  1. Activity 生命周期管理
    ATMS 负责处理 Activity 的生命周期回调,当应用启动一个 Activity 时,ATMS 会调用相应的生命周期方法,并确保 Activity 的状态正确切换。包括:
  • onCreate()
  • onStart()
  • onResume()
  • onPause()
  • onStop()
  • onDestroy()
  1. Task 管理
    Task 是一个 Activity 的集合,通常表示一个用户任务(如打开一个应用)。ATMS 负责管理 Task 的创建、销毁和重新排序。
  • 当用户点击桌面图标启动应用时,ATMS 会创建一个新的 Task。
  • 当用户按下返回键时,ATMS 会销毁当前 Activity 并返回到上一个 Activity。
  1. 多窗口模式
    ATMS 支持多种多窗口模式, ATMS 负责管理多窗口模式下 Activity 的布局和生命周期。包括:
  • 分屏模式(Split-screen): 两个应用共享屏幕。
  • 画中画模式(Picture-in-Picture): 视频播放时以小窗口形式显示。
  • 自由窗口模式(Freeform): 类似于桌面操作系统的窗口模式。
  1. 启动模式(Launch Mode)

  2. 任务栈导航
    ATMS 管理任务栈的导航行为,包括:

  • 返回栈(Back Stack): 当用户按下返回键时,ATMS 会从返回栈中弹出 Activity。
  • 最近任务列表(Recent Tasks): ATMS 负责维护最近任务列表,用户可以快速切换任务。
ATMS 的实现

ATMS是通过AIDL实现的
在这里插入图片描述

ApplicationThread
  • ApplicationThread 是 ActivityThread 的内部类,扮演着应用进程与系统服务(如 ActivityManagerService 和 ActivityTaskManagerService)之间的桥梁角色。
  • ApplicationThread 是一个 Binder 对象,负责接收系统服务的指令并转发给应用进程的主线程(即 ActivityThread),从而实现对应用生命周期、Activity 生命周期等的管理。
ApplicationThread 的作用
  • 接收系统服务的指令: 接收来自 ActivityManagerService(AMS)和 ActivityTaskManagerService(ATMS)的指令,例如启动 Activity、启动 Service、绑定 Service、处理广播等。
  • 转发指令到主线程: 将系统服务的指令转发到应用的主线程(即 ActivityThread),确保这些操作在主线程中执行。
  • 与应用组件交互: 与应用的 Activity、Service、BroadcastReceiver 等组件交互,触发它们的生命周期方法(如 onCreate()、onStart()、onResume() 等)。
ApplicationThread 的核心方法

这些方法都是由系统服务(如 AMS 或 ATMS)调用,然后通过 Binder 机制传递到应用进程的 ApplicationThread,最终由 ActivityThread 在主线程中执行。

  • 启动 Activity: scheduleLaunchActivity()
  • 启动 Service: scheduleCreateService()
  • 绑定 Service: scheduleBindService()
  • 处理广播: scheduleReceiver()
  • 暂停 Activity: schedulePauseActivity()
  • 销毁 Activity: scheduleDestroyActivity()
ApplicationThread 的实现

ApplicationThread是基于AIDL实现
在这里插入图片描述

ActivityTaskManagerService和ApplicationThread调用关系

在这里插入图片描述

Launcher 界面中启动Activity的流程
流程

在这里插入图片描述

源码分析
  • 点击桌面图标会触发ItemClickHandler.java的onClick()。
/*
* @param v 被点击的视图对象(如应用图标、文件夹图标等)
**/
//packages/apps/Launcher3/src/com/android/launcher3/touch/ItemClickHandler.java
private static void onClick(View v) {
        ...
        if (v.getWindowToken() == null) return;
        //Launcher 是 Launcher 应用的主 Activity,负责管理主屏幕、应用图标、小部件等。
        Launcher launcher = Launcher.getLauncher(v.getContext());
        if (!launcher.getWorkspace().isFinishedSwitchingState()) return;
        //Launcher 中的每个视图(如应用图标、文件夹图标等)都关联了一个 ItemInfo 对象,用于存储该视图的元数据(如应用包名、组件名、位置信息等)。
        Object tag = v.getTag();
        //点击的是应用图标
        if (tag instanceof WorkspaceItemInfo) {
            onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
        } else if (tag instanceof FolderInfo) {//点击文件夹图标
            if (v instanceof FolderIcon) {
                onClickFolderIcon(v);
            }
        } else if (tag instanceof AppInfo) {//快捷方式
            startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
        } else if (tag instanceof LauncherAppWidgetInfo) {//点击的是小部件
            if (v instanceof PendingAppWidgetHostView) {
                onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
            }
        } else if (tag instanceof ItemClickProxy) {
            ((ItemClickProxy) tag).onItemClicked(v);
        }
    }

点击应用图标执行下面方法


    /**
     * Event handler for an app shortcut click.
     *
     * @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
     */
    public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
        ...

        // Start activities  启动activity
        startAppShortcutOrInfoActivity(v, shortcut, launcher);
    }

    private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
        ...
        Intent intent;
        ...
        //从 WorkspaceItemInfo 中获取 Intent,用于启动应用或显示应用信息。
        if (item instanceof WorkspaceItemInfo) {
            WorkspaceItemInfo si = (WorkspaceItemInfo) item;
            if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)
                    && Intent.ACTION_VIEW.equals(intent.getAction())) {
                // make a copy of the intent that has the package set to null
                // we do this because the platform sometimes disables instant
                // apps temporarily (triggered by the user) and fallbacks to the
                // web ui. This only works though if the package isn't set
                intent = new Intent(intent);
                intent.setPackage(null);
            }
            if ((si.options & WorkspaceItemInfo.FLAG_START_FOR_RESULT) != 0) {
                launcher.startActivityForResult(item.getIntent(), 0);
                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
                launcher.logAppLaunch(launcher.getStatsLogManager(), item, instanceId);
                return;
            }
        }
        ...
        // 启动普通应用
        launcher.startActivitySafely(v, intent, item);
    }

  • 触发Launcher.java的startActivitySafely
    //packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
    @Override
    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
        // Only pause is taskbar controller is not present
        mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
        //调用父类即Launcher.java
        boolean started = super.startActivitySafely(v, intent, item);
        if (getTaskbarUIController() == null && !started) {
            mHotseatPredictionController.setPauseUIUpdate(false);
        }
        return started;
    }
//packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
@Override
    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
        //检查 Launcher 是否已经处于 resumed 状态(即完全恢复并可见)。
        //如果 Launcher 未完全恢复,直接启动 Activity 可能会导致窗口管理器(WM)的启动动画被破坏。
        if (!hasBeenResumed()) {
            //如果 Launcher 未完全恢复,则延迟启动 Activity,直到 Launcher 下次恢复。
            //使用 addOnResumeCallback 方法注册一个回调,在 Launcher 恢复时执行 startActivitySafely。
            addOnResumeCallback(() -> startActivitySafely(v, intent, item));
            if (mOnDeferredActivityLaunchCallback != null) {
                mOnDeferredActivityLaunchCallback.run();
                mOnDeferredActivityLaunchCallback = null;
            }
            return true;
        }

       //调用父类ActivityContext的 startActivitySafely 方法
        boolean success = super.startActivitySafely(v, intent, item);
        //BubbleTextView 是 Launcher 中用于显示应用图标和文件夹图标的视图。
        //如果成功启动 Activity,并且触发视图是 BubbleTextView,则设置其按压状态为 true。
        if (success && v instanceof BubbleTextView) {           
            BubbleTextView btv = (BubbleTextView) v;
            btv.setStayPressed(true);
            //使用 addOnResumeCallback 方法注册一个回调,在 Launcher 恢复时将按压状态重置为 false。
            addOnResumeCallback(() -> btv.setStayPressed(false));
        }
        return success;
    }
  • 触发ActivityContext.java的startActivitySafely

主要包含以下几个步骤

  1. 主线程检查:确保方法在主线程中调用。
  2. 安全模式检查:在安全模式下阻止非系统应用的启动。
  3. 快捷方式启动:支持深度快捷方式的启动。
  4. 启动选项:根据视图和 ItemInfo 准备启动选项。
  5. 用户句柄:支持跨用户启动 Activity。
  6. 日志记录:记录应用启动日志。
  7. 异常处理:捕获并处理启动过程中的异常。
    //packages/apps/Launcher3/src/com/android/launcher3/views/ActivityContext.java
    /**
     * 安全地启动一个 Activity,并处理一些特殊情况(如安全模式、快捷方式启动、跨用户启动等)
     * @param v 触发启动操作的视图(如应用图标)。
     * @param intent 要启动的 Activity 的 Intent。
     * @param item 与视图关联的 ItemInfo 对象(如 WorkspaceItemInfo),可以为 null。
     * @return RunnableList 用于监听动画结束的回调列表。如果启动失败,则返回 null。
     */
    default RunnableList startActivitySafely(
            View v, Intent intent, @Nullable ItemInfo item) {
        //确保该方法在主线程(UI 线程)中调用,避免在后台线程中启动 Activity。
        Preconditions.assertUIThread();
        Context context = (Context) this;
        //检查安全模式
        //如果设备处于安全模式,并且目标应用不是系统应用,则阻止启动并显示提示信息。
        if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
            Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
            return null;
        }
        //如果 ItemInfo 是深度快捷方式(ITEM_TYPE_DEEP_SHORTCUT),并且不是占位符(isPromise() 返回 false),则标记为快捷方式启动。
        boolean isShortcut = (item instanceof WorkspaceItemInfo)
                && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
                && !((WorkspaceItemInfo) item).isPromise();
        //GO_DISABLE_WIDGETS:如果为 true,则禁用快捷方式启动,并返回 null。
        if (isShortcut && GO_DISABLE_WIDGETS) {
            return null;
        }
        //准备启动选项
        //如果视图 v 为 null,则调用 makeDefaultActivityOptions 方法创建默认启动选项。
        ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
                : makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
                        ? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
        //设置用户句柄
        UserHandle user = item == null ? null : item.user;
        Bundle optsBundle = options.toBundle();
        // 准备 Intent
        //设置 Intent 的标志位,确保目标 Activity 在新的任务栈中启动。
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (v != null) {
            //用于动画过渡效果。
            intent.setSourceBounds(Utilities.getViewBounds(v));
        }
        //启动Activity
        try {
            //快捷方式启动
            if (isShortcut) {
                String id = ((WorkspaceItemInfo) item).getDeepShortcutId();
                String packageName = intent.getPackage();
                ((Context) this).getSystemService(LauncherApps.class).startShortcut(
                        packageName, id, intent.getSourceBounds(), optsBundle, user);
            } else if (user == null || user.equals(Process.myUserHandle())) {
                // Could be launching some bookkeeping activity
                context.startActivity(intent, optsBundle);//普通启动
            } else {
                //如果用户句柄不是当前用户,跨用户启动 Activity。
                context.getSystemService(LauncherApps.class).startMainActivity(
                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
            }
            ...
            return options.onEndCallback;
        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
            ...
        }
        return null;
    }
Activity启动流程
  • 执行Activity的startActivity()
   //frameworks/base/core/java/android/app/Activity.java
    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        getAutofillClientController().onStartActivity(intent, mIntent);
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            startActivityForResult(intent, -1);
        }
    }

startActivityForResult主要包含以下几个步骤

  • 启动目标 Activity:
  • 通过 Instrumentation.execStartActivity 方法启动目标 Activity。
  • 处理返回结果:
    如果目标 Activity 立即返回结果,则通过 mMainThread.sendActivityResult 方法将结果发送给当前 Activity。
  • 标记 Activity 已启动:
    如果 requestCode 大于等于 0,则标记当前 Activity 已启动目标 Activity。
  • 取消输入并启动退出过渡:
    取消当前 Activity 的输入事件,并启动退出过渡动画。
  • 支持子 Activity:
    如果当前 Activity 有父 Activity,则通过父 Activity 启动目标 Activity。
     /*
     * @param intent 要启动的 Activity 的 Intent。
     * @param requestCode 请求代码,用于标识启动请求。当目标 Activity 返回结果时,该代码会传递给 onActivityResult 方法。
     * @param options 启动选项(如动画、过渡效果等),可以为 null。
     * @see #startActivity
     */
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        // 检查是否有父 Activity
        if (mParent == null) {
            //将启动选项转换为适合当前 Activity 的格式。例如,处理与启动动画相关的选项。
            options = transferSpringboardActivityOptions(options);
            //通过 Instrumentation 执行实际的 Activity 启动操作。
            //参数包括当前 Activity 的上下文、应用线程、Activity 的 IBinder 令牌、目标 Intent、请求代码和启动选项。
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            //如果目标 Activity 立即返回结果(如 RESULT_CANCELED),则通过主线程将结果发送给当前 Activity。
            //结果会传递给 onActivityResult 方法。
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                // 如果 requestCode 大于等于 0,则标记当前 Activity 已启动目标 Activity。
                // 这可以避免在目标 Activity 返回结果之前显示当前 Activity,从而减少闪烁。
                mStartedActivity = true;
            }
            //取消输入并启动退出过渡
            //取消当前 Activity 的输入事件(如键盘输入)。
            //启动退出过渡动画(如 Activity 切换动画)。
            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            //调用父 Activity 的方法
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }
Instrumentation阶段

Instrumentation 用于监控应用程序与系统的交互。它在应用程序的整个生命周期中扮演着重要角色,尤其是在 Activity 的启动、生命周期回调、以及测试框架中。

Instrumentation 的作用
  • 监控应用程序的生命周期:如 Activity 的启动、停止、销毁等。
  • 注入测试代码:在 Android 测试框架中,Instrumentation 用于注入测试代码,监控应用程序的行为。
  • 提供上下文和环境:为应用程序提供测试所需的上下文和环境。
  • 处理系统交互:如启动 Activity、发送广播、处理 Intent 等。
Instrumentation 的核心方法
  • execStartActivity 启动一个 Activity。
  • callActivityOnCreate 调用 Activity 的 onCreate 方法。
  • callActivityOnResume 调用 Activity 的 onResume 方法。
  • newActivity 创建一个新的 Activity 实例。
  • sendKeySync 同步发送一个按键事件。
  • runOnMainSync 在主线程中同步执行一个 Runnable。
execStartActivity 执行启动 Activity

execStartActivity 主要包括以下几个步骤

  • IApplicationThread:用于与系统服务通信。
  • Referrer:处理 Activity 的来源信息。
  • ActivityMonitor:监控和拦截 Activity 启动。
  • Intent 准备:确保 Intent 可以跨进程传递。
  • ActivityTaskManager:与系统服务交互,启动 Activity。
  • 异常处理:捕获并处理启动过程中的异常。
     //frameworks/base/core/java/android/app/Instrumentation.java
     /*
     * @param who 启动 Activity 的上下文(通常是当前 Activity)。
     * @param contextThread 应用的主线程(IApplicationThread 类型)。
     * @param token 当前 Activity 的 IBinder 令牌。
     * @param target 目标 Activity(可以为 null)。
     * @param intent 要启动的 Activity 的 Intent。
     * @param requestCode 请求代码(用于 startActivityForResult)。
     * @param options 启动选项(如动画、过渡效果等)。
     *
     * @return 返回一个 ActivityResult 对象,包含目标 Activity 的结果(如结果代码和返回数据)。如果启动失败,则返回 null。
     * {@hide}
     */
    @UnsupportedAppUsage
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        //获取 IApplicationThread,表示应用的主线程,用于与系统服务(如 ActivityTaskManager)通信。
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        //处理 Referrer,将 Referrer 添加到 Intent 的 EXTRA_REFERRER 字段中。
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        //处理 ActivityMonitor,用于监控和拦截 Activity 启动。
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                //遍历所有的 ActivityMonitor。
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    ActivityResult result = null;
                    //如果 ActivityMonitor 忽略特定的 Intent,则调用 onStartActivity 方法处理。
                    if (am.ignoreMatchingSpecificIntents()) {
                        if (options == null) {
                            options = ActivityOptions.makeBasic().toBundle();
                        }
                        result = am.onStartActivity(who, intent, options);
                    }
                    //如果 ActivityMonitor 匹配当前的启动请求,则根据其配置决定是否拦截启动。
                    if (result != null) {
                        am.mHits++;
                        return result;
                    } else if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
           //将 Intent 中的 EXTRA_STREAM 数据迁移到 ClipData 中。
            intent.migrateExtraStreamToClipData(who);
            //准备 Intent,确保它可以跨进程传递。
            intent.prepareToLeaveProcess(who);
            //启动 Activity
            //获取 ActivityTaskManager 的系统服务。
            //调用 ActivityTaskManager 的 startActivity 方法,启动目标 Activity。
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getOpPackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                    target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
            //通知 Activity 启动结果。
            notifyStartActivityResult(result, options);
            //检查启动结果,如果失败则抛出异常。
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

execStartActivity执行后启动过程就会进入到服务端 SystemServer的 ATMS Binder 服务中去了。


http://www.kler.cn/a/539277.html

相关文章:

  • Qt通过FFmpeg打开RTSP并截图一帧作为背景
  • java项目之金华学校社团管理系统源码(ssm+mysql)
  • 视觉硬件选型和算法选择(CNN)
  • RuoYi-Vue-Oracle的oracle driver驱动配置问题ojdbc8-12.2.0.1.jar的解决
  • Maven入门核心知识点总结
  • 【Nginx + Keepalived 实现高可用的负载均衡架构】
  • mmap 文件映射
  • 【C编程问题集中营】使用数组指针时容易踩得坑
  • 单片机之基本元器件的工作原理
  • 4.寻找两个正序数组的中位数
  • PAT乙级真题 — 1074 宇宙无敌加法器(java)
  • 降低获客与裂变渠道成本的新策略:融合开源2+1链动模式、AI智能名片与S2B2C商城小程序
  • Linux 创建进程 fork()、vfork() 与进程管理
  • python基础入门:3.5实战:词频统计工具
  • 问卷数据分析|SPSS之分类变量描述性统计
  • 深入浅出:SSL证书的作用与重要性
  • 第二十一章:考研的艰难抉择与放弃入学的转折
  • 基于javaweb的SpringBoot+MyBatis毕业设计选题答辩管理系统(源码+文档+部署讲解)
  • PromptSource安装报错
  • 科普书《从一到无穷大》的科普知识推翻百年集论
  • PlantUml常用语法
  • 青少年编程与数学 02-009 Django 5 Web 编程 02课题、开发环境
  • DeepSeek在无人机上应用技术详解
  • leetcode_80删除有序数组中的重复项 II
  • 【算法】快速排序算法的实现:C 和 C++ 版本
  • 信息学奥赛一本通1003