android 单例 生命周期,Android-生命周期和启动模式

Android的生命周期

典型情况下的生命周期

e7314b9a6744

如上图所示,正常情况下,Activity会经历以下的生命周期

1. onCreate : 与onDestroy配对,表示Activity正在被创建,这是生命周期的第一个方法。在这个方法中可以做一些初始化的工作(加载布局资源、初始化Activity所需要的数据等),耗时的工作在异步线程上完成。

2. onRestart : 表示Activity正在重新启动。一般情况下,在当前Activity从不可见重新变为可见的状态时onRestart就会被调用。这种情形一般是由于用户的行为所导致的,比如用户按下Home键切换到桌面或者打开了一个新的Activity(这时当前Activity会暂停,也就是onPause和onStop被执行),接着用户有回到了这个Activity,就会出现这种情况。

3. onStart : 与onStop配对,表示Activity正在被启动,并且即将开始。但是这个时候要注意它与onResume的区别。两者都表示Activity可见,但是onStart时Activity还正在加载其他内容,正在向我们展示,用户还无法看到,即无法交互。

4. onResume : 与onPause配对,表示Activity已经创建完成,并且可以开始活动了,这个时候用户已经可以看到界面了,并且即将与用户交互(完成该周期之后便可以响应用户的交互事件了)。

5. onPause : 与onResume配对,表示Activity正在暂停,正常情况下,onStop接着就会被调用。在特殊情况下,如果这个时候用户快速地再回到当前的Activity,那么onResume会被调用(极端情况)。一般来说,在这个生命周期状态下,可以做一些存储数据、停止动画的工作,但是不能太耗时,如果是由于启动新的Activity而唤醒的该状态,那会影响到新Activity的显示,原因是onPause必须执行完,新的Activity的onResume才会执行。

6. onStop : 与onStart配对,表示Activity即将停止,可以做一些稍微重量级的回收工作,同样也不能太耗时(可以比onPause稍微好一点)。

7. onDestory : 与onCreate配对,表示Activity即将被销毁,这是Activity生命周期的最后一个回调,我们可以做一些回收工作和最终的资源释放。

这里提出两个问题:

1.onStart和onResume、onPause和onStop从描述上来看差不多,对我们来说有什么实质上的不同呢?

2.假设当前Activity为A,如果这时用户打开了一个新的活动B,那么B的onResume和A的onPause谁先执行呢?

e7314b9a6744

别看妹子啦,先看第一个问题,从实际的使用过程中来说,onStart和onResume、onPause和onStop看起来确实差不多。但Android为什么要提供看似重复的接口呢?根据上面的分析我们可以知道,这两对回调具有不同的意义,onStart和onStop是根据应用是否可见来进行回调的 ,onResume和onPause是根据应用是否位于前台来进行回调的,除此之外,无其他明显区别。

第二个问题,我们可以从Android源码里得到解释,A的onPause执行后B的onResume才会被调用。从另一个角度来说Android的官方文档中对onPause有这样一句解释:不能在onPause里进行重量级操作,因为必须在onPause执行过后,新的Activity才能Resume。

异常情况下的生命周期

e7314b9a6744

onSaveInstanceState方法只会出现在 Activity被异常终止的情况下,它的调用时机是在 onStop之前,它和onPause方法没有既定的时序关系,可能在它之前,也可能在它之后。当 Activity被重新创建的时候, onRestoreInstanceState会被回调,它的调用时机是 onStart之后。系统只会在 Activity即将被销毁并且有机会重新显示的情况下才会去调用 onSaveInstanceState方法。当 Activity在异常情况下需要重新创建时,系统会默认为我们保存当前 Activity的视图结构,并且在 Activity重启后为我们恢复这些数据,比如文本框中用户输入的数据、listview滚动的位置等,这些 view相关的状态系统都会默认为我们恢复。具体针对某一个 view系统能为我们恢复哪些数据可以查看 view的源码中的onSaveInstanceState和 onRestoreInstanceState方法。

Demo

@Override

protected void onSaveInstanceState(Bundle outState) {

super.onSaveInstanceState(outState);

KLog.d(getClass().getSimpleName(),"onSaveInstanceState");

outState.putString(STATE, "test");

}

@Override

protected void onRestoreInstanceState(Bundle savedInstanceState) {

super.onRestoreInstanceState(savedInstanceState);

KLog.d(getClass().getSimpleName(),"[onRestoreInstanceState]: "

+ savedInstanceState.getString(STATE));

}

当我们旋转屏幕过后,可以看到如下的log:

10-23 22:50:56.032 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onCreate

10-23 22:50:56.036 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onStart

10-23 22:50:56.040 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onResume

10-23 22:51:05.456 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onPause

10-23 22:51:05.460 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onSaveInstanceState

10-23 22:51:05.460 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onStop

10-23 22:51:05.460 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onDestroy

10-23 22:51:05.484 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onCreate

10-23 22:51:05.496 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onStart

10-23 22:51:05.496 1613-1613/com.hugo.demo.activitydemo D/MainActivity: [onRestoreInstanceState]: test

10-23 22:51:05.496 1613-1613/com.hugo.demo.activitydemo D/MainActivity: onResume

在onSaveInstanceState里面保存的test,确实在重新创建活动的时候在onRestoreInstanceState被还原出来了。

Android开发艺术探索上有这样一句话:

关于保存和恢复 View 的层次结构,系统工作流程是: Activity 异常终止, Activity 调用 onSaveInstanceState 去保存数据,然后 Activity 会委托 Windows 去保存数据,接着 Window 再委托它上面的顶层容器去保存数据。顶层容器是一个 ViewGroup ,一般来说它很可能是 DectorView ,最后顶层容器再去通知它的子元素保存数据。(这是一种委托思想,上层委托下层,父容器委托子元素去处理事情,如 View 的绘制过程,事件分发都是采用类似的思想)

Fragment的生命周期

普通的Fragment

e7314b9a6744

e7314b9a6744

Activity的生命周期 和 Fragment的生命周期的对比

从图中可以看出fragment的生命周期大部分和activity一致,不同的是:

onAttach() :当Fragment 和 Activity产生关联时调用.

onCreateView():为Fragment加载布局的时候调用

onActivityCreated():当 Activity 的 onCreated() 方法返回后调用此方法

onDestroyView():当 Fragment 中的视图被移除的时候,调用这个方法。

onDetach():当 Fragment 和 Activity 分离的时候,调用这个方法。

除了上面的这些回调函数,fragment还有三个回调是值得我们注意的:

onViewCreated():它在onCreateView()执行之后立马被调用,但它执行的时候,之前保存的状态还未被被恢复到了视图

onSaveInstanceState():可在此保存fragment的一些数据,与activity不同的是,它可能在onDestroy之前的任何时间调用

onViewStateRestored():在onActivityCreated()之后,onStart()之前被调用,以前在onSaveInstanceState()里保存的数据可以在这里还原。

ViewPager 中的 Fragment

我们开发中经常会用到 ViewPager + Fragment 组合的形式来完成特定的需求。本身 Fragment 生命周期就比 Activity 要复杂很多,当它在 ViewPager 中又是怎么回调呢?

我先给 ViewPager 加入三个 Fragment:

viewPager = (ViewPager) findViewById(R.id.viewpager);

fragmentList.add(new OneTextFragment());

fragmentList.add(new TwoTextFragment());

fragmentList.add(new ThreeTextFragment());

viewPager.setAdapter(new FtAdapter(getSupportFragmentManager(), fragmentList));

启动这个activity后,出现如下log:

10-23 23:17:19.332 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onCreate

10-23 23:17:19.388 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onStart

10-23 23:17:19.392 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onResume

10-23 23:17:19.404 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onAttach

10-23 23:17:19.404 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onCreate

10-23 23:17:19.408 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onActivityCreated

10-23 23:17:19.408 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onStart

10-23 23:17:19.412 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onResume

10-23 23:17:19.412 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onAttach

10-23 23:17:19.412 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onCreate

10-23 23:17:19.412 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onActivityCreated

10-23 23:17:19.416 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onStart

10-23 23:17:19.416 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onResume

当活动进入到后台的时候:

10-23 23:19:37.536 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onPause

10-23 23:19:38.308 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onSaveInstanceState

10-23 23:19:38.308 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onStop

10-23 23:19:38.312 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onStop

10-23 23:19:38.312 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onStop

当活动返回前台的时候:

10-23 23:28:19.568 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onRestart

10-23 23:28:19.568 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onStart

10-23 23:28:19.568 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onStart

10-23 23:28:19.572 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onStart

10-23 23:28:19.572 1613-1613/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onResume

10-23 23:28:19.576 1613-1613/com.hugo.demo.activitydemo D/OneTextFragment: onResume

10-23 23:28:19.576 1613-1613/com.hugo.demo.activitydemo D/TwoTextFragment: onResume

当我滑动一页的时候:

10-23 23:30:26.560 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onAttach

10-23 23:30:26.564 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onCreate

10-23 23:30:26.568 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onActivityCreated

10-23 23:30:26.568 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onStart

10-23 23:30:26.572 30297-30297/com.hugo.demo.activitydemo D/ThreeTextFragment: onResume

可以看到在fragment进行滑动切换的时候,对下一个fragment进行了预加载。

再滑动一页:

10-23 23:32:29.612 30297-30297/com.hugo.demo.activitydemo D/OneTextFragment: onStop

10-23 23:32:29.612 30297-30297/com.hugo.demo.activitydemo D/OneTextFragment: onDestroyView

这个时候第一个fragment已经被销毁了。

当我们增加一行代码:viewPager.setOffscreenPageLimit(int limit)

10-23 23:38:47.236 4139-4139/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onCreate

10-23 23:38:47.272 4139-4139/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onStart

10-23 23:38:47.276 4139-4139/com.hugo.demo.activitydemo D/ViewPagerHostActivity: onResume

10-23 23:38:47.280 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onAttach

10-23 23:38:47.284 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onCreate

10-23 23:38:47.284 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onActivityCreated

10-23 23:38:47.288 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onStart

10-23 23:38:47.288 4139-4139/com.hugo.demo.activitydemo D/OneTextFragment: onResume

10-23 23:38:47.292 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onAttach

10-23 23:38:47.292 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onCreate

10-23 23:38:47.292 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onActivityCreated

10-23 23:38:47.296 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onAttach

10-23 23:38:47.296 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onCreate

10-23 23:38:47.300 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onActivityCreated

10-23 23:38:47.300 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onStart

10-23 23:38:47.304 4139-4139/com.hugo.demo.activitydemo D/TwoTextFragment: onResume

10-23 23:38:47.304 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onStart

10-23 23:38:47.308 4139-4139/com.hugo.demo.activitydemo D/ThreeTextFragment: onResume

可以看到除了本来要显示的那个fragment外,还有其的2个fragment也被创建了出来。所以说我们通过viewPager.setOffscreenPageLimit(int limit)可以设置缓存fragment的个数。

Android的启动模式

Activity 的四种启动模式

standard:标准模式,每次启动一个Activity都会产生一个新的实例,不论这个实例是否存在。

singleTop: 栈顶复用模式,在这种情况下,如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被调用,通过此方法我们可以取出当前请求的信息。

singleTask:栈内复用模式,这是一种单例模式,在这种模式下,只要有一个activity在一个栈中存在,那么多次启动这个activity都不会创建新的实例,和singleTop一样,系统也会回调其onNewIntent方法。注意:由于singleTask默认具有clearTop的效果,所以会导致所有在该活动上的其他activity全部出栈。如现在栈内的情况是ABCD,如果B以singleTask模式启动,启动B后,B上面的所有活动都会出栈,最后栈内的情况是AB。

singleInstance:单实例模式,这是一种加强的singleTask实例,它除了具有singleTask模式的所有特性外,还加强了一点,那就是具有此种模式的activity只能单独的位于一个任务栈中。

这些启动模式可以在功能清单AndroidManifest.xml 中设置launchMode属性。

实际操作

singleTop:我们在清单里设置oneActivity的launchMode为singleTop,然后代码里设置活动的启动顺序为one-->one,反复点击多次,查看活动栈里的情况如下。

执行adb命令:adb shell dumpsys activity activitys

Running activities (most recent first):

Run #1: ActivityRecord{23e3b5b u0 com.hugo.demo.activitydemo/.dLaunchChapter.OneActivity t595}

Run #0: ActivityRecord{1a2c6f3 u0 com.hugo.demo.activitydemo/.LaunchActivity t595}

我们可以看到活动栈中只有一个one,说明oneActivity并没有被重新创建

singleTask:如果我们将oneActivity的launchMode设置为singleTask,然后在代码里设置活动的启动顺序为one->Two->one。

one -> Two 我们记录下当前activity的栈

我们记录下当前的 Activity 栈:

Running activities (most recent first):

Run #2: ActivityRecord{1e8701b7 u0 com.hugo.demo.activitydemo/.dLaunchChapter.TwoActivity t632}

Run #1: ActivityRecord{39e11719 u0 com.hugo.demo.activitydemo/.dLaunchChapter.OneActivity t632}

two->one 记录下当前的 Activity 栈:

Running activities (most recent first):

Run #1: ActivityRecord{39e11719 u0 com.hugo.demo.activitydemo/.dLaunchChapter.OneActivity t632}

可以看见,oneActivity上面的TwoActivity被出栈了,而oneActivity和上次内存信息相同,说明确实是复用了,没有创建新的实例。

TaskAffinity-任务相关性和allowTaskReparenting

这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。当然,我们可以为每个activity单独指定TaskAffinity属性,这个属性必须和包名不一致,否则相当于没有指定。TaskAffinity属性主要和singleTask或allowTaskReparenting属性配对使用,在其他情况下没有意义。

举例:当应用A启动了应用B的一个活动C,然后按home键回到桌面,再在这个时候打开应用B,如果活动C的allowTaskReparenting属性值为true的话,那么当B被启动的时候,活动C会从应用A的任务栈移到应用B的任务栈中。可以这么理解,由于A启动了C,那么C肯定是在A的任务栈中的,但C属于B应用,那么C的TaskAffinity的值肯定不会和A的任务栈相同(因为包名不同)。所以当B被启动过后,B会创建自己的任务栈。系统在这个时候发现C所需要的任务栈已经被创建了,所以就把C从A的任务栈移到了B的任务栈。

提问:现在我们有3个活动,我们将oneActivity的launchMode属性设置为standard,twoActivity和threeActivity的launchMode属性设置为singleTask,并指定他们的taskAffinity属性为:"com.ryg.task1",注意taskAffinity属性的值为字符串。然后做如下操作。在oneActivity里面启动twoActivity,在twoActivity里面启动threeActivity,在threeActivity里面启动oneActivity,最后在oneActivity里面启动twoActivity,那现在按back键,显示的是哪个activity?

答案是返回到oneActivity。如此时再次按back键,将返回到桌面。

参考文章及Demo源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值