Android N分屏流程分析之一

本文详细解析了Android N系统中分屏功能的工作原理及其实现过程,包括用户长按Recents键触发分屏模式的具体代码流程,以及系统如何判断应用是否支持分屏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       在Android N上,谷歌制作了一个分屏的效果,即进入应用后,长按Recents键,进入到分屏状态。

                                

       左侧是进入分屏的应用,中间区域是可以滑动的线,右侧是recents列表。

       下面是进入分屏的完整的交互图:

下面开始一步步的详细介绍:

在上一篇文章中已经介绍了Navigationbar button添加显示和事件绑定的流程分析记录,这里不再赘述。接下来,我就按照用户的操作,一步步的分析界面的变化。

1.进入分屏场景分析:

       用户长按recents键,从响应长按事件开始。代码如下所示:

    private View.OnLongClickListener mRecentsLongClickListener = new View.OnLongClickListener() {

        @Override
        public boolean onLongClick(View v) {
            if (mRecents == null || !ActivityManager.supportsMultiWindow()
                    || !getComponent(Divider.class).getView().getSnapAlgorithm()
                            .isSplitScreenFeasible()) {
                return false;
            }

            toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
                    MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
            return true;
        }
    };

    @Override
    protected void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
        if (mRecents == null) {
            return;
        }
        int dockSide = WindowManagerProxy.getInstance().getDockSide();
        if (dockSide == WindowManager.DOCKED_INVALID) {
            mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
                    ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
        } else {
            EventBus.getDefault().send(new UndockingTaskEvent());
            if (metricsUndockAction != -1) {
                MetricsLogger.action(mContext, metricsUndockAction);
            }
        }
    }


        这段代码位于:framework/base/packages/SystemUI/src/.../PhoneStatusBar.java

        这段首先会判断mRecents是否为空,mRecents是Recents.class,在BaseStatusBar.java中有说明。

        判断当前分屏状态,通过这段代码可以获取到分屏的状态,判断是否处于分屏状态,如果当前是非分屏状态,那么获取到的值就是WindowManager.DOCKED_INVALID。

int dockSide = WindowManagerProxy.getInstance().getDockSide();
        我们分析的是进入分屏的流程,那么获取到的值就是WindowManager.DOCKED_INVALID。这时就会调用到Recents.dockTopTask(...)方法。

2.我们已经获取到了分屏的状态,下面开始处理。

    @Override
    public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
            int metricsDockAction) {
        // Ensure the device has been provisioned before allowing the user to interact with
        // recents
        if (!isUserSetup()) {
            return false;
        }

        Point realSize = new Point();
        if (initialBounds == null) {
            mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
                    .getRealSize(realSize);
            initialBounds = new Rect(0, 0, realSize.x, realSize.y);
        }

        int currentUser = sSystemServicesProxy.getCurrentUser();
        SystemServicesProxy ssp = Recents.getSystemServices();
        ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
        boolean screenPinningActive = ssp.isScreenPinningActive();
        boolean isRunningTaskInHomeStack = runningTask != null &&
                SystemServicesProxy.isHomeStack(runningTask.stackId);
        if (runningTask != null && !isRunningTaskInHomeStack && !screenPinningActive) {
            logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
            if (runningTask.isDockable) {
                if (metricsDockAction != -1) {
                    MetricsLogger.action(mContext, metricsDockAction,
                            runningTask.topActivity.flattenToShortString());
                }
                if (sSystemServicesProxy.isSystemUser(currentUser)) {
                    mImpl.dockTopTask(runningTask.id, dragMode, stackCreateMode, initialBounds);
                } else {
                    if (mSystemToUserCallbacks != null) {
                        IRecentsNonSystemUserCallbacks callbacks =
                                mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                        if (callbacks != null) {
                            try {
                                callbacks.dockTopTask(runningTask.id, dragMode, stackCreateMode,
                                        initialBounds);
                            } catch (RemoteException e) {
                                Log.e(TAG, "Callback failed", e);
                            }
                        } else {
                            Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
                        }
                    }
                }
                mDraggingInRecentsCurrentUser = currentUser;
                return true;
            } else {
                EventBus.getDefault().send(new ShowUserToastEvent(
                        R.string.recents_incompatible_app_message, Toast.LENGTH_SHORT));
                return false;
            }
        } else {
            return false;
        }
    }

        代码位置:framework/base/packages/System/.../Recents.java

       传进来的四个参数值:

int dragMode:NavigationBarGestureHelper.DRAG_MODE_NONE
int stackCreateMode :ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
Rect initialBounds:null
int metricsDockAction:MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS

       由于initialBounds是null,会给他一个值,这个值就是屏幕的大小,例如当前屏幕分辨率是1920*1200,那么initialBounds就是(0,0,1920,1200),即全屏大小。

       获取当前正在运行的task信息,这个task就是在长按recents时正在运行的应用,也可以将是焦点所在的应用。

      我们需要保证能够正常拿到task,否则后面就无法处理task信息。

      判断当前task的stackid是否是homestack,这里需要说明在Android N上系统默认设置了五种Activitystack:   

stack名称stack_id
homestack0
fullscreenstack1
freeformstack2
dcokstack3
pinstack4

        homestack中记录了两个应用:launcher和SystemUI,也就解释了为什么我们在lacunher界面长按recents键是进不了分屏的。为什么加这个判断我会在后面解释。

       runningTask.isDockable是一个属性,只有这个属性是true时,应用才能进入分屏,否者就弹出一个toast提醒用户无法进入分屏,例如我们在相机应用中想进入分屏就不可以。在实际开发中有些应用不想让它进入到分屏中,可以修改isDockable属性,它是由TaskRecord.java中的canGoInDockedStack()方法控制的(在能进入分屏的应用mResizeMode是2,不能进入分屏的应用mResizeMode值为0)。

    后面判断是否是SystemUser,如果是,则执行RecentsImpl中的dockTopTask(...),否则就是RecentsImplProxy.dockTopTask(..)。不过是执行RecentsImplProxy.dockTopTask(..)也会调用到Recents中的方法。。


PS:Taskrecord是记录一个应用全部信息,它通过唯一的taskid来标识应用,如果想扩展或者修改应用的行为,可以从修改TaskRecord.java中的属性值入手








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值