在任务栏中清除掉播放器的进程,状态栏仍有音乐播放器状态,且音乐仍在后台播放
1.先从UI切入问题分析,使用Monitor—Hierarchy View找到清除任务X按钮对应的id为r.id.dismiss_task,并且属于SystemUI
2.到SystemUI定位到包含dismiss_task的是TaskViewHeader.java,该按钮对应的onClick事件为:
TaskView tv = Utilities.findParent(this, TaskView.class);
tv.dismissTask();
// Keep track of deletions by the dismiss button
MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
Constants.Metrics.DismissSourceHeaderButton);
3.查看TaskView.java中的dismissTask
void dismissTask() {
// Animate out the view and call the callback
final TaskView tv = this;
DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv);
dismissEvent.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
EventBus.getDefault().send(new TaskViewDismissedEvent(mTask, tv,
new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
Interpolators.FAST_OUT_SLOW_IN)));
}
});
EventBus.getDefault().send(dismissEvent);
}
这里EventBus send一个DismissTaskViewEvent,处理完成会回调再发生一个TaskViewDismissedEvent
5.定位到处理DismissTaskViewEvent是在TaskStackView.java
public final void onBusEvent(DismissTaskViewEvent event) {
// For visible children, defer removing the task until after the animation
mAnimationHelper.startDeleteTaskAnimation(
event.taskView, useGridLayout(), event.getAnimationTrigger());
}
处理TaskViewDismissedEvent
public final void onBusEvent(TaskViewDismissedEvent event) {
// Announce for accessibility
announceForAccessibility(getContext().getString(
R.string.accessibility_recents_item_dismissed, event.task.title));
if (useGridLayout() && event.animation != null) {
event.animation.setListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animator) {
if (mTaskViewFocusFrame != null) {
// Resize the grid layout task view focus frame
mTaskViewFocusFrame.resize();
}
}
});
}
// Remove the task from the stack
mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
if (mStack.getTaskCount() > 0 && Recents.getConfiguration().isLowRamDevice) {
EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
}
MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
event.task.key.getComponent().toString());
}
6.接着DeleteTaskDataEvent在RecentsActivity.java处理
public final void onBusEvent(DeleteTaskDataEvent event) {
// Remove any stored data from the loader
RecentsTaskLoader loader = Recents.getTaskLoader();
loader.deleteTaskData(event.task, false);
// Remove the task from activity manager
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.removeTask(event.task.key.id);
}
7.SystemServicesProxy.java中removeTask
/** Removes the task */
public void removeTask(final int taskId) {
if (mAm == null) return;
if (RecentsDebugFlags.Static.EnableMockTasks) return;
// Remove the task.
mUiOffloadThread.submit(() -> {
mAm.removeTask(taskId);
});
}
这里就是关键代码了,通过调用ActivityManager的removeTask来删除任务
8.通过ActivityManager.java其对应的是ActivityManagerService.java
@Override
public boolean removeTask(int taskId) {
enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, "removeTask()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
9.ActivityStackSupervisor.java
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) {
tr.removeTaskActivitiesLocked(pauseImmediately);
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;
}
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.
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 procsToKill = new ArrayList<>();
ArrayMap> pmap = mService.mProcessNames.getMap();
for (int i = 0; i < pmap.size(); i++) {
SparseArray 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 == mService.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).getTask();
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.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";
}
}
}
通过上述定位会发现music app包含了前台的服务foregroundServices,所以其process不会被kill掉。这个是google原生设计的。
那解决该问题,我们可以增加判断如果是removeFromRecents 为true时将其包含foregroundServices也kill掉