前言
相信大家都用過任務管理器,不同的手機上面啟動的方式可能會有不同,有虛拟按鍵或者實體按鍵的,可能會通過短按或者長按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的擷取