上一篇我们分析了Activity的启动流程,由于代码量很大,还是没有分析的很详细,但是基本流程都出来了,更详细的东西还是要去看源码,源码在我文章的最后有给出,里面有我添加的详细的注释。这一章来分析Activity的finish过程,这个问题在两年前我去面试金山的时候曾经被问到过,那时候对源码基本没什么了解,所以当时根本是不了解的,今天我们就来分析一下finish的过程到底做了哪些处理,最后对Activity的整个启动过程以及finish过程绘制流程图,以方便我们记忆。

finish代码分析

首先先贴一张时序图:

Activity启动时序图

Activity的finish方法就是调用的父类的finish方法:

Step1.Activity.finish

1
2
3
public void finish() {
finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}

这里调用finish方法,传入参数DONT_FINISH_TASK_WITH_ACTIVITY,这个参数是在finish掉Activity的时候不finish掉Task。

Step2.Activity.finish

1
2
3
4
5
6
7
8
private void finish(int finishTask) {
...
if (ActivityManagerNative.getDefault()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
...
}

ActivityManagerNative.getDefault()方法其实我们在前面文章提到过,得到的是AMP(ActivityManagerProxy)。

Step3.AMP.finishActivity

1
2
3
4
5
6
public boolean finishActivity(IBinder token, int resultCode, Intent resultData, int finishTask)
throws RemoteException {
...
mRemote.transact(FINISH_ACTIVITY_TRANSACTION, data, reply, 0);
...
}

通过Binder调用AMS的finishActivity方法。

Step4.AMS.finishActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
int finishTask) {
...
synchronized (this) {
// 根据token(IBinder)获取Activity的对象封装ActivityRecord
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return true;
}
// Keep track of the root activity of the task before we finish it
TaskRecord tr = r.task;
// 从栈底部获取第一没有被finish的Activity对象封装
ActivityRecord rootR = tr.getRootActivity();
...
if (mController != null) {
// Find the first activity that is not finishing.
// 获取第一个没有被回收的Activity
ActivityRecord next = r.task.stack.topRunningActivityLocked(token, 0);
if (next != null) {
...
}
}
final long origId = Binder.clearCallingIdentity();
try {
// 传入的finishTask是DONT_FINISH_TASK_WITH_ACTIVITY,所以下面参数是false
final boolean finishWithRootActivity =
finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
// 这里if中条件为false所以走else语句
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
|| (finishWithRootActivity && r == rootR)) {
res = removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
...
} else {
res = tr.stack.requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
...
}
return res;
} finally {
...
}
}
}

上面try语句中会走else语句中的方法也就是ActivityStack.requestFinishActivityLocked

Step5.ActivityStack.requestFinishActivityLocked

1
2
3
4
5
6
7
8
final boolean requestFinishActivityLocked(IBinder token, int resultCode,
Intent resultData, String reason, boolean oomAdj) {
ActivityRecord r = isInStackLocked(token);
...

finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
return true;
}

调用finishActivityLocked方法

Step6.ActivityStack.finishActivityLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
String reason, boolean oomAdj) {
if (r.finishing) {// 如果要清理的的正在finish,则不需要再清理
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
}

// 标记开始finish
r.makeFinishingLocked();
...

// 停止键盘分发事件
r.pauseKeyDispatchingLocked();

adjustFocusedActivityLocked(r, "finishActivity");

finishActivityResultsLocked(r, resultCode, resultData);

// 如果任务中没有了Activity要结束任务
final boolean endTask = index <= 0;
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
if (mResumedActivity == r) {// 如果当前显示的Activity就是要结束的Activity

...

if (mPausingActivity == null) {
// 开始暂停Activity
startPausingLocked(false, false, null, false);
}

if (endTask) {// 如果任务中没有了Activity,则移除任务
mStackSupervisor.removeLockedTaskLocked(task);
}
} else if (r.state != ActivityState.PAUSING) {// 如果要移除的Activity不是Pausing状态并且不是正在显示的Activity
...
return finishCurrentActivityLocked(r, (r.visible || r.nowVisible) ?
FINISH_AFTER_VISIBLE : FINISH_AFTER_PAUSE, oomAdj) == null;
} else {
...
}

return false;
}

这里首先调用ActivityRecord.makeFinishingLocked标记开始结束Activity,然后调用ActivityRecord.pauseKeyDispatchingLocked方法停止键盘事件分发。然后调用adjustFocusedActivityLocked方法,调整Activity的焦点状体。

Step7.ActivityStack.adjustFocusedActivityLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void adjustFocusedActivityLocked(ActivityRecord r, String reason) {
// 如果当前栈没有焦点,或者当前Activity也不是当前有焦点的Activity,返回
if (!mStackSupervisor.isFocusedStack(this) || mService.mFocusedActivity != r) {
return;
}

// 获取栈顶正在运行的Activity
final ActivityRecord next = topRunningActivityLocked();
final String myReason = reason + " adjustFocus";
if (next != r) {// 如果栈顶正在运行的不是当前要结束的Activity
if (next != null && StackId.keepFocusInStackIfPossible(mStackId) && isFocusable()) {
mService.setFocusedActivityLocked(next, myReason);
return;
} else {
final TaskRecord task = r.task;
if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
final int taskToReturnTo = task.getTaskToReturnTo();
if (!mFullscreen
&& adjustFocusToNextFocusableStackLocked(taskToReturnTo, myReason)) {
return;
}
if (mStackSupervisor.moveHomeStackTaskToTop(taskToReturnTo, myReason)) {
return;
}
}
}
}

mService.setFocusedActivityLocked(mStackSupervisor.topRunningActivityLocked(), myReason);
}

当前Activity要finish掉,就找找到下一个应该获取焦点的Activity,在该Activity被finish掉之后显示出来。

Step8.ActivityStack.finishActivityResultsLocked

1
2
3
4
5
6
7
8
9
10
11
12
final void finishActivityResultsLocked(ActivityRecord r, int resultCode, Intent resultData) {
// send the result
ActivityRecord resultTo = r.resultTo;
if (resultTo != null) {
...
resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
resultData);
r.resultTo = null;
} else if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "No result destination from " + r);

...
}

如果要接收Result的Activity描述对象ActivityRecord还存在,就将result相关信息封装成ActivityResult对象放到ActivityResult列表中保存起来。然后置空该Activity对应的ActivityRecord对象中的信息。

Step9.ActivityStack.startPausingLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
ActivityRecord resuming, boolean dontWait) {
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ " state=" + mPausingActivity.state);
if (!mService.isSleepingLocked()) {// 如果没有在睡眠,则完成Pause状态
completePauseLocked(false, resuming);
}
}
// prev指向源Activity
ActivityRecord prev = mResumedActivity;
if (prev == null) {
if (resuming == null) {
Slog.wtf(TAG, "Trying to pause when nothing is resumed");
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
return false;
}

if (mActivityContainer.mParentActivity == null) {
// Top level stack, not a child. Look for child stacks.
// 暂停所有子栈的Activity
mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);
}

...
// 改变状态
prev.state = ActivityState.PAUSING;
...
// 获取正在启动的Activity组件
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
...

if (prev.app != null && prev.app.thread != null) {
...
// 通知源Activity组件,暂停源Activity以便有机会执行一些数据保存操作,
// 这里如果目标sdk<11会调用onSaveInstanceState,然后调用onPause
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags, dontWait);
...
} else {
...
}

...
}

这里主要是在finish之前pause该Activity,如果状态正常会调用schedulePauseActivity方法,最终调用Activity的onPause方法,这个过程我们在前面讲过几次了,这里就不再分析,比较简单。我们接着看核心的代码。

Step10.ActivityStack.finishCurrentActivityLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj) {
// 如果当前要finish的Activity正在显示,而复用的Activity还没有显示,那么延迟finish,
// 直到复用Activity显示

// 获取顶部正在运行的Activity
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();

// 如果是显示后在finish,并且要结束的正在显示或者将要显示,并且顶部运行的存在而且没有显示
if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
&& next != null && !next.nowVisible) {
// 如果当前要结束的Activity不在正在停止列表中则添加到停止列表中
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
addToStopping(r, false /* immediate */);
}
...
return r;
}

// make sure the record is cleaned out of other places.
// 从各个列表中清理这个将要被finish的Activity
...
final ActivityState prevState = r.state;// 存储清理之前的状态
r.state = ActivityState.FINISHING;// 设置为正在finish状态
// 判断当前正在清理的Activity是否在当前获取焦点的栈中
final boolean finishingActivityInNonFocusedStack
= r.task.stack != mStackSupervisor.getFocusedStack()
&& prevState == ActivityState.PAUSED && mode == FINISH_AFTER_VISIBLE;

if (mode == FINISH_IMMEDIATELY
|| (prevState == ActivityState.PAUSED
&& (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID))
|| finishingActivityInNonFocusedStack
|| prevState == ActivityState.STOPPED
|| prevState == ActivityState.INITIALIZING) {
...
// 销毁Activity
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");

...
return activityRemoved ? null : r;
}

...
return r;
}

这里是开始销毁Activity,首先要保存状态,然后标记开始finish,然后调用destroyActivityLocked方法开始销毁。

Step11.ActivityStack.destroyActivityLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
...

// 清理Activity
cleanUpActivityLocked(r, false, false);

final boolean hadApp = r.app != null;

if (hadApp) {
...

boolean skipDestroy = false;

try {
r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
r.configChangeFlags);
} catch (Exception e) {
...
}

...

return removedFromHistory;
}

这里主要有两部操作,第一步是cleanUpActivityLocked,第二步是scheduleDestroyActivity。我们先看第一步。

Step12.ActivityStack.cleanUpActivityLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices,
boolean setState) {
if (mResumedActivity == r) {
mResumedActivity = null;
}
if (mPausingActivity == r) {
mPausingActivity = null;
}
mService.resetFocusedActivityIfNeededLocked(r);

...

// Make sure this record is no longer in the pending finishes list.
// This could happen, for example, if we are trimming activities
// down to the max limit while they are still waiting to finish.
mStackSupervisor.mFinishingActivities.remove(r);
mStackSupervisor.mWaitingVisibleActivities.remove(r);

...

if (cleanServices) {// 是否清理服务
cleanUpActivityServicesLocked(r);
}

...
}

这里主要是将指向该销毁的Activity的引用都置空,进行释放,然后将该Activity描述对象从列表中移除该对象,最后是判断是否清理服务,其实主要是绑定该Activity的服务,如果有绑定则解除绑定。

Step13.ActivityManagerService.resetFocusedActivityIfNeededLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
// 重新设置获取焦点的Activity,如果被清理的不是获取焦点的,那么不处理,否则将焦点移动到另外一个Activity
final void resetFocusedActivityIfNeededLocked(ActivityRecord goingAway) {
...

// Try to move focus to another activity if possible.
// 如果允许,将焦点移动到另外一个Activity
if (setFocusedActivityLocked(
focusedStack.topRunningActivityLocked(), "resetFocusedActivityIfNeeded")) {
return;
}

...
}

这里调用setFocusedActivityLocked方法判断该Activity是不是允许允许将焦点移除。

Step14.ActivityManagerService.setFocusedActivityLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
boolean setFocusedActivityLocked(ActivityRecord r, String reason) {
if (r == null || mFocusedActivity == r) {// 如果传入的焦点Activity不存在或者就是显示在有焦点的Activity则不需要处理了
return false;
}

if (!r.isFocusable()) {// 如果没有获取焦点,返回
return false;
}

...

// 获取之前有焦点的Activity
final ActivityRecord last = mFocusedActivity;
mFocusedActivity = r;// 设置传入的Activity为当前焦点Activity
if (r.task.isApplicationTask()) {// 当前焦点Activity所在任务是应用任务(不是桌面和最近应用界面)
if (mCurAppTimeTracker != r.appTimeTracker) {
...
} else {
startTimeTrackingFocusedActivityLocked();
}
} else {
r.appTimeTracker = null;
}
...
// 将要启动的Activity移动到前台
if (mStackSupervisor.moveActivityStackToFront(r, reason + " setFocusedActivity")) {
mWindowManager.setFocusedApp(r.appToken, true);
}
...
return true;
}

这里看一下注释,主要是判断该Activity是否有焦点,是否允许切换焦点。因此要结束的Activity就要把焦点释放掉。

Step15.ActivityStack.cleanUpActivityServicesLocked

1
2
3
4
5
6
7
8
9
10
11
final void cleanUpActivityServicesLocked(ActivityRecord r) {
// Throw away any services that have been bound by this activity.
if (r.connections != null) {
Iterator<ConnectionRecord> it = r.connections.iterator();
while (it.hasNext()) {
ConnectionRecord c = it.next();
mService.mServices.removeConnectionLocked(c, null, r);
}
r.connections = null;
}
}

上面我们提到这个方法主要是释放绑定的服务,如果没有则不需要释放。下面我们回到Step11中提到的第二步。

Step16.ApplicationThreadNative.scheduleDestroyActivity

1
2
3
4
5
6
7
public final void scheduleDestroyActivity(IBinder token, boolean finishing,
int configChanges) throws RemoteException {
...
mRemote.transact(SCHEDULE_FINISH_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
..
}

这个很熟悉了。

Step17.ApplicationThread.scheduleDestroyActivity

1
2
3
4
5
public final void scheduleDestroyActivity(IBinder token, boolean finishing,
int configChanges) {
sendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,
configChanges);
}

通过发送消息到Handler中来处理。

Step18.ActivityThread.H.handleMessage

1
2
3
4
5
6
7
8
9
10
public void handleMessage(Message msg) {
switch (msg.what) {
...
case DESTROY_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
handleDestroyActivity((IBinder) msg.obj, msg.arg1 != 0,
msg.arg2, false);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...

Step19.ActivityThread.handleDestroyActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void handleDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance);
...
if (finishing) {
try {
ActivityManagerNative.getDefault().activityDestroyed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
mSomeActivitiesChanged = true;
}

Step20.ActivityThread.performDestroyActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null) {
...

performPauseActivityIfNeeded(r, "destroy");

...

try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnDestroy(r.activity);
...
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
...
}
}
...
return r;
}

执行performPauseActivityIfNeeded,然后执行Instrumentation.callActivityOnDestroy方法结束。

Step21.ActivityThread.performPauseActivityIfNeeded

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
if (r.paused) {
// You are already paused silly...
return;
}

try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
...
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
...
}
r.paused = true;
}

如果已经paused了,则直接返回,否则调用Instrumentation.callActivityOnPause方法。

Step22.Instrumentation.callActivityOnPause

1
2
3
public void callActivityOnPause(Activity activity) {
activity.performPause();
}

Step23.Activity.performPause

1
2
3
4
5
final void performPause() {
..
onPause();
...
}

Step24.Activity.onPause

1
2
3
4
5
protected void onPause() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
getApplication().dispatchActivityPaused(this);
mCalled = true;
}

这里就是调用了Activity的onPause方法了。

Step25.Activity.performStop

1
2
3
4
5
6
7
8
9
10
11
12
final void performStop(boolean preserveWindow) {
...

if (!mStopped) {
...

mCalled = false;
mInstrumentation.callActivityOnStop(this);
...
}
mResumed = false;
}

这里调用Instrumentation.callActivityOnStop方法,这里本来还有Fragment的处理,我略掉了,自己看看。

Step26.Instrumentation.callActivityOnStop

1
2
3
public void callActivityOnStop(Activity activity) {
activity.onStop();
}

这里调用了Activity的onStop方法。代码略了。

Step28.Instrumentation.callActivityOnDestroy

1
2
3
4
5
6
7
 public void callActivityOnDestroy(Activity activity) {
...

activity.performDestroy();

...
}

调用Activity的performDestroy方法

Step29.Activity.performDestroy

1
2
3
4
5
final void performDestroy() {
...
onDestroy();
...
}

这里终于看到了onDestroy方法。这里还有Fragment的处理,我没有显示出来,自己看看。我们再回到Step20中的第二个方法中。

Step31.AMP.activityDestroyed

1
2
3
4
5
6
7
8
9
10
public void activityDestroyed(IBinder token) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
mRemote.transact(ACTIVITY_DESTROYED_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
reply.readException();
data.recycle();
reply.recycle();
}

然后调用AMS中的activityDestroyed方法。

Step32.AMS.activityDestroyed

1
2
3
4
5
6
7
8
public final void activityDestroyed(IBinder token) {
synchronized (this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
stack.activityDestroyedLocked(token, "activityDestroyed");
}
}
}

Step33.ActivityStack.activityDestroyedLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
final void activityDestroyedLocked(IBinder token, String reason) {
final long origId = Binder.clearCallingIdentity();
try {
ActivityRecord r = ActivityRecord.forTokenLocked(token);
...

if (isInStackLocked(r) != null) {
if (r.state == ActivityState.DESTROYING) {
cleanUpActivityLocked(r, true, false);
removeActivityFromHistoryLocked(r, null, reason);
}
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
} finally {
Binder.restoreCallingIdentity(origId);
}
}

这里主要是两步,第一个是调用cleanUpActivityLocked方法,清理Activity,前面Step12我们分析过了,不再分析。然后是调用removeActivityFromHistoryLocked方法,就是将该结束的Activity从历史列表中删除.

Step35.ActivityStack.removeActivityFromHistoryLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 从历史记录移除
private void removeActivityFromHistoryLocked(
ActivityRecord r, TaskRecord oldTop, String reason) {
mStackSupervisor.removeChildActivityContainers(r);
finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null);
...
if (task != null && task.removeActivity(r)) {
if (mStackSupervisor.isFocusedStack(this) && task == topTask &&
task.isOverHomeStack()) {
mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), reason);
}
removeTask(task, reason);
}
cleanUpActivityServicesLocked(r);
r.removeUriPermissionsLocked();
}

调用finishActivityResultsLocked方法处理Result,前面提到了。调用ActivityStackSupervisor.moveHomeStackTaskToTop将Home属性的Activity所在任务栈放到顶部。调用cleanUpActivityServicesLocked清理绑定的服务。

Step39.ActivityStackSupervisor.resumeFocusedStackTopActivityLocked

1
2
3
boolean resumeFocusedStackTopActivityLocked() {
return resumeFocusedStackTopActivityLocked(null, null, null);
}

这里我们在上一章Activity的启动时已经分析过了。这里不再分析。到这里Activity的finish就分析完了,从这里我们主要学到的是finish的过程,以及finish过程中系统做了哪些处理,我们在写代码过程中应该做那些处理。

流程图:

Activity启动时序图

代码地址:

直接拉取导入开发工具(Intellij idea或者Android studio)

Android_Framework_Source

Android开发群:192508518

微信公众账号:Code-MX

注:本文原创,转载请注明出处,多谢。