android 最近任務

RecentsActivity 是最近任務管理界面,通過該activity用戶可以對啓動的應用進行簡單的管理。其配置信息如下


frameworks/base/packages/SystemUI/AndroidManifest.xml
 
        <activity android:name=".recents.RecentsActivity"
                  android:label="@string/accessibility_desc_recent_apps"
                  android:exported="false"
                  android:launchMode="singleInstance"
                  android:excludeFromRecents="true"
                  android:stateNotNeeded="true"
                  android:resumeWhilePausing="true"
                  android:screenOrientation="behind"
                  android:resizeableActivity="true"
                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
                  android:theme="@style/RecentsTheme.Wallpaper">
            <intent-filter>
                <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
            </intent-filter>
        </activity>
可以看到其launchMode爲"singleInstance"。RecentsActivity 啓動的入口在
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
 
    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
        public void onClick(View v) {
            awakenDreams();
            toggleRecentApps();
        }
    };
關於RecentsActivity ,這裏主要討論3個方面,task的獲取、應用縮略圖的獲取、task移除。
getRecentTasks
在ams中,有一個mRecentTasks對象,該對象保存了近期啓動的task任務信息,RecentsActivity實際上是要獲取mRecentTasks對象


frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 
 
  /**
     * List of intents that were used to start the most recent tasks.
     */
    final RecentTasks mRecentTasks;


在上面的入口中,toggleRecentApps()最終會調用到


frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
 
    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
            boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) {
        ...
        int minNumTasksToQuery = 10;
        int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
        int flags = ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
                ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK |
                ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
                ActivityManager.RECENT_IGNORE_UNAVAILABLE |
                ActivityManager.RECENT_INCLUDE_PROFILES;
        if (includeFrontMostExcludedTask) {
            flags |= ActivityManager.RECENT_WITH_EXCLUDED;
        }
        List<ActivityManager.RecentTaskInfo> tasks = null;
        try {
            tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
        } catch (Exception e) {
            Log.e(TAG, "Failed to get recent tasks", e);
        }
 
        ...
        return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
    }
這裏設置了flag,不去獲取HOME_STACK_ID、DOCKED_STACK_ID、PINNED_STACK_ID上的task,根據機器內存大小設置了最大的查詢數

frameworks/base/core/java/android/app/ActivityManager.java 
static public int getMaxRecentTasksStatic() {
        if (gMaxRecentTasks < 0) {
            return gMaxRecentTasks = isLowRamDeviceStatic() ? 36 : 48;
        }
        return gMaxRecentTasks;
    }
然後調用ActivityManager的getRecentTasksForUser方法

frameworks/base/core/java/android/app/ActivityManager.java 
public List<RecentTaskInfo> getRecentTasksForUser(int maxNum, int flags, int userId)
            throws SecurityException {
        try {
            return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
                    flags, userId).getList();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
通過binder通信最終會進入到ams

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 
   public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
            int userId) {
        final int callingUid = Binder.getCallingUid();
        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
                false, ALLOW_FULL_ONLY, "getRecentTasks", null);
 
        final boolean includeProfiles = (flags & ActivityManager.RECENT_INCLUDE_PROFILES) != 0;
        final boolean withExcluded = (flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0;
        synchronized (this) {
            final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
                    callingUid);
            final boolean detailed = checkCallingPermission(
                    android.Manifest.permission.GET_DETAILED_TASKS)
                    == PackageManager.PERMISSION_GRANTED;
 
            if (!isUserRunning(userId, ActivityManager.FLAG_AND_UNLOCKED)) {
                Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
                return ParceledListSlice.emptyList();
            }
            mRecentTasks.loadUserRecentsLocked(userId);
 
            final int recentsCount = mRecentTasks.size();
            ArrayList<ActivityManager.RecentTaskInfo> res =
                    new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount);
 
            final Set<Integer> includedUsers;
            if (includeProfiles) {
                includedUsers = mUserController.getProfileIds(userId);
            } else {
                includedUsers = new HashSet<>();
            }
            includedUsers.add(Integer.valueOf(userId));
 
            for (int i = 0; i < recentsCount && maxNum > 0; i++) {
                TaskRecord tr = mRecentTasks.get(i);
                // Only add calling user or related users recent tasks
                if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
                    continue;
                }
 
                if (tr.realActivitySuspended) {
                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
                    continue;
                }
 
                // Return the entry if desired by the caller.  We always return
                // the first entry, because callers always expect this to be the
                // foreground app.  We may filter others if the caller has
                // not supplied RECENT_WITH_EXCLUDED and there is some reason
                // we should exclude the entry.
 
                if (i == 0
                        || withExcluded
                        || (tr.intent == null)
                        || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
                                == 0)) {
                    if (!allowed) {
                        // If the caller doesn't have the GET_TASKS permission, then only
                        // allow them to see a small subset of tasks -- their own and home.
                        if (!tr.isHomeTask() && tr.effectiveUid != callingUid) {
                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
                            continue;
                        }
                    }
                    if ((flags & ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS) != 0) {
                        if (tr.stack != null && tr.stack.isHomeStack()) {
                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                    "Skipping, home stack task: " + tr);
                            continue;
                        }
                    }
                    if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK) != 0) {
                        final ActivityStack stack = tr.stack;
                        if (stack != null && stack.isDockedStack() && stack.topTask() == tr) {
                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                    "Skipping, top task in docked stack: " + tr);
                            continue;
                        }
                    }
                    if ((flags & ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS) != 0) {
                        if (tr.stack != null && tr.stack.isPinnedStack()) {
                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                    "Skipping, pinned stack task: " + tr);
                            continue;
                        }
                    }
                    if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
                        // Don't include auto remove tasks that are finished or finishing.
                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                "Skipping, auto-remove without activity: " + tr);
                        continue;
                    }
                    if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0
                            && !tr.isAvailable) {
                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                "Skipping, unavail real act: " + tr);
                        continue;
                    }
 
                    if (!tr.mUserSetupComplete) {
                        // Don't include task launched while user is not done setting-up.
                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                "Skipping, user setup not complete: " + tr);
                        continue;
                    }
 
                    ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
                    if (!detailed) {
                        rti.baseIntent.replaceExtras((Bundle)null);
                    }
 
                    res.add(rti);
                    maxNum--;
                }
            }
            return new ParceledListSlice<>(res);
        }
    }
首先loadUserRecentsLocked

frameworks/base/services/core/java/com/android/server/am/RecentTasks.java
 
    void loadUserRecentsLocked(int userId) {
        if (!mUsersWithRecentsLoaded.get(userId)) {
            // Load the task ids if not loaded.
            loadPersistedTaskIdsForUserLocked(userId);
            Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
            addAll(mTaskPersister.restoreTasksForUserLocked(userId));
            cleanupLocked(userId);
            mUsersWithRecentsLoaded.put(userId, true);
        }
    }

通過restoreTasksForUserLocked方法去獲取task列表

frameworks/base/services/core/java/com/android/server/am/TaskPersister.java
    List<TaskRecord> restoreTasksForUserLocked(final int userId) {
        final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
        ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
 
        File userTasksDir = getUserTasksDir(userId);
 
        File[] recentFiles = userTasksDir.listFiles();
        if (recentFiles == null) {
            Slog.e(TAG, "restoreTasksForUserLocked: Unable to list files from " + userTasksDir);
            return tasks;
        }
 
        for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
            File taskFile = recentFiles[taskNdx];
            if (DEBUG) {
                Slog.d(TAG, "restoreTasksForUserLocked: userId=" + userId
                        + ", taskFile=" + taskFile.getName());
            }
            BufferedReader reader = null;
            boolean deleteFile = false;
            try {
                reader = new BufferedReader(new FileReader(taskFile));
                final XmlPullParser in = Xml.newPullParser();
                in.setInput(reader);
 
                int event;
                while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
                        event != XmlPullParser.END_TAG) {
                    final String name = in.getName();
                    if (event == XmlPullParser.START_TAG) {
                        if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: START_TAG name=" + name);
                        if (TAG_TASK.equals(name)) {
                            final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor);
                            if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: restored task="
                                    + task);
                            if (task != null) {
                                // XXX Don't add to write queue... there is no reason to write
                                // out the stuff we just read, if we don't write it we will
                                // read the same thing again.
                                // mWriteQueue.add(new TaskWriteQueueItem(task));
 
                                final int taskId = task.taskId;
                                if (mStackSupervisor.anyTaskForIdLocked(taskId,
                                        /* restoreFromRecents= */ false, 0) != null) {
                                    // Should not happen.
                                    Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
                                } else if (userId != task.userId) {
                                    // Should not happen.
                                    Slog.wtf(TAG, "Task with userId " + task.userId + " found in "
                                            + userTasksDir.getAbsolutePath());
                                } else {
                                    // Looks fine.
                                    mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId);
                                    task.isPersistable = true;
                                    tasks.add(task);
                                    recoveredTaskIds.add(taskId);
                                }
                            } else {
                                Slog.e(TAG, "restoreTasksForUserLocked: Unable to restore taskFile="
                                        + taskFile + ": " + fileToString(taskFile));
                            }
                        } else {
                            Slog.wtf(TAG, "restoreTasksForUserLocked: Unknown xml event=" + event
                                    + " name=" + name);
                        }
                    }
                    XmlUtils.skipCurrentTag(in);
                }
            } catch (Exception e) {
                Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error ", e);
                Slog.e(TAG, "Failing file: " + fileToString(taskFile));
                deleteFile = true;
            } finally {
                IoUtils.closeQuietly(reader);
                if (deleteFile) {
                    if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
                    taskFile.delete();
                }
            }
        }
 
        ...
        return tasks;
    }

主要是去解析xml文件,位於/data/system_ce/0/recent_tasks目錄下


可以看到這些task信息是以文件形式保存的,這就是重啓後進RecentsActivity看到task還在的原因。

解析完這些xml文件就獲取到了初始的task信息。

但實際上loadUserRecentsLocked裏面的語句是隻會在開機初始化時執行一次,因此這裏相當於什麼都沒有做。

然後還要回到


frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 
   public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
            int userId) {
        ...
        synchronized (this) {
            ...
            mRecentTasks.loadUserRecentsLocked(userId);
 
            ...
 
            for (int i = 0; i < recentsCount && maxNum > 0; i++) {
                TaskRecord tr = mRecentTasks.get(i);
                // Only add calling user or related users recent tasks
                if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
                    continue;
                }
 
                if (tr.realActivitySuspended) {
                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
                    continue;
                }
 
                // Return the entry if desired by the caller.  We always return
                // the first entry, because callers always expect this to be the
                // foreground app.  We may filter others if the caller has
                // not supplied RECENT_WITH_EXCLUDED and there is some reason
                // we should exclude the entry.
 
                if (i == 0
                        || withExcluded
                        || (tr.intent == null)
                        || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
                                == 0)) {
                    if (!allowed) {
                        // If the caller doesn't have the GET_TASKS permission, then only
                        // allow them to see a small subset of tasks -- their own and home.
                        if (!tr.isHomeTask() && tr.effectiveUid != callingUid) {
                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
                            continue;
                        }
                    }
                    if ((flags & ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS) != 0) {
                        if (tr.stack != null && tr.stack.isHomeStack()) {
                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                    "Skipping, home stack task: " + tr);
                            continue;
                        }
                    }
                    if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK) != 0) {
                        final ActivityStack stack = tr.stack;
                        if (stack != null && stack.isDockedStack() && stack.topTask() == tr) {
                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                    "Skipping, top task in docked stack: " + tr);
                            continue;
                        }
                    }
                    if ((flags & ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS) != 0) {
                        if (tr.stack != null && tr.stack.isPinnedStack()) {
                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                    "Skipping, pinned stack task: " + tr);
                            continue;
                        }
                    }
                    if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
                        // Don't include auto remove tasks that are finished or finishing.
                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                "Skipping, auto-remove without activity: " + tr);
                        continue;
                    }
                    if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0
                            && !tr.isAvailable) {
                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                "Skipping, unavail real act: " + tr);
                        continue;
                    }
 
                    if (!tr.mUserSetupComplete) {
                        // Don't include task launched while user is not done setting-up.
                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                                "Skipping, user setup not complete: " + tr);
                        continue;
                    }
 
                    ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
                    if (!detailed) {
                        rti.baseIntent.replaceExtras((Bundle)null);
                    }
 
                    res.add(rti);
                    maxNum--;
                }
            }
            return new ParceledListSlice<>(res);
        }
    }
根據之前設置的flag去排除掉不需要的task,最後調用createRecentTaskInfoFromTaskRecord()方法把TaskRecord信息轉換成RecentTaskInfo的信息

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 
     private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) {
        // Update the task description to reflect any changes in the task stack
        tr.updateTaskDescription();
 
        // Compose the recent task info
        ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
        rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
        rti.persistentId = tr.taskId;
        rti.baseIntent = new Intent(tr.getBaseIntent());
        rti.origActivity = tr.origActivity;
        rti.realActivity = tr.realActivity;
        rti.description = tr.lastDescription;
        rti.stackId = tr.stack != null ? tr.stack.mStackId : -1;
        rti.userId = tr.userId;
        rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
        rti.firstActiveTime = tr.firstActiveTime;
        rti.lastActiveTime = tr.lastActiveTime;
        rti.affiliatedTaskId = tr.mAffiliatedTaskId;
        rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
        rti.numActivities = 0;
        if (tr.mBounds != null) {
            rti.bounds = new Rect(tr.mBounds);
        }
        rti.isDockable = tr.canGoInDockedStack();
        rti.resizeMode = tr.mResizeMode;
 
        ActivityRecord base = null;
        ActivityRecord top = null;
        ActivityRecord tmp;
 
        for (int i = tr.mActivities.size() - 1; i >= 0; --i) {
            tmp = tr.mActivities.get(i);
            if (tmp.finishing) {
                continue;
            }
            base = tmp;
            if (top == null || (top.state == ActivityState.INITIALIZING)) {
                top = base;
            }
            rti.numActivities++;
        }
 
        rti.baseActivity = (base != null) ? base.intent.getComponent() : null;
        rti.topActivity = (top != null) ? top.intent.getComponent() : null;
 
        return rti;
    } 
從這裏可以看到recenttask是以TaskReord爲單位進行管理的,不是一app或者activity爲單位進行管理。
到這裏就獲取了所需的RecentTaskInfo信息。

從這裏可以看到mRecentTasks只是剔除了一些不符合要求的task做了些簡單的處理就返回了,那麼mRecentTasks真正是在哪裏賦值的呢?

有兩個地方,一是在startActivity的過程中,二是在activity 重新resume時。

先看startActivity時,這裏省略了startActivity的具體過程


frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {
            
        ...
 
        if (andResume) {
            // As part of the process of launching, ActivityThread also performs
            // a resume.
            stack.minimalResumeActivityLocked(r);
        }
        ...
 
        return true;
    }
在minimalResumeActivityLocked方法中將TaskRecord對象加入到mRecentTasks對象中

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
    void minimalResumeActivityLocked(ActivityRecord r) {
       ...
        mRecentTasks.addLocked(r.task);
       ...
    }
這裏有個疑問,ActivityStack中的mRecentTasks是怎麼和ActivityManagerService的mRecentTasks聯繫起來的呢?
在ams初始化時創建了ActivityStackSupervisor對象,用來輔助ams管理ActivityStack


frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 
   public ActivityManagerService(Context systemContext) {
      ...
        mStackSupervisor = new ActivityStackSupervisor(this);
        mActivityStarter = new ActivityStarter(this, mStackSupervisor);
        mRecentTasks = new RecentTasks(this, mStackSupervisor);
       ...
    }
同時也創建了mRecentTasks對象,參數是ams和mStackSupervisor。
RecentTasks的構造函數中又把自己通過setRecentTasks的方法保存到了ActivityStackSupervisor


frameworks/base/services/core/java/com/android/server/am/RecentTasks.java
  RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) {
        ...
        mStackSupervisor.setRecentTasks(this);
    }
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
 
...
private RecentTasks mRecentTasks;
...
 void setRecentTasks(RecentTasks recentTasks) {
        mRecentTasks = recentTasks;
    }
ActivityStackSupervisor中也保存了一個RecentTasks對象,這樣實際上和ActivityManagerService的mRecentTasks指向了同一個地址。
在ActivityStackSupervisor中創建ActivityStack時,又把mRecentTasks傳遞到了ActivityStack中,


frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
 
ActivityContainer(int stackId) {
            synchronized (mService) {
                ...
                mStack = new ActivityStack(this, mRecentTasks);
                ...
            }
        }
這樣ActivityStack中的mRecentTasks就和ActivityManagerService的mRecentTasks保持一致了。
現在回到mRecentTasks的賦值問題,第二個時機是在activity 重新resume時。

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    ...
    mRecentTasks.addLocked(next.task);
    ...
}
getTaskThumbnail
獲取到RecentTaskInfo之後就要去獲取縮略圖信息了

frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
public void getThumbnail(int taskId, ThumbnailData thumbnailDataOut) {
        ...
        ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
        ...
    }
很簡潔,就調用了ActivityManager的方法

frameworks/base/core/java/android/app/ActivityManager.java 
 public TaskThumbnail getTaskThumbnail(int id) throws SecurityException {
        try {
            return ActivityManagerNative.getDefault().getTaskThumbnail(id);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
通過binder通信調用了ams的方法

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 
 public ActivityManager.TaskThumbnail getTaskThumbnail(int id) {
        synchronized (this) {
            enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
                    "getTaskThumbnail()");
            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
                    id, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
            if (tr != null) {
                return tr.getTaskThumbnailLocked();
            }
        }
        return null;
    }
首先根據taskid去找到TaskRecord,然後獲取Thumbnail

public TaskThumbnail getTaskThumbnailLocked() {
        if (stack != null) {
            final ActivityRecord resumedActivity = stack.mResumedActivity;
            if (resumedActivity != null && resumedActivity.task == this) {
                final Bitmap thumbnail = stack.screenshotActivitiesLocked(resumedActivity);
                setLastThumbnailLocked(thumbnail);
            }
        }
        final TaskThumbnail taskThumbnail = new TaskThumbnail();
        getLastThumbnail(taskThumbnail);
        return taskThumbnail;
    }
這裏除了棧頂正在顯示的TaskRecord回去實時的截取屏幕圖像,其他的走getLastThumbnail

frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
 void getLastThumbnail(TaskThumbnail thumbs) {
        thumbs.mainThumbnail = mLastThumbnail;
        thumbs.thumbnailInfo = mLastThumbnailInfo;
        thumbs.thumbnailFileDescriptor = null;
        if (mLastThumbnail == null) {
            thumbs.mainThumbnail = mService.mRecentTasks.getImageFromWriteQueue(
                    mLastThumbnailFile.getAbsolutePath());
        }
        // Only load the thumbnail file if we don't have a thumbnail
        if (thumbs.mainThumbnail == null && mLastThumbnailFile.exists()) {
            try {
                thumbs.thumbnailFileDescriptor = ParcelFileDescriptor.open(mLastThumbnailFile,
                        ParcelFileDescriptor.MODE_READ_ONLY);
            } catch (IOException e) {
            }
        }
    }
這裏實際是去讀取文件,這些縮略圖已經保存在/data/system_ce/0/recent_images文件夾下


把這些文件讀取出來就獲取到了task的縮略圖。

但是這裏並沒有看到去截屏,這些圖片是什麼時候截取的呢?

截圖的時機是在activity onpause之後。


frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
private void completePauseLocked(boolean resumeNext) {
        ...
        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    }
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
            boolean preserveWindows) {
        // First the front stacks. In case any are not fullscreen and are in front of home.
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            final int topStackNdx = stacks.size() - 1;
            for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows);
            }
        }
    }
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
            boolean preserveWindows) {
        ...
        makeInvisible(r, visibleBehind);
           
        ...
    }
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
    private void makeInvisible(ActivityRecord r, ActivityRecord visibleBehind) {
       ...
        try {
            setVisible(r, false);
            ...
        } catch (Exception e) {
            ...
        }
    }

makeInvisible又調用了setVisible

private void setVisible(ActivityRecord r, boolean visible) {
        r.visible = visible;
        if (!visible && r.mUpdateTaskThumbnailWhenHidden) {
            r.updateThumbnailLocked(r.task.stack.screenshotActivitiesLocked(r), null);
            r.mUpdateTaskThumbnailWhenHidden = false;
        }
        ...
    }
就是在這裏更新TaskThumbnail的,screenshotActivitiesLocked(r)是真正截圖的地方
截圖完成之後,最終通過saveImage方法保存到文件中,這裏path爲/data/system_ce/0/recent_images文件夾


frameworks/base/services/core/java/com/android/server/am/RecentTasks.java
void saveImage(Bitmap image, String path) {
        mTaskPersister.saveImage(image, path);
    }
這樣需要獲取縮略圖時之前去讀文件就可以了。

removeTask
當從RecentsActivity移除某一個應用時,實際會調用到removeTask

frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
    public void removeTask(final int taskId) {
        ...
        // Remove the task.
        BackgroundThread.getHandler().post(new Runnable() {
            @Override
            public void run() {
                mAm.removeTask(taskId);
            }
        });
    }
然後調用到ams的removeTask方法,參數taskId爲要移除的TaskRecord的id

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
  public boolean removeTask(int taskId) {
        enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, "removeTask()");
        synchronized (this) {
            final long ident = Binder.clearCallingIdentity();
            try {
                return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }
調用該方法首先會檢查是否有聲明android.Manifest.permission.REMOVE_TASKS的權限,這裏我們是有聲明這個權限的。然後調用removeTaskByIdLocked

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
  private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
            boolean removeFromRecents) {
        final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
                taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
        if (tr != null) {
            tr.removeTaskActivitiesLocked();
            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
            if (tr.isPersistable) {
                notifyTaskPersisterLocked(null, true);
            }
            return true;
        }
        Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
        return false;
    }
先看下這裏的參數,taskid爲要移除的task id,killProcess爲true,表示要殺掉該taskrecord裏所有activity所在的進程,removeFromRecents爲true,表示要從mRecentTasks移除。
這裏通過taskid找到該TaskRecord,然後首先調用了TaskRecord的removeTaskActivitiesLocked方法


frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
public void removeTaskActivitiesLocked() {
        // Just remove the entire task.
        performClearTaskAtIndexLocked(0);
    }
frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
    final void performClearTaskAtIndexLocked(int activityNdx) {
        int numActivities = mActivities.size();
        for ( ; activityNdx < numActivities; ++activityNdx) {
            final ActivityRecord r = mActivities.get(activityNdx);
            if (r.finishing) {
                continue;
            }
            if (stack == null) {
                // Task was restored from persistent storage.
                r.takeFromHistory();
                mActivities.remove(activityNdx);
                --activityNdx;
                --numActivities;
            } else if (stack.finishActivityLocked(
                    r, Activity.RESULT_CANCELED, null, "clear-task-index", false)) {
                --activityNdx;
                --numActivities;
            }
        }
    }
傳遞下來的參數activityNdx = 0,表示吧整個TaskRecord移除。這裏的mActivities是該TaskRecord中保存的所有ActivityRecord對象,stack是該TaskRecord所在的ActivityStack,這裏不爲空,所以會調用ActivityStack的finishActivityLocked方法去移除該TaskRecord中的ActivityRecord對象。
從ActivityStack移除完TaskRecord之後回到removeTaskByIdLocked,然後又調用了cleanUpRemovedTaskLocked方法


frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
            boolean removeFromRecents) {
        if (removeFromRecents) {
            mRecentTasks.remove(tr);
            tr.removedFromRecents();
        }
        ComponentName component = tr.getBaseIntent().getComponent();
        if (component == null) {
            Slog.w(TAG, "No component for base intent of task: " + tr);
            return;
        }
 
        // Find any running services associated with this app and stop if needed.
        mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
 
        if (!killProcess) {
            return;
        }
 
        // Determine if the process(es) for this task should be killed.
        final String pkg = component.getPackageName();
        ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
        ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
        for (int i = 0; i < pmap.size(); i++) {
 
            SparseArray<ProcessRecord> uids = pmap.valueAt(i);
            for (int j = 0; j < uids.size(); j++) {
                ProcessRecord proc = uids.valueAt(j);
                if (proc.userId != tr.userId) {
                    // Don't kill process for a different user.
                    continue;
                }
                if (proc == mHomeProcess) {
                    // Don't kill the home process along with tasks from the same package.
                    continue;
                }
                if (!proc.pkgList.containsKey(pkg)) {
                    // Don't kill process that is not associated with this task.
                    continue;
                }
 
                for (int k = 0; k < proc.activities.size(); k++) {
                    TaskRecord otherTask = proc.activities.get(k).task;
                    if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
                        // Don't kill process(es) that has an activity in a different task that is
                        // also in recents.
                        return;
                    }
                }
 
                if (proc.foregroundServices) {
                    // Don't kill process(es) with foreground service.
                    return;
                }
 
                // Add process to kill list.
                procsToKill.add(proc);
            }
        }
 
        // Kill the running processes.
        for (int i = 0; i < procsToKill.size(); i++) {
            ProcessRecord pr = procsToKill.get(i);
            if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                    && pr.curReceiver == null) {
                pr.kill("remove task", true);
            } else {
                // We delay killing processes that are not in the background or running a receiver.
                pr.waitingToKill = "remove task";
            }
        }
    }
這裏removeFromRecents爲true,因此會先將該TaskRecord從mRecentTasks中移除,然後過濾出可以殺掉的Process的ProcessRecord信息,放到procsToKill列表中。分兩種情況,如果是SCHED_GROUP_BACKGROUND類型並且沒有在執行reciver,會調用kill方法立即去殺掉。否則簡單將procsToKill中ProcessRecord的waitingToKill字段設置爲remove task。這裏涉及到Android進程管理的相關知識,可參看
Android系統中的進程管理:進程的優先級
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章