7.0分屏原理

  • 7.0的Activity新增了onMultiWindowModeChanged方法,当页面在分屏或不分屏状态变换时,会回调这个方法。

分屏的任务管理器和虚拟按键在com/Android/systemui目录下。长按多任务键时会调用PhoneStatusBar的toggleSplitScreenMode()方法。需要说明的是,多任务界面实际上是一个名叫RecentsActivity的Activity。

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


需要分屏时会调用Recents的dockTopTask方法,该方法内部会调用RecentsImpl的dockTopTask,该方法除了在多任务界面做出任务管理“样式”的调整以外,还会调用SystemServicesProxy.moveTaskToDockedStack,在分屏模式下打开最近任务


 public void dockTopTask(int topTaskId, int dragMode,
    int stackCreateMode, Rect initialBounds) {
        SystemServicesProxy ssp = Recents.getSystemServices();
        // Make sure we inform DividerView before we actually start the activity so we can    change
        // the resize mode already.
        if (ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds)) {//远程调用ActivityManagerService.moveTaskToDockedStack方法
         EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
         showRecents(
            false /* triggeredFromAltTab */,
            dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
            false /* animate */,
            true /* launchedWhileDockingTask*/,
            false /* fromHome */,
            DividerView.INVALID_RECENTS_GROW_TARGET);
}}

而该进程下的RecentsActivity则负责远程与PackageManager通信,拿到应用截图,添加到界面上


private void reloadStackView() {
    //省略部分代码

    RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
    loadOpts.runningTaskId = launchState.launchedToTaskId;
    loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
    loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
    loader.loadTasks(this, loadPlan, loadOpts);
    TaskStack stack = loadPlan.getTaskStack();
    mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0);
    mRecentsView.updateStack(stack, true /* setStackViewTasks */);
    // Update the nav bar scrim, but defer the animation until the enter-window event
    boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
    mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null);
    //省略部分代码
}

那么7.0的Activity新增的方法onMultiWindowModeChanged是在什么时候调用的呢?回到刚才的SystemServicesProxy.moveTaskToDockedStack,该方法会跨进程调用ActivityManagerService.moveTaskToDockedStack


public boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
    Rect initialBounds, boolean moveHomeStackFront) {
    enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()");
    synchronized (this) {
    long ident = Binder.clearCallingIdentity();
    try {
        if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
                + " to createMode=" + createMode + " toTop=" + toTop);
        mWindowManager.setDockedStackCreateState(createMode, initialBounds);
        final boolean moved = mStackSupervisor.moveTaskToStackLocked(
                taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS, "moveTaskToDockedStack",
                animate, DEFER_RESUME);
        if (moved) {
            if (moveHomeStackFront) {
                mStackSupervisor.moveHomeStackToFront("moveTaskToDockedStack");
            }
            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
        }
        return moved;
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}
}


该方法会调用StackSupervisor.moveTaskToStackLocked,在StackSuperviso中做尺寸调整等工作以外,最终调用TaskRecord.updateOverrideConfiguration()。若需要分屏,则会调用scheduleReportMultiWindowModeChanged方法


Configuration updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
//省略部分代码
    if (mFullscreen != oldFullscreen) {
        mService.mStackSupervisor.scheduleReportMultiWindowModeChanged(this);
    }
    return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
}

scheduleReportMultiWindowModeChanged最终调用ActivityThreadhandleMultiWindowModeChanged,并通过appToken找到需要回调的Activity

private void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode) {
    final ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        r.activity.dispatchMultiWindowModeChanged(isInMultiWindowMode);
    }
}


Activity的dispatchMultiWindowModeChanged会回调onMultiWindowModeChanged,便可进行业务处理

分屏时Activity的生命周期

  • 分三种情况

  • 当前显示自己的应用页面,长按多任务键时出现分屏

    onMultiWindowModeChanged(true)->onPause-onStop->onDestroy->onCreate->onStart- >onResume->onPause

  • 分屏时长按多任务键,全屏显示自己的应用时

    onStop->onDestroy->onCreate->onStart->onResume>onPause>onMultiWindowModeChanged(false)->onResume

  • 当前显示其他应用,按多任务键出现自己的应用时

    onMultiWindowModeChanged(true)->nDestroy->onCreate->onStart->onResume

参考资料 
示例 | Android Developers

转自:http://blog.csdn.net/a279822581/article/details/52445154