Android分屏流程分析

这篇具有很好参考价值的文章主要介绍了Android分屏流程分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文基于Android 11。

SystemUI模块中的Divider管理着所有关于分屏的对象:

  • DividerView(分屏分割线,分屏显示界面)
  • SplitScreenTaskOrganizer(分屏Task组织者,分屏逻辑)

这里重点关注分屏逻辑实现SplitScreenTaskOrganizer。

Devider类实现了DisplayController.OnDisplaysChangedListener,系统启动后回调onDisplayAdded():

// Devider.java
@Override
public void onDisplayAdded(int displayId) {
    mSplits.init();
}

调用了SplitScreenTaskOrganizer对象的init()方法:

class SplitScreenTaskOrganizer extends TaskOrganizer {
    void init() throws RemoteException {
        registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
        registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
        synchronized (this) {
            try {
                mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
                        WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
                mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
                        WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
            } catch (Exception e) {
                // teardown to prevent callbacks
                unregisterOrganizer();
                throw e;
            }
        }
    }
}

SplitScreenTaskOrganizer继承了TaskOrganizer,TaskOrganizer给ActivityTaskManager/WindowManager提供了管理task的接口。

/**
 * Interface for ActivityTaskManager/WindowManager to delegate control of tasks.
 */

TaskOrganizer又被TaskOrganizerController统一管理。

/**
 * Stores the TaskOrganizers associated with a given windowing mode and
 * their associated state.
 */

回过来看init()方法的实现,主要是两个方法:

  • registerOrganizer()
  • TaskOrganizer.createRootTask()

1.registerOrganizer

registerOrganizer(int windowingMode)方法接收int类型的windowingMode参数,分别传入了WINDOWING_MODE_SPLIT_SCREEN_PRIMARY和WINDOWING_MODE_SPLIT_SCREEN_SECONDARY参数,registerOrganizer()方法在父类TaskOrganizer实现,通过TaskOrganizerController对象注册。

  • WINDOWING_MODE_SPLIT_SCREEN_PRIMARY(分屏中的主屏)
  • WINDOWING_MODE_SPLIT_SCREEN_SECONDARY(分屏中的副屏)

1.1 TaskOrganizerController

// TaskOrganizerController.java

private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();

@Override
public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
    
    // 添加到变量 mTaskOrganizersForWindowingMode
    LinkedList<IBinder> orgs = mTaskOrganizersForWindowingMode.get(windowingMode);
    if (orgs == null) {
        orgs = new LinkedList<>();
        mTaskOrganizersForWindowingMode.put(windowingMode, orgs);
    }
    orgs.add(organizer.asBinder());
    
    // 添加到 mTaskOrganizerStates 管理。
    if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
        mTaskOrganizerStates.put(organizer.asBinder(),
                new TaskOrganizerState(organizer, uid));
    }
    
    // 更新task状态,通知task对象已经被organized,关联TaskOrganizer对象,task.setTaskOrganizer(ITaskOrganizer organizer)
    mService.mRootWindowContainer.forAllTasks((task) -> {
        if (task.getWindowingMode() == windowingMode) {
            task.updateTaskOrganizerState(true /* forceUpdate */);
        }
    });
}

TaskOrganizerController将ITaskOrganizer对象添加到mTaskOrganizerStates管理,mTaskOrganizerStates是一个HashMap,key是Binder对象,value则是内部的代理类TaskOrganizerState,当对应的task状态变化回调时再从这个HashMap取出;更新task状态,通知task对象已经被organized,关联TaskOrganizer对象,task.setTaskOrganizer(ITaskOrganizer organizer)。

1.2 Task

// Task.java
boolean updateTaskOrganizerState(boolean forceUpdate) {
    // 从TaskOrganizerController.mTaskOrganizersForWindowingMode获取ITaskOrganizer对象
    final ITaskOrganizer org =
            mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
    final boolean result = setTaskOrganizer(org);
    mLastTaskOrganizerWindowingMode = windowingMode;
    return result;
}

setTaskOrganizer()方法关联ITaskOrganizer对象,更新变量mLastTaskOrganizerWindowingMode。

// Task.java
boolean setTaskOrganizer(ITaskOrganizer organizer) {
    mTaskOrganizer = organizer;
}

@Override
boolean isOrganized() {
    return mTaskOrganizer != null;
}

后续task状态发生变化,通过isOrganized()方法判断是否需要回调通知mTaskOrganizer。

// ActivityStack.java
@Override
void onChildPositionChanged(WindowContainer child) {
    if (isOrganized()) {
        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
    }
}

ActivityStack继承Task,在onChildAdded(),onChildRemoved(),positionChildAt()等改变父子层级的方法都会调用onChildPositionChanged(),如果mTaskOrganizer对象不为null,通过mTaskOrganizerController分发事件信息,接着就是mTaskOrganizerController从mTaskOrganizerStates变量中拿到对象的代理对象TaskOrganizerState回调onTaskAppeared(),onTaskVanished(),onTaskInfoChanged()等。

2.TaskOrganizer.createRootTask

TaskOrganizer.createRootTask()方法很简单,在Display.DEFAULT_DISPLAY中创建两个模式为WINDOWING_MODE_SPLIT_SCREEN_PRIMARY和WINDOWING_MODE_SPLIT_SCREEN_SECONDARY的空Task,不显示任何内容,且和其他task不同的是,其mCreatedByOrganizer为true。

如果是WINDOWING_MODE_SPLIT_SCREEN_PRIMARY模式,TaskDisplayArea还会保存其引用到mRootSplitScreenPrimaryTask变量,以便后续开启分屏时找到对应的task。

// TaskDisplayArea.java

private ActivityStack mRootSplitScreenPrimaryTask;

void addStackReferenceIfNeeded(ActivityStack stack) {
    if (windowingMode == WINDOWING_MODE_PINNED) {
        mRootPinnedTask = stack;
    } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
    	mRootSplitScreenPrimaryTask = stack;
    }
}

之后要开启分屏模式,就是将要分屏的task分别设置为其子task。

3.开启分屏

        ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
        options.setSplitScreenCreateMode(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
        Bundle optsBundle = options == null ? null : options.toBundle();
        ActivityTaskManager.getService().startActivityFromRecents(taskId, optsBundle);

要开启分屏需要在启动时添加ActivityOptions,setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY),通过ActivityTaskManagerService.startActivityFromRecents(int taskId, Bundle bOptions)开启。

分屏逻辑大体实现就是找到对应具体要显示的task,如果没有就重新创建,然后找到一开始TaskOrganizer.createRootTask()创建的mPrimary和mSecondary,通过reparent()或者addChild()设置为对应的父子关系。

3.1主屏初始化

// ActivityTaskManagerService.java
int startActivityFromRecents(int callingPid, int callingUid, int taskId,
        SafeActivityOptions options) {
    
    final ActivityOptions activityOptions = options != null
                ? options.getOptions(this)
                : null;
    // 主屏
    task = mRootWindowContainer.anyTaskForId(taskId,
                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
    
    // 副屏
    return mService.getActivityStartController().startActivityInPackage(task.mCallingUid,
                callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
                null, 0, 0, options, userId, task, "startActivityFromRecents",
                false /* validateIncomingUser */, null /* originatingPendingIntent */,
                false /* allowBackgroundActivityStart */);
}

用户开启分屏时,主屏一般是已经存在的task,不需要重新启动,可以通过mRootWindowContainer.anyTaskForId()找到对应的task。

而副屏一般是用户通过点击图标打开的,需要通过mService.getActivityStartController().startActivityInPackage()通过ActivityStater启动。

// RootWindowContainer.java
Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode,
        @Nullable ActivityOptions aOptions, boolean onTop) {
    
    // 具体要显示的task
    final PooledPredicate p = PooledLambda.obtainPredicate(
        Task::isTaskId, PooledLambda.__(Task.class), id);
    Task task = getTask(p);
    p.recycle();
    
    if (task != null) {
        if (aOptions != null) {
            
            // 主屏task: mode=split-screen-primary
            final ActivityStack launchStack =
                    getLaunchStack(null, aOptions, task, onTop);
            if (launchStack != null && task.getStack() != launchStack) {
                final int reparentMode = onTop
                        ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
                task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
                        "anyTaskForId");
            }
        }
        return task;
    }
}

先通过taskId找到具体要显示的task,然后通过getLaunchStack()方法找到之前创建的主屏task,第2小节中TaskDisplayArea保存的mRootSplitScreenPrimaryTask引用,将其设置为task的父task,task.reparent()。

3.2 副屏初始化

副屏初始化流程和主屏大同小异,一般需要通过ActivityStarter重新创建新task,设置副屏task为其父task。

// TaskDisplayArea.java
ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
        boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
    
    // 找到副屏 task
    Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
    if (launchRootTask != null) {
        // Since this stack will be put into a root task, its windowingMode will be inherited.
        windowingMode = WINDOWING_MODE_UNDEFINED;
    }
    
    // 创建具体要显示的task
    final ActivityStack stack = new ActivityStack(mAtmService, stackId, activityType,
           info, intent, createdByOrganizer);
    if (launchRootTask != null) {
        launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
        if (onTop) {
            positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
        }
    } else {
        addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
        stack.setWindowingMode(windowingMode, true /* creating */);
    }
    return stack;
}

对比一下分屏开启前后的window container结构:

  • 开启前:
    Android分屏流程分析
  • 开启后:
    Android分屏流程分析

4.分屏界面显示

在选中主屏task后系统就进入到了分屏界面,前面说过关于主副屏task的状态变化都会回调通知SplitScreenTaskOrganizer,包括上面的设置子task操作(reparent,addChild),回调onTaskInfoChanged(),handleTaskInfoChanged()。

// SplitScreenTaskOrganizer.java
private void handleTaskInfoChanged(RunningTaskInfo info) {
    
    final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
    final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
    if (info.token.asBinder() == mPrimary.token.asBinder()) {
        mPrimary = info;
    } else if (info.token.asBinder() == mSecondary.token.asBinder()) {
        mSecondary = info;
    }
    final boolean primaryIsEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
    final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
    
    if (primaryIsEmpty || secondaryIsEmpty) {
        if (mDivider.isDividerVisible()) {
            mDivider.startDismissSplit();
        } else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
            // 显示分屏界面
            mDivider.startEnterSplit();
        }
    } else if (secondaryImpliesMinimize) {
        mDivider.ensureMinimizedSplit();
    } else {
        mDivider.ensureNormalSplit();
    }
}

可以看到当主屏被填满时就开始显示分屏界面了。文章来源地址https://www.toymoban.com/news/detail-499209.html

到了这里,关于Android分屏流程分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android 11 SystemUI 启动流程

    从表面上看, 我们看到的 状态栏、通知栏、下拉菜单、导航栏、锁屏、最近任务、低电提示 等系统页面都是 SystemUI 的。SystemUI,在源码目录中位于: framework/base/packages 目录下, 可见 SystemUI 和 framework 是关联的, SystemUI 依赖了很多内部 API , 系统资源, SystemUI 编译是要依赖

    2024年02月13日
    浏览(40)
  • Android 10.0 SystemUI启动流程

    1、手机开机后,Android系统首先会创建一个Zygote(核心进程)。 2、由Zygote启动SystemServer。 3、SystemServer会启动系统运行所需的众多核心服务和普通服务、以及一些应用及数据。例如:SystemUI 启动就是从 SystemServer 里启动的。 4、进入锁屏界面,开机完成。 SystemServer 中有一个

    2024年02月05日
    浏览(49)
  • Android 9-SystemUI:(1)启动流程

    具体分析(以下代码示例,讲解,都是通过,Android9代码来举例) SystemUI,其实是可以看作是一个系统级的服务,也就是SystemUIService, SystemUI的服务启动,要从SystemServer.run()方法入手 main 方法里启动了 run() 方法,而在 run 方法中调用了startOtherServices() 方法,在启动startOtherServices(

    2024年02月11日
    浏览(44)
  • 车载Android应用开发与分析 - 初试 SystemUI Plugin

    在前面的视频、文章中我们介绍完了整个车载Android应用开发所需要的基础知识: 【视频文稿】车载Android应用开发与分析 - 走进车载操作系统 - 掘金 【视频文稿】车载Android应用开发与分析 - AOSP的下载与编译 - 掘金 【视频文稿】车载Android应用开发与分析 - 开发系统应用 - 掘

    2024年02月02日
    浏览(41)
  • Android 12 源码分析 —— 应用层 一(SystemUI准备篇)

    在接下来的时间中,将会使用Pixel 3(blueline)作为研究对象,选用AOSP的android-12.0.0_r34分支作源代码。 先从android的应用层进行探析,然后慢慢深入android的framework,接着进入android的hal层,最后以android的linux内核结束,期间可能会穿插一些其他文章如android的art虚拟机分析等。 本文

    2024年02月12日
    浏览(48)
  • Android 12 源码分析 —— 应用层 二(SystemUI大体组织和启动过程)

    在前一篇文章中,我们介绍了SystemUI怎么使用IDE进行编辑和调试。这是分析SystemUI的最基础,希望读者能尽量掌握。 本篇文章,将会介绍SystemUI的大概组织架构,以及它的启动过程。本篇文章读完,将会知道: SystemUI为什么选择使用Dagger2 SystemUI怎么新建一个模块 SystemUI的启动

    2024年02月06日
    浏览(81)
  • Android 10.0 系统systemui下拉通知栏的通知布局相关源码分析

     在android10.0的系统rom开发中,在进行systemui中的下拉通知栏的布局自定义的时候,对于原生systemui的 系统的下拉通知栏的通知布局的了解也是非常重要的,接下来就来分析下相关的下拉通知栏的通知布局的相关 源码流程,了解这些才方便对通知栏的布局做修改   在10.0的系统

    2023年04月21日
    浏览(48)
  • HIDL Service创建流程 - 基于Android 12 S分析

    系列文章 Android 12 S ServiceManager原理 Android 12 S Native Service的创建流程 Android 12 S Binder原理之BpBinder,BnBinder以及IInterface介绍 Android 12 S HIDL Service创建流程 Android 12 S 自定义Hal服务selinux权限添加 Android 12 S 自定义Native服务selinux权限添加 Android 12 S java服务调用native服务 Android 12 S 自定

    2024年02月11日
    浏览(39)
  • Android SystemUI源码分析与修改,作为Android程序员应该怎样去规划自己的学习路线

    systemui:keyCode=“4” android:layout_weight=“0” systemui:glowBackground=“@drawable/ic_sysbar_highlight” android:contentDescription=“@string/accessibility_back” / 音量减的布局如下,这里先把Visibility定义为Gone,然后在代码中控制是否显示: com.android.systemui.statusbar.policy.KeyButtonView android:id=“@+id/sub”

    2024年04月15日
    浏览(54)
  • Android 12 源码分析 —— 应用层 四(SystemUI的基本布局设计及其基本概念)

    更新历史 日期 内容 1 2023-9-11 增加文中提及的渐变动画的效果图 在上两篇文章中,我们介绍SystemUI的启动过程,以及基本的组件依赖关系。基本的依赖关系请读者一定要掌握,因为后面的文章,将会时常出现这些依赖关系的使用,届时将会一笔带过,而不会详细说明他们的实

    2024年02月08日
    浏览(67)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包