天天看點

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的擷取