天天看点

Andorid N 最近任务管理器流程详解(一)前言1、最近任务管理器的触发

前言

相信大家都用过任务管理器,不同的手机上面启动的方式可能会有不同,有虚拟按键或者实体按键的,可能会通过短按或者长按Menu键来触发,如果这些都没有,只有一个指纹按键的,可能是通过长按的方式来触发。这个功能我们经常会用到,他可以让我们快速回到之前任务栈,好了,废话不多说了,直接来分析代码。

我们分为五个部分来讲解,分别是:

  • 最近任务管理器的触发
  • Task的获取
  • 缩略图的获取
  • Task的移除
  • 其他

1、最近任务管理器的触发

文件:PhoneStatusBar.java

private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            awakenDreams();
            // 见1.1
            toggleRecentApps();
        }
    };
           

1.1 toggleRecentApps

文件:BaseStatusBar.java

@Override
    public void toggleRecentApps() {
    // 见1.1.1
        toggleRecents();
    }
           

1.1.1 toggleRecents

文件:BaseStatusBar.java

protected void toggleRecents() {
        if (mRecents != null) {
            // 见1.2
            mRecents.toggleRecents(mDisplay);
        }
    }
           

1.2 toggleRecents

文件:Recents.java

@Override
    public void toggleRecents(Display display) {

        ......

        int currentUser = sSystemServicesProxy.getCurrentUser();
        if (sSystemServicesProxy.isSystemUser(currentUser)) {
            // 见1.2.1
            mImpl.toggleRecents(growTarget);
        } else {
            ......
        }
    }
           

这个方法的从名字也能看出来是要启动Recents activity,主要通过RecentsImpl的toggleRecents方法,我们继续往下看

1.2.1 mImpl.toggleRecents

文件:RecentsImpl.java

public void toggleRecents(int growTarget) {
        // Skip this toggle if we are already waiting to trigger recents via alt-tab
        if (mFastAltTabTrigger.isDozing()) {
            return;
        }

        mDraggingInRecents = false;
        mLaunchedWhileDocking = false;
        mTriggeredFromAltTab = false;

        try {
            SystemServicesProxy ssp = Recents.getSystemServices();
            MutableBoolean isHomeStackVisible = new MutableBoolean(true);
            long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;

            if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
                ......
            } else {
                // 间隔的时间要大于350
                if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
                    return;
                }
                // 获得正在运行的任务栈
                ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
                // 见1.2.2
                startRecentsActivity(runningTask, isHomeStackVisible.value, true /* animate */,
                        growTarget);

                // Only close the other system windows if we are actually showing recents
                ssp.sendCloseSystemWindows(BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
                mLastToggleTime = SystemClock.elapsedRealtime();
            }
        } catch (ActivityNotFoundException e) {
            Log.e(TAG, "Failed to launch RecentsActivity", e);
        }
    }
           

这里最重要的就是startRecentsActivity,也就是启动任务管理器的Acitvity

1.2.2 startRecentsActivity

文件:RecentsImpl.java

protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
            boolean isHomeStackVisible, boolean animate, int growTarget) {

        ......

        // animate为true
        if (!animate) {
            startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -, -));
            return;
        }

        ActivityOptions opts;
        if (isBlacklisted) {
            opts = getUnknownTransitionActivityOptions();
        } else if (useThumbnailTransition) {
            // Try starting with a thumbnail transition
            opts = getThumbnailTransitionActivityOptions(runningTask, mDummyStackView,
                    windowOverrideRect);
        } else {
            // 当栈的数量大于0时,hasRecentTasks为true
            opts = hasRecentTasks
                ? getHomeTransitionActivityOptions() // 是这个,一直过渡动画
                : getUnknownTransitionActivityOptions();
        }
        // 见1.2.3
        startRecentsActivity(opts);
        mLastToggleTime = SystemClock.elapsedRealtime();
    }
           

主要做启动RecentsActivity之前的一些准备工作

1.2.3 开始启动RecentsActivity

文件:RecentsImpl.java

private void startRecentsActivity(ActivityOptions opts) {
        Intent intent = new Intent();
        intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);

        if (opts != null) {
            // 启动 RecentsActivity
            mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
        } else {
            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
        }
        EventBus.getDefault().send(new RecentsActivityStartingEvent());
    }
           

1.3 RecentsActivity

我们先来看看onCreate

文件:

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mFinishedOnStartup = false;
        // 如果在Recents组件初始化完毕之前就启动了该Activity,那么就finish该Acitivity
        // 这种情况一般发生在debug时,push SysUI apk的时候
        SystemServicesProxy ssp = Recents.getSystemServices();
        if (ssp == null) {
            mFinishedOnStartup = true;
            finish();
            return;
        }

        // Register this activity with the event bus
        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);

        // 初始化包监听器
        // 包监听器用来监听从PackageManagerd到recent list里面内容的更新
        mPackageMonitor = new RecentsPackageMonitor();
        mPackageMonitor.register(this);

        // Set the Recents layout
        setContentView(R.layout.recents);
        takeKeyEvents(true); // 即使在Activity中没有取得焦点的View,也会处理此按键事件
        mRecentsView = (RecentsView) findViewById(R.id.recents_view);
        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        mScrimViews = new SystemBarScrimViews(this);
        getWindow().getAttributes().privateFlags |=
                WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;

        Configuration appConfiguration = Utilities.getAppConfiguration(this);
        mLastDeviceOrientation = appConfiguration.orientation;
        mLastDisplayDensity = appConfiguration.densityDpi;
        mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
        mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
            @Override
            public void run() {
                dismissRecentsToFocusedTask(MetricsEvent.OVERVIEW_SELECT_TIMEOUT);
            }
        });

        // Set the window background
        getWindow().setBackgroundDrawable(mRecentsView.getBackgroundScrim());

        // Create the home intent runnable
        mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

        // 注册广播监听器,坚挺灭屏和时间变化的广播
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_TIME_CHANGED);
        registerReceiver(mSystemBroadcastReceiver, filter);

        getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);

        // 见 重新加载stack view
        reloadStackView();
    }
           

1.3.1 重新加载stack view

文件:RecentsActivity.java

private void reloadStackView() {
        // If the Recents component has preloaded a load plan, then use that to prevent
        // reconstructing the task stack
        RecentsTaskLoader loader = Recents.getTaskLoader();
        RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
        if (loadPlan == null) {
            loadPlan = loader.createLoadPlan(this);
        }

        // Start loading tasks according to the load plan
        RecentsConfiguration config = Recents.getConfiguration();
        RecentsActivityLaunchState launchState = config.getLaunchState();
        if (!loadPlan.hasTasks()) {
            // 见, 加载任务
            loader.preloadTasks(loadPlan, launchState.launchedToTaskId,
                    !launchState.launchedFromHome);
        }

        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() == );
        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() > , null);

        // If this is a new instance relaunched by AM, without going through the normal mechanisms,
        // then we have to manually trigger the enter animation state
        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
                !launchState.launchedFromApp;
        if (wasLaunchedByAm) {
            EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
        }

        // Keep track of whether we launched from the nav bar button or via alt-tab
        if (launchState.launchedWithAltTab) {
            MetricsLogger.count(this, "overview_trigger_alttab", );
        } else {
            MetricsLogger.count(this, "overview_trigger_nav_btn", );
        }

        // Keep track of whether we launched from an app or from home
        if (launchState.launchedFromApp) {
            Task launchTarget = stack.getLaunchTarget();
            int launchTaskIndexInStack = launchTarget != null
                    ? stack.indexOfStackTask(launchTarget)
                    : ;
            MetricsLogger.count(this, "overview_source_app", );
            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
        } else {
            MetricsLogger.count(this, "overview_source_home", );
        }

        // Keep track of the total stack task count
        int taskCount = mRecentsView.getStack().getTaskCount();
        MetricsLogger.histogram(this, "overview_task_count", taskCount);

        // After we have resumed, set the visible state until the next onStop() call
        mIsVisible = true;
    }
           

如果loadPlan.hasTasks()等于false,那么通过preLoadTasks来加载Task,接下来我们进入第二部份—Task的获取