前言
从点击应用图标到View显示出来主要经历了哪些流程?第一:Activity启动;第二:View经过测量、布局、绘制之后显示出来;
该文主要讲述:
(1)Activity启动流程:主要讲述从Launcher桌面点击应用图标之后,框架如何去启动该应用的Activity,以及Activity如何一步步执行onCreate、onStart、onResume;
(2)View测量布局绘制流程都是从根View(DecorView)开始的,DecorView究竟在那里创建?在Activity.onCreate生命周期中setContentView()主要做了哪些工作;以及Activity中View的整体布局层次;从点击App图标到View显示出来主要流程(二)
(3)Activity启动之后,如何去开启View的测量、布局、绘制工作,View如何进行测量、布局、绘制工作;[从点击App图标到View显示出来主要流程(三)]
Activity启动
场景描述:从手机桌面Launcher点击一个图标去启动新应用的MainActivity,Launcher和MainActivity生命周期执行顺序为:
Launcher.onpause;//首先是Launcher的onPause,上一个应用执行onPause之后才会去启动下一个应用;
MainActivity.oncreate;
MainActivity.onstart;
ActivityThread.handleResumeActivity()
MainActivity.onresume;
//到此MainActivity已经处于前台,然后开始测量、布局绘制工作;
//以下流程是Launcher生命周期执行onStop操作
Looper.myQueue().addIdleHandler(new Idler())
Looper.myQueue().addIdleHandler(new Idler())
Idler.queueIdle()
ActivityManagerNative.getDefault().activityIdle()
ActivityManagerService.activityIdle()
ActivityStackSupervisor.activityIdleInternalLocked()
ActivityStack.stopActivityLocked()
IApplicationThread.scheduleStopActivity()
ActivityThread.scheduleStopActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage()
ActivityThread.H.handleMessage()
ActivityThread.handleStopActivity()
ActivityThread.performStopActivityInner()
ActivityThread.callCallActivityOnSaveInstanceState()
Instrumentation.callActivityOnSaveInstanceState()
Activity.performSaveInstanceState()
Activity.onSaveInstanceState()
Activity.performStop()
Instrumentation.callActivityOnStop()
Launcher.onStop() //从Launcher启动新Activity时,这样前后两个Activity正常生命周期执行完成;
说明:
(1)可以看到启动新Activity时会先执行上一个Activity.onPause操作,执行完成之后,才会去启动新的Activity,所以在onPause中不能执行耗时操作,不然会影响其它Activity启动速度;
(2)在新启动的Activity执行onResume之后,才会执行Activity.makeVisible();在makeVisible中执行wm.addView(mDecor, getWindow().getAttributes());然后才会执行View的测量、布局、绘制工作;所以在onResume执行耗时操作会影响该Activity自身View的显示速度;
下边结合源码开始一步步分析MainActivity启动过程以及Launcher生命周期的执行流程;
1.从Launcher桌面点击一个图标去启动应用会执行Activity.startActivity()方法,但它们最终都会调用Activity.startActivityForResult方法
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
// startActivity方法有好几种重载方式,但它们最终都会调用startActivityForResult方法
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
/*每一个应用程序只有一个Instrumentation对象,每个Activity内都有一个对该对象的引用。
Instrumentation可以理解为应用进程的管家,ActivityThread要创建或暂停某个Activity时,
都需要通过Instrumentation来进行具体的操作。对Activity生命周期方法的调用根本就离不开Instrumentation
mMainThread实际上是ActivityThread对象。在Activity.attach时初始化。
mMainThread.getApplicationThread() 就是ActivityThread里面的ApplicationThread成员变量。
ActivityThread,就是主线程(UI线程),它是在App启动时创建的,它代表了App应用程序。*/
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
}
}
2.执行Instrumentation.execStartActivity()方法
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
try {
/*
App与AMS通过Binder进行IPC通信,AMS(SystemServer进程)与zygote通过Socket进行IPC通信。
App和AMS(SystemServer进程)还有zygote进程分属于三个独立的进程
systemserver和zygote是Android Framework里面两大非常重要的进程。
客户端:ActivityManagerProxy(app应用) =====>Binder驱动=====> ActivityManagerService:服务器(systemserver)
客户端:ApplicationThread (app应用)<=====Binder驱动<===== ApplicationThreadProxy:服务器(systemserver)
ActivityManager.getService()是应用持有的AMS代理,通过Binder通信可以调用到AMS中的startActivity方法;
who.getBasePackageName():启动其它应用时,会将自己的包名传递给AMS,这样AMS知道是谁启动的该Activity;
*/
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
// 检查启动Activity的结果(抛出异常,例如清单文件未注册Activity)
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
3.通过Binder跨进程通信执行AMS.startActivity();最终会执行到ActivityStackResumeTopActivityInnerLocked方法中;
ActivityManagerService.startActivity
ActivityManagerService.startActivityAsUser
ActivityStarter.startActivityMayWait
ActivityStarter.startActivityLocked
ActivityStarter.startActivity
ActivityStarter.startActivity
ActivityStarter.startActivityUnchecked
ActivityStackSupervisor.resumeFocusedStackTopActivityLocked
ActivityStack.resumeTopActivityUncheckedLocked
ActivityStack.resumeTopActivityInnerLocked
3.1 ActivityStarter.startActivityMayWait方法如下:
final int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, WaitResult outResult,
Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
TaskRecord inTask, String reason) {
// 利用PK\MS解析满足Intent等参数要求的信息
// android中是通过Intent来启动一个新的activity的,因此AMS在得到请求启动activity时,
// 首先需要根据Intent从PMS中获得要启动的activity,PMS通过parse每个application的AndroidManifest.xml
// 来获得所有的activity信息。针对每个Intent提供的信息,PMS会提供给AMS一个ResolveInfo对象。
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
if (rInfo == null) {
...
}
// Collect information about the target of the Intent.
// 1.对参数intent的内容进行解析,得到MainActivity的相关信息,保存在aInfo变量中:
// mSupervisor根据intent从ResolveInfo中找到合适的应用的activityInfo.
// 如果有多个activity可选择,则会弹出ResolverActivity让用户选择合适的应用
// mSupervisor(ActivityStackSupervisor)负责管理Activity和对应stack之间的关系
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
...
// 调用startActivityLocked方法(核心方法),进行实际的启动工作。
int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
aInfo, rInfo, voiceSession, voiceInteractor,
resultTo, resultWho, requestCode, callingPid,
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
options, ignoreTargetSecurity, componentSpecified, outRecord, inTask,
reason);
...
}
3.2 ActivityStarter.startActivityUnchecked()方法如下:
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity) {
...
/*确定新启动的Activity是否可以复用现有的Activity。返回null表示否
Activity有三种类型:application、home、recents,保存在ActivityRecord;
判断Acitivity是否可以复用时,会判断Activity的类型,如何Activity存在
但是待启动Activity的类型和已经已启动Activity类型不一致,也是不能复用旧
的Activity,这样就可能导致Activity双示例
*/
ActivityRecord reusedActivity = getReusableIntentActivity();
// reusedActivity不为空,则意味着找到了目标Activity,也说明其之前已经启动过,只需要把其重新挪到前台显示即可。
// 目标Activity所在的任务(TaskRecord)和任务所在的栈(ActivityStack)就是宿主任务和宿主栈。
// 宿主栈之前可能在前台(Foreground)也可能在后台(Background),如果在后台,则需要将宿主栈挪动前台。
if (reusedActivity != null) {
if (!mAddingToTask && mReuseTask == null) {
// We didn't do anything... but it was needed (a.k.a., client don't use that
// intent!) And for paranoia, make sure we have correctly resumed the top activity.
/*如果要启动Activity已经存在,该方法会调用ActivityStackSupervisor.resumeFocusedStackTopActivityLocked
将该Acitivity挪到前台显示,
*/
resumeTargetStackIfNeeded();
}
}
// mDoResume表示是否需要立即显示Activity。有些需要Pending的Activity,该值为false
if (mDoResume) {
final ActivityRecord topTaskActivity =
mStartActivity.getTask().topRunningActivityLocked();
// 最终将Activity挪到显示状态。到这里ActivityStarter的使命基本就结束了
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);
}
...
return START_SUCCESS;
}
4.进入关键方法ActivityStack.resumeTopActivityInnerLocked(),该方法会先找到当前处于Resume的Activity,并执行该Activity的onPause操作,然后才会去启动新的Activity
/**
* 1.如果mResumedActivity != null,则需要先暂停这个Activity。即执行caller activity的onPause。
* mResumedActivity代表当前已经存在于界面的Activity。然后返回;因为启动MainAcitivity的是
Launcher,并且Launcher是resume状态,所以需要先暂停Launcher;
* 2.当mResumedActivity == null,若待启动的Activity对应的进程存在,那么仅需要重新启动该Activity;
* 否则,需要调用ActivityStackSupervisor的startSpecificActivityLocked函数,启动整个进程;
*/
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
//获取当前获的焦点的Activity,也可以理解为当前处于Resume状态的Activity
final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
// 表示可以在当前显示的Activity执行Pausing时,同时进行Resume操作。
// 变量resumeWhilePausing的取意就是需不需要等到Activity执行Pause完毕。
//该值默认为false;
final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0
&& !lastResumedCanPip;
/*
mStackSupervisor.pauseBackStacks方法会遍历所有Stack中的mResumedActivity。
然后调用stack.startPausingLocked()暂停该Activity,通过Binder执行app.thread.schedulePauseActivity方法,
调用到ActivityThread.handlePauseActivity方法,然后调用activity.onpause方法,
然后通知AMS自己已经onpause;然后调用AMS.activityPaused方法。
AMS.activityPaused最终还会resumeTopActivityInnerLocked方法执行后续启动Activity流程,
从该流程可以看出上一个Acitivty不执行onPause是不是去启动新的Activity
*/
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
/*上一个Acitivity(Launcher)成功onPause之后,pausing值为true,
resumeWhilePausing默认为false,这时会进入if中返回,那是不是新的Activity就无法启动了,
实际不是,在Launcher.onPause之后,会通知AMS自己已经onpause;然后调用AMS.activityPaused方法。
AMS.activityPaused最终还会走到该方法中,由于条件不符合,会执行resumeTopActivityInnerLocked
后续的流程;
*/
if (pausing && !resumeWhilePausing) {
return true;
}
ActivityStack lastStack = mStackSupervisor.getLastStack();
/*next.app是ProcessRecord,是ActivityRecord的宿主进程
next.app.thread是IApplicationThread类型的对象,应用启动之后,SystenServer会保存
应用成的Binder代理IApplicationThread,该代理的服务端是ActivityThread.ApplicationThread;
SystemServer通过IApplicationThread去回调应用Activity、Service生命周期以及其他工作,SystemServer
需要和应用端进行通信都是通过IApplicationThread来完成的;next.app != null && next.app.thread != null
说明该应用已经启动;
*/
if (next.app != null && next.app.thread != null) {
// 待启动Activity的宿主进程已经存在了,即热启动
// 这里直接调用了scheduleResumeActivity,会调用onStart和onResume。此即热启动
next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
mService.isNextTransitionForward(), resumeAnimOptions);
} else {
//启动Activity,需要创建进程,即冷(温)启动
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
return true;
}
冷启动:该Acitivity的进程还没有启动;
温启动:该Activity所在进程已经启动,但是该Activity还未启动;
热启动:该Activity所在进程已经启动,该Activity也已经启动,但是目前不是resume状态;
5.执行ActivityStackSupervisor.startSpecificActivityLocked()
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
// 通过进程名和uid获取ProcessRecord
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
// 如果对应进程已经存在,并向AMS注册过
if (app != null && app.thread != null) {
try {
//通知进程启动Activity,执行onCreate等完整生命周期。此即温启动
realStartActivityLocked(r, app, andResume, checkConfig);
return;
}
}
// 如果进程不存在,利用AMS的startProcessLocked函数,通过zygote创建一个新的进程,即冷启动
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
6.以温启动为例进行说明,继续执行ActivityStackSupervisor.realStartActivityLocked()方法,此时会真正启动通过real可以看出来,这是要真正的启动Activity;
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
try {
//将ProcessRecord和ActivityRecord关联
r.app = app;
try {
/* 所有参数信息到位后准备启动Activity。通知应用进程启动Activity。
r.appToken表示ActivityRecord的Token,app.thread是ActivityThread.ApplicationThread
在SystemServer中的代理,SystemServer通过app.thread来与应用进程进行通信,
会调用ActivityThread.ApplicationThread.scheduleLaunchActivity()方法
至此,Acitivty的启动由SystemServer进入应用程序内部
*/
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, !andResume,
mService.isNextTransitionForward(), profilerInfo);
} catch (RemoteException e) {
}
} finally {
endDeferResume();
}
return true;
}
7.ActivityThread.ApplicationThread.scheduleLaunchActivity()会发送H.LAUNCH_ACTIVITY消息,ApplicationThread的工作其实就是与AMS通信,该消息实际还是有ActivityThread进行处理,H继承Handler,ActivityThread中大量事件处理依赖此Handler。Handler收到H.LAUNCH_ACTIVITY消息之后,会调用handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
/**
* 这里包含了ActivityThread中启动Activity的几乎全部逻辑。其主要职能就是触发Activity生命周期的开始。
* 有两个重要的方法:performLaunchActivity和handleResumeActivity。
*/
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
/*调用performLaunchActivity(),由Instrumentation通过类加载器新建Activity,
利用了反射机制。此时才真正有了用户启动的Activity对象。*/
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
// 如果初始化成功,便调用handleResumeActivity()来处理Activity进入显示状态时需要完成的操作
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
}
}
7.1 ActivityThread.performLaunchActivity方法:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 1.从ActivityClientRecord中获取待启动的Activity的组件信息ActivityInfo
ActivityInfo aInfo = r.activityInfo;
if (r.loadedApk == null) {
r.loadedApk = getLoadedApk(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
// 先要为将要启动的Activity创建Context。
ContextImpl appContext = createBaseContextForActivity(r);
// 2.通过Instrumentation的newActivity方法使用类加载器创建Activity对象
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// 通过类加载器新建Activity,利用了反射机制。此时才真正有了用户启动的Activity对象。
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
}
try {
// 3.通过LoadedApk的makeApplication方法尝试创建Application对象
Application app = r.loadedApk.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.loadedApk.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.loadedApk.getAppDir());
if (activity != null) {
appContext.setOuterContext(activity);
// 4.通过attach方法将Application对象、Context对象绑定到Activity中,并完成一些重要数据的初始化
// 从ActivityRecord取出token,即r.token,作为参数传递给attach,将其绑定到Activity上
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
// 设置主题
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
// 5.调用Activity的onCreate函数。isPersistable()方法判断该Activity是否是Persistable的。
// Persistable的应用在destroy或kill掉后会马上重启。
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
r.activity = activity;
r.stopped = true;
// 调用Activity的onStart函数
if (!r.activity.mFinished) {
activity.performStart();
//在onResume的时候,会判断Activity是否是stop状态,以决定是否执行onRestart
r.stopped = false;
}
}
// 将activity保存到进程中
mActivities.put(r.token, r);
}
return activity;
}
在该方法中创建了Activity实例,并调用了Activity.onCreate、onStart方法,在handleResumeActivity方法会调用Activity.onResume方法;
7.2 执行AcitivtyThread.handleResumeActivity()方法
在该方法中会调用performResumeActivity(),然后调用Activity.performResume()方法,在该方法中通过Instrumentation调用Activity.onResume方法
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
/*在该方法中会调用performResumeActivity(),然后调用Activity.performResume()方法,
在该方法中通过Instrumentation调用Activity.onResume方法*/
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.activity.mVisibleFromClient) {
/* 调用Activity.makeVisible(),其中会调用wm.addView(mDecor, getWindow().getAttributes());
会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联,
ViewRootImpl是DecorView的父元素,但是ViewRootImpl并不是View。
在调用wm.addView(mDecor, getWindow().getAttributes())方法之后,
后续就会开启View的绘制流程,通过代码可以看到View的绘制流程开启是在
Activity.onResume之后,所以onResume执行耗时操作会影响View的显示速度*/
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
// r为刚刚完成onResume的新Activity,mNewActivities保存已经完成onResume的Activity。
// 新的Activity的nextIdle指向旧的
r.nextIdle = mNewActivities;
// 将新建的Activity保存到mNewActivities中
mNewActivities = r;
if (localLOGV) Slog.v(
TAG, "Scheduling idle handler for " + r);
// 向ActivityThread的MessageQueue中增加一个Idler()。
// 当MessageQueue调用next时,如果队列中没有其他的message需要处理,
// 那么将调用添加到MessageQueue的IdleHandler的queueIdle接口。
// 执行Looper消息,开始前Activity的onStop。
Looper.myQueue().addIdleHandler(new Idler());
}
if (reallyResume) {
try {
// 通知AMS Activity进入Resume状态。AMS会修改对应的存储信息
ActivityManager.getService().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
}
其中有两个关键点:
(1)调用r.activity.makeVisible();其中会调用wm.addView(mDecor, getWindow().getAttributes());会将DecorView添加到Window中,该方法最终会启动View的绘制流程,可以看出启动绘制流程是在onResume生命周期之后,所以onResume执行耗时操作会影响View的显示速度;View的绘制流程后边会讲到;
(2)Looper.myQueue().addIdleHandler(new Idler());在该方法中最终会调用上一个Activity的onStop方法;这样两个Activity正常生命周期就执行完毕;调用前一个Activity.onStop方法的流程如下:
Looper.myQueue().addIdleHandler(new Idler())
Idler.queueIdle()
ActivityManagerNative.getDefault().activityIdle()
ActivityManagerService.activityIdle()
ActivityStackSupervisor.activityIdleInternalLocked()
ActivityStack.stopActivityLocked()
IApplicationThread.scheduleStopActivity()
ActivityThread.scheduleStopActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage()
ActivityThread.H.handleMessage()
ActivityThread.handleStopActivity()
ActivityThread.performStopActivityInner()
ActivityThread.callCallActivityOnSaveInstanceState()
Instrumentation.callActivityOnSaveInstanceState()
Activity.performSaveInstanceState()
Activity.onSaveInstanceState()
Activity.performStop()
Instrumentation.callActivityOnStop()
Activity.onStop()
Activity启动总结
(1)在Activity启动的时候首先会暂停上一个Activity,即执行Activity.onPause;
(2)Binder通信是单向的,SystemServer通过ActivityThread.ApplicationThread的代理来与应用程序进行通信,应用进程通过AMS的代理与SystemServer进行通信;这样就完成了应用进程和SystemServer之间的双向通信,
(3)在AcitivtyThread.handleResumeActivity会先调用Acitivty.onResume方法,然后调用Activity.makeVisible()和Looper.myQueue().addIdleHandler(new Idler()) ;
(4)Activity.makeVisible启动View的绘制工作;因为onResume执行完之后才会执行makeVisible,所以onResume不能执行耗时操作,不然会影响View的显示速度;
(5)Looper.myQueue().addIdleHandler(new Idler()) ;最终会调用上一个Activity的onStop方法;
网友评论