[深入SystemUI]-了解recents的启动流程(一)

本文流程基于Android 9.0
preloadRecentApps()流程介绍

1. PhoneWindowManager的事件分发

PhoneWindowManager处理点击事件的方法是interceptKeyBeforeDispatching()。相应处理逻辑如下: 点击switch

} else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
    //不处于锁屏界面
    if (!keyguardOn) {
        //ACTION_DOWN并且只点了一次
        if (down && repeatCount == 0) {
            //预加载recent
            preloadRecentApps();
        } else if (!down) {
            //当不处于ACTION_DOWN时,可能是up或者cancel
            toggleRecentApps();
        }
    }
    return -1;
}
复制代码

2.调用StatusBaraManbagerService的preloadRecentApps

private void preloadRecentApps() {
    mPreloadedRecentApps = true;
    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
    if (statusbar != null) {
        //调用statusbar的preloadRecentApps()
        statusbar.preloadRecentApps();
    }
}
复制代码

这里的是statusbar是通过getStatusBarManagerInternal()获取的,getStatusBarManagerInternal()实现如下:

StatusBarManagerInternal getStatusBarManagerInternal() {
    synchronized (mServiceAquireLock) {
        if (mStatusBarManagerInternal == null) {
            mStatusBarManagerInternal =
                    LocalServices.getService(StatusBarManagerInternal.class);
        }
        return mStatusBarManagerInternal;
    }
}
复制代码

那么通过LocalServices.getService()是怎么得到的呢?其实在StatusBarManagerService初始化的时候,就将StatusBarManagerInernal的实现加进了LocalService中。

/**
 * Construct the service, add the status bar view to the window manager
 */
public StatusBarManagerService(Context context, WindowManagerService windowManager) {
    mContext = context;
    mWindowManager = windowManager;

    LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
    LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
}
复制代码

mInternalService的实现如下:

/**
 * Private API used by NotificationManagerService.
 */
private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
    private boolean mNotificationLightOn;

    // 接口其他一些方法的实现

    @Override
    public void toggleRecentApps() {
        if (mBar != null) {
            try {
                mBar.toggleRecentApps();
            } catch (RemoteException ex) {}
        }
    }

    @Override
    public void setCurrentUser(int newUserId) {
        if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId);
        mCurrentUserId = newUserId;
    }


    @Override
    public void preloadRecentApps() {
        if (mBar != null) {
            try {
                mBar.preloadRecentApps();
            } catch (RemoteException ex) {}
        }
    }

    @Override
    public void cancelPreloadRecentApps() {
        if (mBar != null) {
            try {
                mBar.cancelPreloadRecentApps();
            } catch (RemoteException ex) {}
        }
    }

    @Override
    public void showRecentApps(boolean triggeredFromAltTab) {
        if (mBar != null) {
            try {
                mBar.showRecentApps(triggeredFromAltTab);
            } catch (RemoteException ex) {}
        }
    }

    @Override
    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
        if (mBar != null) {
            try {
                mBar.hideRecentApps(triggeredFromAltTab, triggeredFromHomeKey);
            } catch (RemoteException ex) {}
        }
    }
    // 接口其他一些方法的实现
};
复制代码

3.跨进程调用preloadRecentApps的实际实现

可以看出,调用mInternalService实际上是调用mBar:IStatusBar的方法。

mBar对应的对象时什么呢?

// ================================================================================
// Callbacks from the status bar service.
// ================================================================================
@Override
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
        List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
        Rect fullscreenStackBounds, Rect dockedStackBounds) {
    enforceStatusBarService();

    Slog.i(TAG, "registerStatusBar bar=" + bar);
    mBar = bar;
    // code...
}
复制代码

registerStatusBar在哪里调用呢?在StatusBarstart()方法中调用到:

@Override
public void start() {
    //code...
    try {
        mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
                fullscreenStackBounds, dockedStackBounds);
    } catch (RemoteException ex) {
        // If the system process isn't there we're doomed anyway.
    }
    //code...
}
复制代码

CommandQueue其实是IStatusBar的一个代理类,调用mCommandQueuepreloadRecentApps()方法其实是调用CommandQueue内部接口CallbackspreloadRecentApps(),而实现CommandQueue.Callbacks是在Recents类中实现的,对应的preloadRecentApps()的实际实现也是在Recents中,具体实现如下:

/**
 * Preloads info for the Recents activity.
 */
@Override
public void preloadRecentApps() {
    // Ensure the device has been provisioned before allowing the user to interact with
    // recents
    if (!isUserSetup()) {
        return;
    }

    if (mOverviewProxyService.getProxy() != null) {
        // TODO: Proxy to Launcher
        return;
    }

    int currentUser = sSystemServicesProxy.getCurrentUser();
    //判断当前用户是否为系统用户
    if (sSystemServicesProxy.isSystemUser(currentUser)) {
        //mImpl为RecentsImpl,调用的实际上是RecentsImpl的preloadRecents()
        mImpl.preloadRecents();
    } else {
        if (mSystemToUserCallbacks != null) {
            IRecentsNonSystemUserCallbacks callbacks =
                    mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
            if (callbacks != null) {
                try {
                    callbacks.preloadRecents();
                } catch (RemoteException e) {
                    Log.e(TAG, "Callback failed", e);
                }
            } else {
                Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
            }
        }
    }
}
复制代码

4.从AMS中获取任务队列,加入到任务栈TaskStack中

RecentsImplpreloadRecents方法:

public void preloadRecents() {
    //如果屏幕固定,直接返回,屏幕固定功能是在Android5.1上增加的,当打开此功能时,不允许切换到其他界面
    if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
        return;
    }

    // Skip preloading recents when keyguard is showing
    final StatusBar statusBar = getStatusBar();
    if (statusBar != null && statusBar.isKeyguardShowing()) {
        return;
    }

    // Preload only the raw task list into a new load plan (which will be consumed by the
    // RecentsActivity) only if there is a task to animate to.  Post this to ensure that we
    // don't block the touch feedback on the nav bar button which triggers this.
    mHandler.post(() -> {
        SystemServicesProxy ssp = Recents.getSystemServices();
        // 判断最近任务不可见
        if (!ssp.isRecentsActivityVisible(null)) {
            ActivityManager.RunningTaskInfo runningTask =
                    ActivityManagerWrapper.getInstance().getRunningTask();
            //如果获取到的runningTask(运行着的任务)为null时,则直接返回
            if (runningTask == null) {
                return;
            }

            RecentsTaskLoader loader = Recents.getTaskLoader();
            sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
            loader.preloadTasks(sInstanceLoadPlan, runningTask.id);
            TaskStack stack = sInstanceLoadPlan.getTaskStack();
            if (stack.getTaskCount() > 0) {
                // Only preload the icon (but not the thumbnail since it may not have been taken
                // for the pausing activity)
                preloadIcon(runningTask.id);

                // At this point, we don't know anything about the stack state.  So only
                // calculate the dimensions of the thumbnail that we need for the transition
                // into Recents, but do not draw it until we construct the activity options when
                // we start Recents
                updateHeaderBarLayout(stack, null /* window rect override*/);
            }
        }
    });
}
复制代码

RecentsTaskLoaderpreloadTasks()方法:

/** Preloads recents tasks using the specified plan to store the output. */
public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) {
    preloadTasks(plan, runningTaskId, ActivityManagerWrapper.getInstance().getCurrentUserId());
}

/** Preloads recents tasks using the specified plan to store the output. */
public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
        int currentUserId) {
    try {
        Trace.beginSection("preloadPlan");
        plan.preloadPlan(new PreloadOptions(), this, runningTaskId, currentUserId);
    } finally {
        Trace.endSection();
    }
}
复制代码

RecentsTaskLoadPlanpreloadPlan()方法,将从AMS中获取的任务队列加入到任务栈TaskStack中:

/**
 * Preloads the list of recent tasks from the system. After this call, the TaskStack will
 * have a list of all the recent tasks with their metadata, not including icons or
 * thumbnails which were not cached and have to be loaded.
 *
 * The tasks will be ordered by:
 * - least-recent to most-recent stack tasks
 *
 * Note: Do not lock, since this can be calling back to the loader, which separately also drives
 * this call (callers should synchronize on the loader before making this call).
 */
public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId,
        int currentUserId) {
    Resources res = mContext.getResources();
    ArrayList<Task> allTasks = new ArrayList<>();
    if (mRawTasks == null) {
        //从ActivityManagerService中获取原始的任务列表
        mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
                ActivityManager.getMaxRecentTasksStatic(), currentUserId);

        // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
        Collections.reverse(mRawTasks);
    }

    int taskCount = mRawTasks.size();
    //通过遍历,将RawTask变为最近任务中可以用于加载显式的Task类型,最终加入到最近任务栈中mStack
    for (int i = 0; i < taskCount; i++) {
        ActivityManager.RecentTaskInfo t = mRawTasks.get(i);

        // Compose the task key
        final ComponentName sourceComponent = t.origActivity != null
                // Activity alias if there is one
                ? t.origActivity
                // The real activity if there is no alias (or the target if there is one)
                : t.realActivity;
        final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
        TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent,
                sourceComponent, t.userId, t.lastActiveTime);

        boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
        boolean isStackTask = !isFreeformTask;
        boolean isLaunchTarget = taskKey.id == runningTaskId;

        ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
        if (info == null) {
            continue;
        }

        // Load the title, icon, and color
        String title = opts.loadTitles
                ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription)
                : "";
        String titleDescription = opts.loadTitles
                ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
                : "";
        Drawable icon = isStackTask
                ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false)
                : null;
        ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
                false /* loadIfNotCached */, false /* storeInCache */);
        int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
        int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
        boolean isSystemApp = (info != null) &&
                ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);

        // TODO: Refactor to not do this every preload
        if (mTmpLockedUsers.indexOfKey(t.userId) < 0) {
            mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId));
        }
        boolean isLocked = mTmpLockedUsers.get(t.userId);

        // Add the task to the stack
        Task task = new Task(taskKey, icon,
                thumbnail, title, titleDescription, activityColor, backgroundColor,
                isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow,
                t.taskDescription, t.resizeMode, t.topActivity, isLocked);

        allTasks.add(task);
    }

    // Initialize the stacks
    mStack = new TaskStack();
    //这里是预先将任务队列加载显式到TaskStackView上
    mStack.setTasks(allTasks, false /* notifyStackChanges */);
}
复制代码

注意:保存任务队列的都是一些临时变量,会伴随着gc回收掉。

参考:Android输入事件从读取到分发五:事件分发前的拦截过程

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值