背景
某天,测试提了一个bug,说当前页面关闭了以后回到了上一个页面,但是对应的音乐并没有立刻停止,而是过了一段时间才停止。于是翻阅了一下代码:
@Override
protected void onStop() {
super.onStop();
if (mIsLoading) {
mAudioTool.pausePlayAudio();
}
}
mAudioTool.pausePlayAudio方法是停止播放音频的代码,似乎并没有什么问题。而且这段代码已经运行了好久,之前测试也是通过的,为何这个版本会出现这个问题?
所以首先我debug一下当前页面的onStop方法,结果发现页面关闭的时候onStop方法并没有被执行,然后过了差不多10s左右再被执行,感觉又是踩到了某个坑里面去了,仔细回想了最近改了的代码,问题可能出在了上一个页面的下面这段代码:
/**
* 开始抖动
*/
private fun startShake() {
if(mRotateAnim == null){
mRotateAnim = RotateAnimation(-2f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
}
if (mRotateAnim!!.hasStarted() && !mRotateAnim!!.hasEnded()) {
//当前动画已经开始并且没有结束
return
}
//从左向右
mRotateAnim!!.duration = 50
mRotateAnim!!.repeatMode = Animation.REVERSE
mRotateAnim!!.repeatCount = Animation.INFINITE
val smallAnimationSet = AnimationSet(false)
smallAnimationSet.addAnimation(mRotateAnim)
cl_wrong_book.startAnimation(smallAnimationSet)
}
由于设置的repeatCount是INFINITE,所以动画是一直在执行中的。
不过还是有点纳闷,动画跟Activity生命周期有啥关系?为了印证下到底是不是Animation影响的当前页面生命周期调用异常,于是将这段代码进行了删除操作。结果Activity的onStop生命周期还真的就正常执行了。然后自己又做了一个demo,来查看下onStop的执行时间。
(这段是没有动画的)
2020-12-14 12:40:17.334 24575-24575/com.example.demo I/MainActivity: onCreate
2020-12-14 12:40:17.663 24575-24575/com.example.demo I/MainActivity: onStart
2020-12-14 12:40:17.670 24575-24575/com.example.demo I/MainActivity: onResume
2020-12-14 12:40:20.818 24575-24575/com.example.demo I/MainActivity: onPause
2020-12-14 12:40:20.836 24575-24575/com.example.demo I/SecondActivity: onCreate
2020-12-14 12:40:20.857 24575-24575/com.example.demo I/SecondActivity: onStart
2020-12-14 12:40:20.858 24575-24575/com.example.demo I/SecondActivity: onResume
2020-12-14 12:40:21.406 24575-24575/com.example.demo I/MainActivity: onStop
2020-12-14 12:40:22.930 24575-24575/com.example.demo I/SecondActivity: onPause
2020-12-14 12:40:22.936 24575-24575/com.example.demo I/MainActivity: onStart
2020-12-14 12:40:22.937 24575-24575/com.example.demo I/MainActivity: onResume
2020-12-14 12:40:23.439 24575-24575/com.example.demo I/SecondActivity: onStop
2020-12-14 12:40:23.440 24575-24575/com.example.demo I/SecondActivity: onDestroy
(这段是加上动画的)
2020-12-14 12:38:06.392 24278-24278/com.example.demo I/MainActivity: onCreate
2020-12-14 12:38:06.563 24278-24278/com.example.demo I/MainActivity: onStart
2020-12-14 12:38:06.565 24278-24278/com.example.demo I/MainActivity: onResume
2020-12-14 12:38:23.940 24278-24278/com.example.demo I/MainActivity: onPause
2020-12-14 12:38:23.964 24278-24278/com.example.demo I/SecondActivity: onCreate
2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onStart
2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onResume
2020-12-14 12:38:24.544 24278-24278/com.example.demo I/MainActivity: onStop
2020-12-14 12:38:28.111 24278-24278/com.example.demo I/SecondActivity: onPause
2020-12-14 12:38:28.117 24278-24278/com.example.demo I/MainActivity: onStart
2020-12-14 12:38:28.118 24278-24278/com.example.demo I/MainActivity: onResume
2020-12-14 12:38:38.153 24278-24278/com.example.demo I/SecondActivity: onStop
2020-12-14 12:38:38.155 24278-24278/com.example.demo I/SecondActivity: onDestroy
demo的log也显示了在加上动画以后,确实onStop和onDestroy就会被延迟执行,而且试了多次,发现每次延迟的时间都是10s左右。所以猜测一定存在某种定时执行onStop操作的场景,不然不可能每次都这么凑巧。
既然踩到坑,那么我就得想办法搞清楚为什么会出现这种情况。所以还是跟以前一样,查阅Android源码一探究竟。
finish()操作
/**
* Finishes the current activity and specifies whether to remove the task associated with this
* activity.
*/
@UnsupportedAppUsage
private void finish(int finishTask) {
if (mParent == null) {
int resultCode;
Intent resultData;
synchronized (this) {
resultCode = mResultCode;
resultData = mResultData;
}
if (false) Log.v(TAG, "Finishing self: token=" + mToken);
try {
if (resultData != null) {
resultData.prepareToLeaveProcess(this);
}
if (ActivityTaskManager.getService()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
// Empty
}
} else {
mParent.finishFromChild(this);
}
// Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
// be restored now.
if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_RESTORE,
mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
}
}
/**
* Call this when your activity is done and should be closed. The
* ActivityResult is propagated back to whoever launched you via
* onActivityResult().
*/
public void finish() {
finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}
Activity的finish方法会调用自身带有参数的finish方法,然后通过Binder会执行ActivityTaskManagerService的finishActivity方法。
@Override
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
int finishTask) {
...代码省略...
synchronized (mGlobalLock) {
...代码省略...
try {
boolean res;
final boolean finishWithRootActivity =
finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
|| (finishWithRootActivity && r == rootR)) {
...代码省略
} else {
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
}
}
return res;
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
由于finishTask是DONT_FINISH_TASK_WITH_ACTIVITY类型,所以会走else分支,tr.getStack()得到的是ActivityStack对象,所以接下来执行的就是ActivityStack的requestFinishActivityLocked方法。
/**
* @return Returns true if this activity has been removed from the history
* list, or false if it is still in the list and will be removed later.
*/
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
String reason, boolean oomAdj, boolean pauseImmediately) {
if (r.finishing) {
//这个判断条件是为了防止多次进入,做了一道屏障
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
}
mWindowManager.deferSurfaceLayout();
try {
//这个方法是为了将finishing设置为true
r.makeFinishingLocked();
final TaskRecord task = r.getTaskRecord();
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
r.mUserId, System.identityHashCode(r),
task.taskId, r.shortComponentName, reason);
final ArrayList activities = task.mActivities;
final int index = activities.indexOf(r);
if (index < (activities.size() - 1)) {
task.setFrontOfTask();
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
// If the caller asked that this activity (and all above it)
// be cleared when the task is reset, don't lose that information,
// but propagate it up to the next activity.
ActivityRecord next = activities.get(index+1);
next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}
//停止按键的事件分发
r.pauseKeyDispatchingLocked();
adjustFocusedActivityStack(r, "finishActivity");
//检查是否有设置ActivityResult,如果存在则加入列表中
finishActivityResultsLocked(r, resultCode, resultData);
final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
//当前页面处于Resume状态,所以会进入此分支
if (mResumedActivity == r) {
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare close transition: finishing " + r);
if (endTask) {
mService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
task.getTaskInfo());
}
getDisplay().mDisplayContent.prepareAppTransition(transit, false);
// Tell window manager to prepare for this one to be removed.
r.setVisibility(false);
if (mPausingActivity == null) {
if (DEBUG_PAUSE) Slog.