Android TVSystemUI

Android 近期任务列表Recents功能

2020-08-29  本文已影响0人  雷涛赛文

       前段时间在做多任务管理的功能,即Android系统Recents功能,该功能的上层实现是在SystemUI里面,通过阅读SystemUI及framework相关的源码,当前系统版本是Android 8.1,现将学习记录如下:

一.功能概览

image.png

      Recents功能是在SystemUI应用里面实现的,SystemUI作为Android系统核心应用,与Framework有密切关系,当然Recents功能也是完全依赖Framework的;

二.重要功能类

1.SystemServicesProxy.java

       与系统服务级的交互代理类,SystemUI应用依靠该类与AMS进行交互;

1.1.获取最近任务列表

public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
            boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) {
    .........
    .........
    try {
         // 最终会调用到AMS
         tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
    } catch (Exception e) {
         Log.e(TAG, "Failed to get recent tasks", e);
    }
    Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
    while (iter.hasNext()) {
          ActivityManager.RecentTaskInfo t = iter.next();
          // Remove the task if it or it's package are blacklsited
          // 可以将不想显示的应用加入黑名单
          if (sRecentsBlacklist.contains(t.realActivity.getClassName()) ||
                    sRecentsBlacklist.contains(t.realActivity.getPackageName())) {
              iter.remove();
              continue;
          }

          // Remove the task if it is marked as excluded, unless it is the first most task and we
          // are requested to include it 
          //删除在AndroidManifest.xml包含android:excludeFromRecents="true"的任务
            boolean isExcluded = (t.baseIntent.getFlags() Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
                    == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
            isExcluded |= quietProfileIds.contains(t.userId);
            if (isExcluded && (!isFirstValidTask || !includeFrontMostExcludedTask)) {
                iter.remove();
            }
     }
    return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
}

1.2.删除最近task

public void removeTask(final int taskId) {
    // Remove the task.
    mUiOffloadThread.submit(() -> {
        mAm.removeTask(taskId);
    });
}

1.3.监听task stack变化,更新最近任务列表

private android.app.TaskStackListener mTaskStackListener = new android.app.TaskStackListener() {
    @Override
     public void onTaskStackChanged() throws RemoteException {
     }
    ......
    ......
    @Override
    public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
                throws RemoteException {
    }
}

2.Recents.java

       继承SystemUI抽象类,recents task功能的入口,初始化必要的实例,提供触发接口;

@Override
public void start() {
    //初始化实例
    sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
    sTaskLoader = new RecentsTaskLoader(mContext);
    mImpl = new RecentsImpl(mContext);
}

//rencets入口
@Override
public void toggleRecentApps() {
    ......
    ......
    mImpl.toggleRecents(growTarget);
}

3.RecentsImpl.java

       Recents主要功能逻辑执行类,包括监听task变化及触发Recents界面;

3.1.监听TaskStack变化来加载任务列表

class TaskStackListenerImpl extends TaskStackListener {
    @Override
    public void onTaskStackChangedBackground() {  
        // Load the next task only if we aren't svelte
        SystemServicesProxy ssp = Recents.getSystemServices();
        ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
        RecentsTaskLoader loader = Recents.getTaskLoader();
        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
        loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
        TaskStack stack = plan.getTaskStack();
        RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
        ......
        ......
        loader.loadTasks(mContext, plan, launchOpts);
}

3.2.启动Recents Activity

public void toggleRecents(int growTarget) {
    ......
    startRecentsActivity(runningTask, isHomeStackVisible.value, true /* animate */,growTarget);
    ......
}

4.RecentsTaskLoader.java

       Recents最近任务加载入口类

4.1.创建RecentsTaskLoadPlan

public RecentsTaskLoadPlan createLoadPlan(Context context) {
    RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context);
    return plan;
}

4.2.启动plan加载任务列表

public synchronized void loadTasks(Context context, RecentsTaskLoadPlan plan,
            RecentsTaskLoadPlan.Options opts) {
    ......
    plan.executePlan(opts, this);
    ......
}

5.RecentsTaskLoadPlan.java

       Recents最近任务加载执行类

void preloadRawTasks(boolean includeFrontMostExcludedTask) {
    //调用SystemServicesProxy来获取最近任务列表
    SystemServicesProxy ssp = Recents.getSystemServices();
    mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
                currentUserId, includeFrontMostExcludedTask, mCurrentQuietProfiles);
    // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
    Collections.reverse(mRawTasks);
}

       通过SystemServicesPeroxy来获取最近任务信息列表mRawTasks,存储的是ActivityManager.RecentTaskInfo,然后进行反序排列;
       preloadPlan()用来预加载最近任务列表;

void preloadPlan(RecentsTaskLoader loader, int runningTaskId,
            boolean includeFrontMostExcludedTask) {
    ......
    ......
    int taskCount = mRawTasks.size();
    for (int i = 0; i < taskCount; i++) {
         ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
         // Compose the task key
         Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
                    t.userId, t.firstActiveTime, t.lastActiveTime);
          ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
          String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
          String titleDescription = loader.getAndUpdateContentDescription(taskKey,
                    t.taskDescription, res);
          .......
          Drawable icon = isStackTask
                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
                    : null;
          ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
                    false /* loadIfNotCached */, false /* storeInCache */);
          ......
          Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
                        thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
                        activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
                        t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode,
                        t.topActivity,
                        isLocked);
         allTasks.add(task);
         mStack = new TaskStack();
         mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */);
}

       该方法主要执行了以下主要逻辑:
       1.遍历mRawTasks,将ActivityManager.RecentTaskInfo封装成Task;
       2.将Task加入allTasks中进行管理;
       3.创建TaskStack实例,进行选择处理从而得到最终任务列表,后续获取最近任务也是通过TaskStack.getStackTasks()来获取;
       executePlan()用来真正加载任务对应的icon和thumbnail;

void executePlan(Options opts, RecentsTaskLoader loader) {
    Resources res = mContext.getResources();
    // Iterate through each of the tasks and load them according to the load conditions.
    ArrayList<Task> tasks = mStack.getStackTasks();
    int taskCount = tasks.size();
    for (int i = 0; i < taskCount; i++) {
        Task task = tasks.get(i);
        Task.TaskKey taskKey = task.key;

        boolean isRunningTask = (task.key.id == opts.runningTaskId);
        boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
        boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);

        // If requested, skip the running task
        if (opts.onlyLoadPausedActivities && isRunningTask) {
            continue;
        }

        if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
            if (task.icon == null) {
                task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,true);
            }
        }
        if (opts.loadThumbnails && isVisibleThumbnail) {
            task.thumbnail = loader.getAndUpdateThumbnail(taskKey,true /* loadIfNotCached */, true /* storeInCache */);
        }
    }
}

       该方法里面相当于执行了懒加载,根据参数判断来是否真正加载到内存,主要为了降低资源消耗;

三.启动

1.启动流程

       在PhoneWindowManager里面监听按键处理,通过调用最终会调用到Recents里面的toggleRecentApps()方法,从而触发Recents最近任务列表界面,本文只画出调用流程图,代码就不贴了,详情可根据流程图参考源码来进一步了解。
       流程如下:

image.png
      从上面流程图可以看到,PhoneWindowManager是依靠StatusBarManagerService来进行分发处理的。

2.注册流程

       注册过程重点看一下SystemUIApplication这个类:

public class SystemUIApplication extends Application implements SysUiServiceProvider {
    ......
    private final Class<?>[] SERVICES = new Class[] {
            .....
            CommandQueue.CommandQueueStart.class,
            ......
            Recents.class,
            .....
            SystemBars.class,
            ......
    };

    private final Map<Class<?>, Object> mComponents = new HashMap<>();

    private void startServicesIfNeeded(Class<?>[] services) {
        ......
        final int N = services.length;
        for (int i = 0; i < N; i++) {
            Class<?> cl = services[i];
            .....
            try {

                Object newService = SystemUIFactory.getInstance().createInstance(cl);
                mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
            } ......

            mServices[i].mContext = this;
            mServices[i].mComponents = mComponents;
            .......
            mServices[i].start();
            .......
            if (mBootCompleted) {
                mServices[i].onBootCompleted();
            }
        }
        ......
        mServicesStarted = true;
    }
}

       上述类就是启动各个SystemUI子类的入口,Recents相关启动的类为:
       CommandQueue.CommandQueueStart.class:创建了CommandQueue实例,然后执行了putComponent()供其他类使用CommandQueue实例,通过该实例调用toggleRecents();
       Recents.class:将自己注册到CommandQueue,来执行toggleRecents();
       SystemBars.class:启动StatusBar,在StatusBar内部将CommandQueue实例注册到StatusBarManagerService中去,在StatusBarManagerService中调用CommandQueue的方法;
       通过反射来创建以上相关类的对象,创建完后先进行赋值操作,将每个service中的mComponents赋值为同一个,这样任何一个类对象putComponent(),其它类对象都可以通过getComponent()获取到,然后执行start()在内部执行对应的创建及注册操作。
       注册过程如下

image.png
       总结一下
       系统system_server进程启动后,会启动SystemUIService,接着就进行一系列调用,在StatusBar的start()方法内将CommandQueue对象通过调用StatusBarManagerService的registerStatusBar()注册到StatusBarManagerService中,后续收到touch事件进入Recents就通过CommandQueue来进行处理了。

四.显示流程

       调用toggleRecentApps(),会启动RecentsActivity,在RecentsActivity内部会加载RecentsView,在RecentsView里面会加载TaskStackView,在TaskStackView里面循环加载TaskView,最终显示task的详细内容,本文只画出调用流程图,代码就不贴了,详情可根据流程图参考源码来进一步了解:


image.png

五.Task及Snapshot获取

1.Task列表获取流程

       Task列表获取入口是:List<ActivityManager.RecentTaskInfo> mRawTask =
ssp.getRecentTasks(xx,xx,xx,xx);从SystemServicesProxy.java里面的逻辑调用开始,调用流程如下:

public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks,   
    int userId,boolean includeFrontMostExcludedTask, 
    ArraySet<Integer> quietProfileIds) {
    ....................
    List<ActivityManager.RecentTaskInfo> tasks = null;
    try {
        tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
    }

    Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
    while (iter.hasNext()) {
        // 执行过滤
    }
    //返回最终的任务列表
    return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
}

       调用到AMS中,从RecentTasks中获取到TaskRecord列表,然后封装成RecentTaskInfo列表;

public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(
    int maxNum, int flags, int userId) {
    ...............
    mRecentTasks.loadUserRecentsLocked(userId);

    final int recentsCount = mRecentTasks.size();
    ArrayList<ActivityManager.RecentTaskInfo> res =
            new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount);

    for (int i = 0; i < recentsCount && maxNum > 0; i++) {
        TaskRecord tr = mRecentTasks.get(i);
        ....................
        //将TaskRecord转换为RecentTaskInfo
        ActivityManager.RecentTaskInfo rti = 
                createRecentTaskInfoFromTaskRecord(tr);
        res.add(rti);
    }
    //返回RecentTaskInfo列表
    return new ParceledListSlice<>(res);
}

       终通过RecentTasks获取到TaskRecord列表,接下来的调用流程后面会讲到;

2.Snapshot获取流程

       缩略图获取入口是:ThumbnailData thumbnailData = ssp.getTaskThumbnail(taskKey.id,true /* reducedResolution */);从SystemServicesProxy.java里面的逻辑调用开始,调用流程如下:

public @NonNull ThumbnailData getThumbnail(int taskId, boolean reducedResolution) {
    ........
    //ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", true);默认为true,进入
    if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
        ActivityManager.TaskSnapshot snapshot = null;
        try {
            //调用到ActivityManagerService里面的方法
            snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to retrieve snapshot", e);
        }
        if (snapshot != null) {
            thumbnailData = ThumbnailData.createFromTaskSnapshot(snapshot);
        } else {
            return new ThumbnailData();
        }
    } else {
        .............
   }
   return thumbnailData;
}

       从入口SystemServicesProxy会调用到AMS中的方法;

//2.ActivityManagerService.java
public TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
        enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
    final long ident = Binder.clearCallingIdentity();
    try {
        final TaskRecord task;
        synchronized (this) {
            task = mStackSupervisor.anyTaskForIdLocked(taskId,
                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
            if (task == null) {
                Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
                return null;
            }
        }
        // Don't call this while holding the lock as this operation might hit the disk.
        return task.getSnapshot(reducedResolution);
    } finally {
         Binder.restoreCallingIdentity(ident);
    }
}

//3.TaskRecord.java
TaskSnapshot getSnapshot(boolean reducedResolution) {
    // TODO: Move this to {@link TaskWindowContainerController} once recent tasks are more
    // synchronized between AM and WM.
    return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution);
}

//4.WindowManagerService.java
public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution) {
    return mTaskSnapshotController.getSnapshot(taskId, userId, true /* restoreFromDisk */,
                reducedResolution);
}

       经过上面一步一步的调用,最终会调用到TaskSnapshotController这个类中,由这个类来统一管理执行task的截图处理逻辑;

//5.TaskSnapshotController.java
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
            boolean reducedResolution) {
    //最终从TaskSnapshotCache里面获取[前面已经存储了]
    return mCache.getSnapshot(taskId, userId, restoreFromDisk, reducedResolution
                || DISABLE_FULL_SIZED_BITMAPS);
}

//6.TaskSnapshotCache.java
//截完图后会调用该方法存储更新
void putSnapshot(Task task, TaskSnapshot snapshot) {
    final CacheEntry entry = mRunningCache.get(task.mTaskId);
    if (entry != null) {
        mAppTaskMap.remove(entry.topApp);
    }
    final AppWindowToken top = task.getTopChild();
    mAppTaskMap.put(top, task.mTaskId);
    mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, task.getTopChild()));
}

//先从Cache里面取,没有的话再从disk里面取
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
            boolean reducedResolution) {
    synchronized (mService.mWindowMap) {
        // Try the running cache.
        final CacheEntry entry = mRunningCache.get(taskId);
        if (entry != null) {
            return entry.snapshot;
        }
    }

    // Try to restore from disk if asked.
    if (!restoreFromDisk) {
        return null;
    }
    return tryRestoreFromDisk(taskId, userId, reducedResolution);
}

//通过TaskSnapshotLoader从disk里面获取
private TaskSnapshot tryRestoreFromDisk(int taskId, int userId, boolean reducedResolution) {
    final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId, reducedResolution);
    if (snapshot == null) {
        return null;
    }
    return snapshot;
}

//7.TaskSnapshotLoader.java
TaskSnapshot loadTask(int taskId, int userId, boolean reducedResolution) {
    final File protoFile = mPersister.getProtoFile(taskId, userId);
    final File bitmapFile = reducedResolution
                ? mPersister.getReducedResolutionBitmapFile(taskId, userId)
                : mPersister.getBitmapFile(taskId, userId);
    if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) {
        return null;
    }
    try {
        final byte[] bytes = Files.readAllBytes(protoFile.toPath());
        final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
        final Options options = new Options();
        options.inPreferredConfig = Config.HARDWARE;
        final Bitmap bitmap = BitmapFactory.decodeFile(bitmapFile.getPath(), options);
        if (bitmap == null) {
            Slog.w(TAG, "Failed to load bitmap: " + bitmapFile.getPath());
            return null;
        }
        final GraphicBuffer buffer = bitmap.createGraphicBufferHandle();
        if (buffer == null) {
            Slog.w(TAG, "Failed to retrieve gralloc buffer for bitmap: "
                        + bitmapFile.getPath());
            return null;
        }
        return new TaskSnapshot(buffer, proto.orientation,
                new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
                reducedResolution, reducedResolution ? REDUCED_SCALE : 1f);
    } catch (IOException e) {
        Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
        return null;
    }
}

//8.TaskSnapshotPersister.java
1.将snapshot存入/data/system_ce/0/snapshots/
2.从/data/system_ce/0/snapshots/里面取snapshot

3.流程总结

recents.png

六.TaskRecord存储

1.存储时机

       在启动一个Activity后,在Activity Resume后通过mRecentTasks.addLocked(task)将Activity对应的TaskRecord进行存储,具体代码入口为:

void setResumedActivityLocked(ActivityRecord r, String reason) {
        mResumedActivity = r;
        r.state = ActivityState.RESUMED;
        mService.setResumedActivityUncheckLocked(r, reason);
        final TaskRecord task = r.getTask();
        task.touchActiveTime();
        //将task加入RecentTasks
        mRecentTasks.addLocked(task);
    }

      触发入口为:

//ActivityStack.java
//该方法由ActivityStackSupervisor 的 realStartActivityLocked中调用
void minimalResumeActivityLocked(ActivityRecord r) {
        setResumedActivityLocked(r, "minimalResumeActivityLocked");
        r.completeResumeLocked();
        setLaunchTime(r);
    }

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
     .......
     setResumedActivityLocked(next, "resumeTopActivityInnerLocked");
     .......
}

2.存储功能类

2.1.RecentTasks.java

       负责将最近TaskRecord存入列表以及负责获取最近TaskRecord列表,在AMS中进行实例化;

RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) {
        File systemDir = Environment.getDataSystemDirectory();
        mService = service;
        mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this);
        mStackSupervisor.setRecentTasks(this);
}

       在构造方法内部创建TaskPersister,然后将自己引用到 ActivityStackSupervisor内部,供后续调用;

void onSystemReadyLocked() {
    clear();
    mTaskPersister.startPersisting();
}

       在onSystemReadyLocked()内部调用startPersisting()启动线程,接下来会讲到;

 void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
        final ActivityStack stack = task != null ? task.getStack() : null;
        if (stack != null && stack.isHomeOrRecentsStack()) {
            // Never persist the home or recents stack.
            return;
        }
        syncPersistentTaskIdsLocked();
        mTaskPersister.wakeup(task, flush);
    }

       每次有TaskRecord产生或变化时,都会调用notifyTaskPersisterLocked(),在其内部执行mTaskPersister的wakeup()进行相关的操作,接下来会讲到;

2.2.TaskPersister.java

       负责将recent task信息存入/data/system_ce/0/recent_tasks里面以及从/data/system_ce/0/recent_tasks里面获取task信息,简单看一下主要代码:

 TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
            ActivityManagerService service, RecentTasks recentTasks) {
        .............
        mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
    }

       前面讲到,TaskPersister是在RecentTasks构造方法内创建的,也就是说,在RecentTasks创建的时候,TaskPersister也就被创建了,在构造方法内部会创建一个线程LazyTaskWriterThread来对TaskRecord进行存储操作;

void startPersisting() {
        if (!mLazyTaskWriterThread.isAlive()) {
            mLazyTaskWriterThread.start();
        }
    }

       通过startPersisting()来启动LazyTaskWriterThread线程,接下来看一下LazyTaskWriterThread都干了什么工作;

    private class LazyTaskWriterThread extends Thread {

        LazyTaskWriterThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();
            while (true) {
                .......
                final boolean probablyDone;
                synchronized (TaskPersister.this) {
                    probablyDone = mWriteQueue.isEmpty();
                }
                if (probablyDone) {
                    if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
                    persistentTaskIds.clear();
                    synchronized (mService) {
                        if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
                        for (int taskNdx = mRecentTasks.size() - 1; taskNdx >= 0; --taskNdx) {
                            final TaskRecord task = mRecentTasks.get(taskNdx);
                            if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task +
                                    " persistable=" + task.isPersistable);
                            final ActivityStack stack = task.getStack();
                            if ((task.isPersistable || task.inRecents)
                                    && (stack == null || !stack.isHomeOrRecentsStack())) {
                                if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
                                persistentTaskIds.add(task.taskId);
                            } else {
                                if (DEBUG) Slog.d(TAG,
                                        "omitting from persistentTaskIds task=" + task);
                            }
                        }
                        mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
                                mRecentTasks.usersWithRecentsLoadedLocked());
                    }
                    removeObsoleteFiles(persistentTaskIds);
                }
                writeTaskIdsFiles();

                processNextItem();
            }
        }

       从线程的代码可以主要看到以下几点:
       1.在run()内部有while(true),说明是该线程一直存在的;
       2.对obsolete文件进行清理,包括recent_tasks、recent_images文件夹,通过WMS连间接调用TaskSnapshotPersister对snapshots文件夹进行清理;
       3.在while(true)最后执行processNextItem(),因为是死循环,肯定不会一直执行,应该存在wait()操作,接下来一下看一下processNextItem()这个方法:

        private void processNextItem() {
            ......

            // If mNextWriteTime, then don't delay between each call to saveToXml().
            final WriteQueueItem item;
            synchronized (TaskPersister.this) {
                if (mNextWriteTime != FLUSH_QUEUE) {
                    // The next write we don't have to wait so long.
                    mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
                    if (DEBUG) Slog.d(TAG, "Next write time may be in " +
                            INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
                }

                while (mWriteQueue.isEmpty()) {
                    if (mNextWriteTime != 0) {
                        mNextWriteTime = 0; // idle.
                        TaskPersister.this.notifyAll(); // wake up flush() if needed.
                    }
                    try {
                        if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
                        TaskPersister.this.wait();
                    } catch (InterruptedException e) {
                    }
                    // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
                    // from now.
                }
                item = mWriteQueue.remove(0);

                long now = SystemClock.uptimeMillis();
                if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
                        mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
                while (now < mNextWriteTime) {
                    try {
                        if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
                                (mNextWriteTime - now));
                        TaskPersister.this.wait(mNextWriteTime - now);
                    } catch (InterruptedException e) {
                    }
                    now = SystemClock.uptimeMillis();
                }

                // Got something to do.
            }
            .........
            else if (item instanceof TaskWriteQueueItem) {
                // Write out one task.
                StringWriter stringWriter = null;
                TaskRecord task = ((TaskWriteQueueItem) item).mTask;
                if (DEBUG) Slog.d(TAG, "Writing task=" + task);
                synchronized (mService) {
                    if (task.inRecents) {
                        // Still there.
                        try {
                            if (DEBUG) Slog.d(TAG, "Saving task=" + task);
                            stringWriter = saveToXml(task);
                        } catch (IOException e) {
                        } catch (XmlPullParserException e) {
                        }
                    }
                }
                if (stringWriter != null) {
                    // Write out xml file while not holding mService lock.
                    FileOutputStream file = null;
                    AtomicFile atomicFile = null;
                    try {
                        atomicFile = new AtomicFile(new File(
                                getUserTasksDir(task.userId),
                                String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
                        file = atomicFile.startWrite();
                        file.write(stringWriter.toString().getBytes());
                        file.write('\n');
                        atomicFile.finishWrite(file);
                    } catch (IOException e) {
                        if (file != null) {
                            atomicFile.failWrite(file);
                        }
                        Slog.e(TAG,
                                "Unable to open " + atomicFile + " for persisting. " + e);
                    }
                }
            }
        }
    }
}

       会判断mWriteQueue是否为空,如果为空,说明没有任务队列,一直wait(),等待notify(),当被唤醒时,会从mWriteQueue()里面取出任务TaskWriteQueueItem,然后等待3S进行saveToXml[调用TaskRecord.java中的saveToXml,依次将task、TaskDescription、ThumbnailInfo、Intent等信息存入xml中],即把Task以xml形式存入/data/system_ce/0/recent_tasks内部,接下来看一下唤醒操作:

void wakeup(TaskRecord task, boolean flush) {
        synchronized (this) {
            if (task != null) {
                int queueNdx;
                for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
                    final WriteQueueItem item = mWriteQueue.get(queueNdx);
                    if (item instanceof TaskWriteQueueItem &&
                            ((TaskWriteQueueItem) item).mTask == task) {
                        if (!task.inRecents) {
                            // This task is being removed.
                            removeThumbnails(task);
                        }
                        break;
                    }
                }
                if (queueNdx < 0 && task.isPersistable) {
                    mWriteQueue.add(new TaskWriteQueueItem(task));
                }
            } else {
                // Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
                // notified.
                mWriteQueue.add(new WriteQueueItem());
            }
            if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
                mNextWriteTime = FLUSH_QUEUE;
            } else if (mNextWriteTime == 0) {
                mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
            }
            if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
                    + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
                    + " Callers=" + Debug.getCallers(4));
            notifyAll();
        }

        yieldIfQueueTooDeep();
    }

       前面讲到,wakeUp()是在RecentTasks的notifyTaskPersisterLocked()方法调用的,在wakeUp()内部,由于Task不为null,因此会向mWriteQueue内部添加TaskWriteQueueItem(task),然后更新一下mNextWriteTime,最后执行notifyAll()唤醒LazyTaskWriterThread线程内部processNextItem()执行写操作;
       将task存入Android系统以下目录中:

/data/system_ce/0/recent_tasks

       该目录存储之前启动过的task,将task的信息以xx_task.xml保存,内容如下:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<task task_id="8" real_activity="com.android.settings/.Settings" real_activity_suspended="false" orig_activity="com.android.settings/.Settings" affinity="com.android.settings" root_has_reset="false" auto_remove_recents="false" asked_compat_mode="false" user_id="0" user_setup_complete="true" effective_uid="1000" task_type="0" first_active_time="1598295290496" last_active_time="1598295294829" last_time_moved="1598295290501" never_relinquish_identity="true" task_description_color="fff5f5f5" task_description_colorBackground="fffafafa" task_description_icon_filename="/data/system_ce/0/recent_images/8_activity_icon_1598295294794.png" task_thumbnailinfo_task_width="0" task_thumbnailinfo_task_height="0" task_thumbnailinfo_screen_orientation="0" task_affiliation_color="-657931" task_affiliation="8" prev_affiliation="-1" next_affiliation="-1" calling_uid="10021" calling_package="com.android.systemui" resize_mode="1" supports_picture_in_picture="false" privileged="true" min_width="-1" min_height="-1" persist_task_version="1">
<intent action="android.intent.action.MAIN" component="com.android.settings/.Settings" flags="14000000" />
</task>

七.Snapshot截取

1.截图时机

       在TaskRecord发生变化时进行截取,简单来说:一个应用通常对应一个TaskRecord,那么截取时机就是应用退出后进行截取,具体的代码入口为:

void onTransitionStarting() {
    handleClosingApps(mService.mClosingApps);
}

void notifyAppVisibilityChanged(AppWindowToken appWindowToken, boolean visible) {
    if (!visible) {
        handleClosingApps(Sets.newArraySet(appWindowToken));
    }
}

private void handleClosingApps(ArraySet<AppWindowToken> closingApps) {
    if (shouldDisableSnapshots()) {
        return;
    }
    // We need to take a snapshot of the task if and only if all activities of the task are
    // either closing or hidden.
    getClosingTasks(closingApps, mTmpTasks);
    snapshotTasks(mTmpTasks);
}

       最终通过snapshotTasks()来进行截取对应TaskRecord的snapshot;

2.截图功能类

2.1.TaskSnapshotController.java

private void snapshotTasks(ArraySet<Task> tasks) {
    for (int i = tasks.size() - 1; i >= 0; i--) {
        final Task task = tasks.valueAt(i);
        final int mode = getSnapshotMode(task);
        final TaskSnapshot snapshot;
        switch (mode) {
            case SNAPSHOT_MODE_NONE:
                continue;
            case SNAPSHOT_MODE_APP_THEME:
                snapshot = drawAppThemeSnapshot(task);
                break;
            case SNAPSHOT_MODE_REAL:
                snapshot = snapshotTask(task);
                break;
            default:
                snapshot = null;
                break;
        }
        if (snapshot != null) {
            //从snapshot获取GraphicBuffer
            final GraphicBuffer buffer = snapshot.getSnapshot();
            if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
                buffer.destroy();
                Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
                            + buffer.getHeight());
            } else {
                //获取到有效的snapshot
                //1.存入cache,供后续接口调用
                mCache.putSnapshot(task, snapshot);
                //2.通过TaskSnapshotPersister.java存入/data/system_ce/0/snapshots/
                mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
                if (task.getController() != null) {
                    //通知回调onSnapshotChanged()
                    task.getController().reportSnapshotChanged(snapshot);
                }
            }
        }
    }
}
①.根据Task来获取snapshot
//通过task获取到该task的snapshot
private TaskSnapshot snapshotTask(Task task) {
    ......
    //获取GraphicBuffer
    final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
                -1, -1, false, scaleFraction, false, true);
    if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
        return null;
    }
    //组合成TaskSnapshot
    return new TaskSnapshot(buffer, top.getConfiguration().orientation,
            minRect(mainWindow.mContentInsets, mainWindow.mStableInsets),
            isLowRamDevice /* reduced */, scaleFraction /* scale */);
}

       根据Task获取内部最顶层的AppWindowToken,在进行一系列计算操作获取截图对应的minLayer和maxLayer,最后调用SufaceControl对对应layer范围进行截图;

2.2.TaskSnapshotCache.java

②.将snapshot存入cache
//当task从recents里面移除后,在TaskRecord.java里面的removedFromRecents()方法里面会执行
//mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);最终会调用到该方法
//将cache及本地文件里面存储的图片文件删除
void notifyTaskRemovedFromRecents(int taskId, int userId) {
     mCache.onTaskRemoved(taskId);
     mPersister.onTaskRemovedFromRecents(taskId, userId);
}
void putSnapshot(Task task, TaskSnapshot snapshot) {
    final CacheEntry entry = mRunningCache.get(task.mTaskId);
    if (entry != null) {
        mAppTaskMap.remove(entry.topApp);
    }
    final AppWindowToken top = task.getTopChild();
    mAppTaskMap.put(top, task.mTaskId);
    mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, task.getTopChild()));
}

2.3.TaskSnapshotPersister.java

③.将snapshot存文件
/**
 * Persists a snapshot of a task to disk.
 */
void persistSnapshot(int taskId, int userId, TaskSnapshot snapshot) {
    synchronized (mLock) {
        mPersistedTaskIdsSinceLastRemoveObsolete.add(taskId);
        sendToQueueLocked(new StoreWriteQueueItem(taskId, userId, snapshot));
    }
}

private class StoreWriteQueueItem extends WriteQueueItem {
    .................
    
    @Override
    void write() {
        boolean failed = false;
        if (!writeProto()) {
            failed = true;
        }
        if (!writeBuffer()) {
            failed = true;
        }
        if (failed) {
            deleteSnapshot(mTaskId, mUserId);
        }
    }
    .....................
}

       最终通过StoreWriteQueueItem来存储snapshot,存储的对应目录为:

/data/system_ce/0/snapshots

存储了两份snapshot,格式为.jpg,一个是屏幕分辨率大小,另外一个是屏幕分辨率一半大小,对应如下:

15.jpg     15_reduced.jpg

3.回调onTaskSnapshotChanged()

       通过AMS.registerTaskStackListener()来监听TaskSnapshotChanged()回调,当任务缩略图截取完成后可以进行相关逻辑处理,本文只画出调用流程图,代码就不贴了,详情可根据流程图参考源码来进一步了解:


image.png

八.删除Recents任务

       进入最近任务列表后,可以删除已经打开的任务,入口为SystemServicesProxy.removeTask(id),调用流程如下:

//1.SystemServicesProxy.java
public void removeTask(final int taskId) {
    if (mAm == null) return;
    if (RecentsDebugFlags.Static.EnableMockTasks) return;

    // Remove the task.
    mUiOffloadThread.submit(() -> {
        mAm.removeTask(taskId);
    });
}

//2.ActivityManagerService.java
public boolean removeTask(int taskId) {
    .......
    try {
        return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

//3.ActivityStackSupervisor.java
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
    return removeTaskByIdLocked(taskId, killProcess, removeFromRecents, !PAUSE_IMMEDIATELY);
}

       调用removeTaskByIdLocked()来对task进行清除;

boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
            boolean pauseImmediately) {
   final TaskRecord tr = anyTaskForIdLocked(taskId,MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
                INVALID_STACK_ID);
    if (tr != null) {
        //TaskRecord 删除task里面的activity
        tr.removeTaskActivitiesLocked(pauseImmediately);
        //清理需要移除的task
        cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
        if (tr.isPersistable) {
            mService.notifyTaskPersisterLocked(null, true);
        }
        return true;
    }
    Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
    return false;
}

       ①.执行removeTaskActivitiesLocked()将TaskRecord内的mActivities逐一进行finish();

void removeTaskActivitiesLocked(boolean pauseImmediately) {
    performClearTaskAtIndexLocked(0, pauseImmediately);
}

final void performClearTaskAtIndexLocked(int activityNdx, 
        boolean pauseImmediately) {
    int numActivities = mActivities.size();
    for ( ; activityNdx < numActivities; ++activityNdx) {
        final ActivityRecord r = mActivities.get(activityNdx);
        if (r.finishing) {
            continue;
        }
        if (mStack == null) {
            // Task was restored from persistent storage.
            r.takeFromHistory();
            mActivities.remove(activityNdx);
            --activityNdx;
            --numActivities;
        } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, 
                       null, "clear-task-index", false, pauseImmediately)) {
            --activityNdx;
            --numActivities;
        }
    }
}

       ②.执行cleanUpRemovedTaskLocked()来后续清理工作

void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
    if (removeFromRecents) {
        //通过RecentTasks删除task的相关信息
        mRecentTasks.remove(tr);
       //将TaskRecord从Recents里面移除
        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.
    mService.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<>();
    //系统中正在run的进程
    ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.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);
            ...............
            //不满足以上条件后,加入procToKill list中
            // Add process to kill list.
            procsToKill.add(proc);
        }
    }

    // Kill the running processes.
    //是否立即kill process
    for (int i = 0; i < procsToKill.size(); i++) {
        ProcessRecord pr = procsToKill.get(i);
        if ((pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                    && pr.curReceivers.isEmpty())) {
            pr.kill("remove task", true);
        } else {
            // We delay killing processes that are not in the background or running a receiver.
            pr.waitingToKill = "remove task";
        }
    }
}

       ③.通知TaskPersister和TaskSnapshotController删除存储的TaskRecord信息

void removedFromRecents() {
    disposeThumbnail();
    closeRecentsChain();
    if (inRecents) {
        inRecents = false;
        //通过以下方法最终会调用到删除task(../recent_tasks)的逻辑
        mService.notifyTaskPersisterLocked(this, false);
    }

    //通过以下方法最终会调用TaskSnapshotController删除snapshot(../snapshots)的逻辑
    mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);
}

//TaskSnapshotController.java
void notifyTaskRemovedFromRecents(int taskId, int userId) {
     //将cache及本地文件里面存储的图片文件删除
     mCache.onTaskRemoved(taskId);
     mPersister.onTaskRemovedFromRecents(taskId, userId);
}

       ④.通过ProcessRecord来kill进程

void kill(String reason, boolean noisy) {
    if (!killedByAm) {
       .......
        //通过Process kill掉进程
        Process.killProcessQuiet(pid);
        ActivityManagerService.killProcessGroup(uid, pid);
        ......
    }
}

九.启动Recents任务

       当进入多任务后,点击列表中的某个item可以进入对应的应用页面,入口是在SystemServiceProxy.java里面的startActivityFromRecents(),然后一步一步调用AMS、ASS、AS来执行启动逻辑,通过taskId来找到对应的task,通过task找到对应的ActivityStack及task里面的topActivity,经过一系列操作后最后将Activity推到栈顶启动,流程图如下:

image.png
本文大概介绍了Recents工作流程,深入了解的话需要阅读源码!
关于Android 11,可以参考Android11 最近任务Recents功能分析
上一篇 下一篇

猜你喜欢

热点阅读