在安卓系统中,我们经常使用清除最近应用列表,或者是”一键清除“这个功能,最近在系统开发中,需要在一键清除时,保留某个应用不被杀死,需要做一些修改,所以就需要最一键清除这个流程进行了解。
长按HOME键会打开最近的APP列表,打开最近APP列表会执行下面的代码:
IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(ServiceManager.getService("statusbar"));try {statusBarService.toggleRecentApps();} catch (RemoteException e) {Slog.e(LOG_TAG, "Error toggling recent apps.");}
IStatusBarService给了我们一个很明确的方向就是StatusBarService,在StatusBarManagerService类中,toggleRecentApps()的源码如下:
@Overridepublic void toggleRecentApps() {if (mBar != null) {try {mBar.toggleRecentApps();} catch (RemoteException ex) {}}}
mBar的赋值在:
@Overridepublic void registerStatusBar(IStatusBar bar, List<String> iconSlots,List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,Rect fullscreenStackBounds, Rect dockedStackBounds) {......mBar = bar;......
在BaseStatusBar类的start方法中调用了StatusBarManagerService.registerStatusBar方法,下面是调用这个方法的语句:
mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
mCommandQueue被赋值给了StatusBarManagerService.mBar,下面是对这个变量赋值的语句:
mCommandQueue = new CommandQueue(this, iconList);
这是CommandQueue类的对象,这个类的代码在文件”frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java”中。
回到StatusBarManagerService.toggleRecentApps方法中,在这个方法中执行了mBar.toggleRecentApps();语句,在这里调用的toggleRecentApps方法是CommandQueue.toggleRecentApps方法,下面来看一下CommandQueue.toggleRecentApps方法的的源码:
public void toggleRecentApps() {synchronized (mList) {......mHandler.obtainMessage(MSG_TOGGLE_RECENT_APPS, 0, 0, null).sendToTarget();}}
追溯MSG_TOGGLE_RECENT_APPS,从CommandQueue.mHandler到BaseStatusBar.toggleRecentApps到BaseStatusBar.H.handleMessage依次调用,代码如下
//CommandQueue.H.handleMessage:public void handleMessage(Message msg) {if (mPaused) {this.sendMessageAtFrontOfQueue(Message.obtain(msg));return;}final int what = msg.what & MSG_MASK;switch (what) {......case MSG_TOGGLE_RECENT_APPS:mCallbacks.toggleRecentApps();break;......}}
//BaseStatusBar.toggleRecentApps:@Overridepublic void toggleRecentApps() {int msg = MSG_TOGGLE_RECENTS_APPS;......mHandler.sendEmptyMessage(msg);}
BaseStatusBar.H.handleMessagepublic void handleMessage(Message m) {switch (m.what) {......case MSG_TOGGLE_RECENTS_APPS:if (mDeviceProvisioned) {toggleRecents();}break;......}}
这里简单介绍一下statusbar的细节,BaseStatusBar有三个子类:PhoneStatusBar, CarStatusBar, TvStatusBar,很明显我们需要关注的的是PhoneStatusBar
面是PhoneStatusBar.toggleRecents方法的源码:
@Overrideprotected void toggleRecents() {// Toggle the recents visibility flagmSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;notifyUiVisibilityChanged(mSystemUiVisibility);super.toggleRecents();}
前两行语句设置了一下状态,然后就调用父类的toggleRecents方法,PhoneStatusBar的父类是BaseStatusBar。
下面是BaseStatusBar.toggleRecents方法的源码:
protected void toggleRecents() {if (mRecents != null) {sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());}}
mRecents实际调用的是Recents类的对象。Recents类代码在文件”frameworks/base/packages/SystemUI/src/com/android/systemui/recent/Recents.java”中。 下面是Recents.toggleRecents方法的代码:
@Overridepublic void toggleRecents(Display display, int layoutDirection, View statusBarView) {......try {Intent intent = new Intent(RecentsActivity.TOGGLE_RECENTS_INTENT);intent.setClassName("com.android.systemui","com.android.systemui.recent.RecentsActivity");intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);......startActivitySafely(intent, opts.toBundle());}} catch (ActivityNotFoundException e) {Log.e(TAG, "Failed to launch RecentAppsIntent", e);}}
Recents.toggleRecents方法启动了RecentsActivity。RecentsActivity类的代码在文件”frameworks/base/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java”中,RecentsActivity的layout是R.layout.recents 查看xml,其中核心的只有com.android.systemui.recents.views.RecentsView这个控件,这个RecentsView位于”frameworks/base/packages/SystemUI/src/com/android/systemui/recents/views/“ 在RecentView中那个“一键清理”的按钮是
mStackActionButton = (TextView) inflater.inflate(Recents.getConfiguration().isLowRamDevice? R.layout.recents_low_ram_stack_action_button: R.layout.recents_stack_action_button,this, false);
mStackActionButton的点击功能位于android/systemui/recents/views/RecentsViewTouchHandler.java中,在RecentsView的点击事件通过RecentsViewTouchHandler进行处理。RecentsViewTouchHandler中处理点击事件的代码如下:
private boolean handleTouchEvent(MotionEvent ev) {int action = ev.getActionMasked();......case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL: {if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {EventBus.getDefault().send(new DismissAllTaskViewsEvent());consumed = true;}}.......return consumed;}追溯DismissAllTaskViewsEvent,可以找到两个地方,一个回到RecentView中``` javapublic final void onBusEvent(DismissAllTaskViewsEvent event) {SystemServicesProxy ssp = Recents.getSystemServices();if (!ssp.hasDockedTask()) {// Animate the background away only if we are dismissing Recents to homeanimateBackgroundScrim(0f, DEFAULT_UPDATE_SCRIM_DURATION);}
BackgroundScrim是在最近应用视图中,在下方的一个渐变的颜色,这里让它动态消失,另一个是frameworks/base/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java中的
public final void onBusEvent(final DismissAllTaskViewsEvent event) {// Keep track of the tasks which will have their data removedArrayList<Task> tasks = new ArrayList<>(mStack.getTasks());mAnimationHelper.startDeleteAllTasksAnimation(getTaskViews(), useGridLayout(), event.getAnimationTrigger());event.addPostAnimationCallback(new Runnable() {@Overridepublic void run() {// Remove all tasks and delete the task data for all tasksmStack.removeAllTasks(true /* notifyStackChanges */);for (int i = tasks.size() - 1; i >= 0; i--) {EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));}}});}
首先执行
mAnimationHelper.startDeleteAllTasksAnimation(getTaskViews(), useGridLayout(), event.getAnimationTrigger());
这里就是展示动画,把界面上显示的任务显示一个动画后清屏 ,在callback中的重点是mStack.removeAllTasks(true /* notifyStackChanges */);和EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));。mStack.removeAllTasks(true /* notifyStackChanges */);调用了frameworks/base/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java的removeAllTasks
public void removeAllTasks(boolean notifyStackChanges) {ArrayList<Task> tasks = mStackTaskList.getTasks();for (int i = tasks.size() - 1; i >= 0; i--) {Task t = tasks.get(i);mStackTaskList.remove(t);mRawTaskList.remove(t);}if (mCb != null && notifyStackChanges) {// Notify that all tasks have been removedmCb.onStackTasksRemoved(this);}}
mCb是TaskStackCallbacks,TaskStackView实现了这一接口
@Overridepublic void onStackTasksRemoved(TaskStack stack) {// Reset the focused taskresetFocusedTask(getFocusedTask());// Return all the views to the poolList<TaskView> taskViews = new ArrayList<>();taskViews.addAll(getTaskViews());for (int i = taskViews.size() - 1; i >= 0; i--) {mViewPool.returnViewToPool(taskViews.get(i));}// Remove all the ignore tasksmIgnoreTasks.clear();// If there are no remaining tasks, then just close recentsEventBus.getDefault().send(new AllTaskViewsDismissedEvent(R.string.recents_empty_message_dismissed_all));}
回到EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));追溯执行位置在RecentActivity中,代码如下
public final void onBusEvent(DeleteTaskDataEvent event) {// Remove any stored data from the loaderRecentsTaskLoader loader = Recents.getTaskLoader();loader.deleteTaskData(event.task, false);// Remove the task from activity managerActivityManagerWrapper.getInstance().removeTask(event.task.key.id);}
最终调用了ActivityManagerWrapper.getInstance().removeTask(event.task.key.id);,ActivityManagerWrapper的removeTask代码如下
public void removeTask(int taskId) {mBackgroundExecutor.submit(new Runnable() {@Overridepublic void run() {try {ActivityManager.getService().removeTask(taskId);} catch (RemoteException e) {Log.w(TAG, "Failed to remove task=" + taskId, e);}}});}
最终是调用了ActivityManager.getService().removeTask(taskId);结束了这一task。
总结
结束最近使用APP的过程,是从statusbar,到recentActivity,显示最近应用的列表,在界面中显示RecentView,RecentView包括应用列表和清除所有应用的文字,点击一键清除调用到了RecentsViewTouchHandler,之后通过eventbus和handler调用到了TaskStackView,之后就区分两部分,一部分执行动画,一部分处理taskstack的数据,最终会调用到AMS的removeTask结束task。
