android系统性学习 2.1.0 —— 四大组件之Activity

                                          Android Activity

目录

activity相关问题自测:

正文:

一、启动模式

二、启动过程

三、Intent/flag

四、taskAffinity

五、生命周期

两个 Activity 之间跳转时必然会执行的是哪几个方法

六、如何保存 Activity 的状态

七、如何退出 Activity? 如何安全退出已调用多个 Activity 的 Application?

八、Context, Activity, Appliction 有什么区别?

九、一个应用程序有几个Context

十、横竖屏切换时候Activity的生命周期


activity相关问题自测:

Q1: 说下Activity的生命周期?

Q2: onStart()和onResume()/onPause()和onStop()的区别?

Q3:Activity A启动另一个Activity B会回调哪些方法?如果Activity B是完全透明呢?如果启动的是一个对话框Activity呢?

Q4:谈谈onSaveInstanceState()方法?何时会调用?

......

参考答案:https://blog.csdn.net/qq_36478274/article/details/103000599

正文:

一、启动模式

在实际的项目中我们应该根据特定的需求为每个活动指定恰当的启动模式。
启动模式一共有4种。standard、singleTop、singleTasksingleInstance

设置Activity启动模式的方法
a.在AndroidManifest.xml中给对应的Activity设定属性:

android:launchMode="standard|singleInstance|single Task|singleTop"。
b.通过标记位设定,方法是:
intent.addFlags(Intent.xxx)

  • 1、standard(标准模式、默认模式)

活动的默认启动模式。在该模式下,每当启动一个新活动,它就会在返回栈中入栈,并处于栈顶的位置,不管此活动是否已经存在于返回栈中,每次启动都会创建该活动的一个新实例,即Activity 的启动三回调( onCreate()->onStart()->onResume() )都会执行。

应用场景:mainfest中没有配置就默认标准模式

  • 2、singleTop(栈顶复用模式)

在启动活动时,如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例,同时 Activity 的 onNewIntent() 方法会被回调。

如果 Activity 已经存在却不在栈顶, 那么作用和标准模式一样, 一个新的 Activity 会被创建并放到栈顶.

在这个模式下, 我们必须同时在 onCreate() 和 onNewIntent() 两个方法中处理好传入的 Intent.

应用场景:

比如搜索功能. 当用户在搜索条中搜索完会跳转到 SearchActivity 查看搜索结果. 通常, 我们在结果页面的顶端也会放置一个同样的搜索条, 这样用户就不需要回退就可以进行相同的搜索.

如果是 standard 模式, 搜索十次就会在栈里创建了十个 SearchActivity 实例, 同时用户需要按十次回退键才能回到之前的页面. 这明显比较二.

如果是 singleTop 模式, 当在搜索结果界面时, SearchActivity 处在栈的顶端. 所以我们只需要发送 Intent 到已经存在的 SearchActivity 实例, 更新页面的结果就足够了, 不需要创建新的 Activity. 同时只需要按一次回退就能回到之前的页面。

登录页面、WXPayEntryActivity、WXEntryActivity 、推送通知栏

  • 3、singleTask(栈内复用模式)

如果栈中任意位置已经有一个 Activity 实例, 那么这个 Activity 就会被调到栈顶, 始终保持一个实例, 同时回调 onNewIntent() , 并且 singleTask 会清理在当前 Activity 上面的所有 Activity.

应用场景:

程序模块逻辑入口:主页面(Fragment的containerActivity)、WebView页面、扫一扫页面

电商中:购物界面,确认订单界面,付款界面

  • 4、singleInstance(单实例模式)

这个模式和 singleTask 很接近, 只会有一个实例存在. 不同点是持有这个 Activity 的 Task 不能再有其他 Activity 的实例了.

如果从这个 Activity 中启动另外一个不同的 Activity, 将会自动创建一个新的 Task 来存放新的 Activity.

如果从一个已经存在许多 Activity 的 Task 中调用 singleInstance Activity. 这个新 Activity 同样会被放在一个新的 Task 中.

该模式下活动会启用一个新的返回栈来管理这个活动(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈)。

那么这样做有什么意义呢?想象以下场景,假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,应该如何实现呢?使用前面3种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。而使用singlelnstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。

应用场景:系统Launcher、锁屏键、来电显示等系统应用

 

在singleTop、singleTask、singleInstance 中如果在应用内存在Activity实例,并且再次发生startActivity(Intent intent)回到Activity后,由于并不是重新创建Activity而是复用栈中的实例,因此Activity再获取焦点后并没调用onCreate、onStart,而是直接调用了onNewIntent(Intent intent)函数;

你知道onNewIntent吗?

只有启动模式是 singleTop 或者使用 FLAG_ACTIVITY_SINGLE_TOP 来 startActivity(Intent). 这个方法才回被调用. 如果新 Activity 已经位于任务栈的栈顶, 那么此 Activity 不会被重新创建. 然是调用如下方法:

onNewIntent-->onRestart-->onStart-->onResume.

二、启动过程

三、Intent/flag

上文中的四种模式都是在mainfest的xml文件中进行配置的

还可通过Intent.setFlags(int flags)设置启动模式;

FLAG_ACTIVITY_CLEAR_TOP : 等同于mainfest中配置的singleTask
FLAG_ACTIVITY_SINGLE_TOP: 同样等同于mainfest中配置的singleTop;
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS: 其对应在AndroidManifest中的属性为android:excludeFromRecents=“true”,当用户按了“最近任务列表”时候,该Task不会出现在最近任务列表中,可达到隐藏应用的目的。
FLAG_ACTIVITY_NO_HISTORY: 对应在AndroidManifest中的属性为:android:noHistory=“true”,这个FLAG启动的Activity,一旦退出,它不会存在于栈中。
FLAG_ACTIVITY_NEW_TASK: 这个属性需要在被start的目标Activity在AndroidManifest.xml文件配置taskAffinity的值【必须和startActivity发其者Activity的包名不一样,如果是跳转另一个App的话可以taskAffinity可以省略】,则会在新标记的Affinity所存在的taskAffinity中压入这个Activity。

四、taskAffinity

Activity 的归属, 也就是 Activity 应该在哪个 Task 中. 一般情况下在同一个应用中, 启动的 Activity 都在同一个 Task 中.

每个 Activity 都有 taskAffinity 属性, 这个属性指出了它希望进入的 Task. 如果一个 Activity 没有显式的指明 taskAffinity, 那么它就会使用 Application 的 taskAffinity, 如果 Application 也没有指明, 那么该 taskAffinity 的值就等于包名

五、生命周期

活动(activity)表示一个单一屏幕上的用户界面。

例如,电子邮件应用程序可能是一个活动,显示新的电子邮件列表是另一个活动,撰写电子邮件,阅读电子邮件可能又是其它的活动。如果应用程序有一个以上的活动,那么应该将其中的一个活动标记为活动启动应用程序。

C,C++ 或者 Java 语言编程,这些程序从 main() 函数开始。

Android系统是从一个Activity 的 onCreate() 方法调用开始启动程序。一个回调方法 - 启动一个活动,以及其它回调方法,如销毁一个活动,活动的生命周期如下图所示序列:

 

Activity类定义了以下的回调方法,即事件。并不需要实现所有的回调方法。需要了解每一个变化以及实现,以确保应用程序如用户所期望的行为或方式。

回调

描述

onCreate()

这是第一个回调,在活动第一次创建时调用

onStart()

这个回调在活动为用户可见时被调用

onResume()

这个回调在应用程序与用户开始可交互的时候调用

onPause()

被暂停的活动无法接受用户输入,不能执行任何代码。

在当前活动将要被暂停,上一个活动将要被恢复时调用

onStop()

当活动不在可见时调用

onDestroy()

当活动被系统销毁之前调用

onRestart()

当活动被停止以后重新打开时调用

 

两个 Activity 之间跳转时必然会执行的是哪几个方法

一般情况下比如说有两个 activity, 分别叫 A,B, 当在 A 里面激活 B 的时候, A 会调用 onPause() 方法, 然后 B 调用 onCreate()-->onStart()-->onResume(). 这个时候 B 覆盖了窗体, A 会调用 onStop() 方法. 如果 B 是个透明的或者是对话框的样式, 就不会调用 A 的 onStop() 方法.

 

六、如何保存 Activity 的状态

覆写 onSaveInstanceState() 方法

Activity 的状态通常情况下系统会自动保存的, 只有当我们需要保存额外的数据时才需要使用到这样的功能.

一般来说, 调用 onPause() 和 onStop() 方法后的 activity 实例仍然存在于内存中, activity 的所有信息和状态数据不会消失, 当 activity 重新回到前台之后, 所有的改变都会得到保留. 但是当系统内存不足时, 调用 onPause() 和 onStop() 方法后的 Activity 可能会被系统摧毁, 此时内存中就不会存有该 Activity 的实例对象了, 如果之后这个 Activity 重新回到前台, 之前所作的改变就会消失.

为了避免此种情况的发生, 需要覆写 onSaveInstanceState() 方法. onSaveInstanceState() 方法接受一个 Bundle 类型的参数, 开发者可以将状态数据存储到这个 Bundle 对象中, 这样即使 Activity 被系统摧毁, 当用户重新启动这个 Activity 而调用它的 onCreate()方法时, 上述的 Bundle 对象会作为实参传递给 onCreate()方法, 开发者可以从 Bundle 对象中取出保存的数据, 然后利用这些数据将 activity 恢复到被摧毁之前的状态.

需要注意的是, onSaveInstanceState() 方法并不是一定会被调用的, 因为有些场景是不需要保存状态数据的. 比如用户按下 BACK 键退出 Activity 时, 用户显然想要关闭这个 Activity, 此时是没有必要保存数据以供下次恢复的, 也就是 onSaveInstanceState() 方法不会被调用. 如果调用 onSaveInstanceState() 方法, 调用将发生在 onPause() 或 onStop() 方法之前.

 

onSaveInstanceState和onRestoreInstanceState
a.出现时机:异常 情况下Activity 重建,非用户主动去销毁

b.系统异常终止时,调用onSavaInstanceState来保存状态。该方法调用在onStop之前,但和onPause没有时序关系。

onSaveInstanceState与onPause的区别:前者适用于对临时性状态的保存,而后者适用于对数据的持久化保存。

c.Activity被重新创建时,调用onRestoreInstanceState(该方法在onStart之后),并将onSavaInstanceState保存的Bundle对象作为参数传到onRestoreInstanceState与onCreate方法。

可通过onRestoreInstanceState(Bundle savedInstanceState)和onCreate((Bundle savedInstanceState)来判断Activity是否被重建,并取出数据进行恢复。但需要注意的是,在onCreate取出数据时一定要先判断savedInstanceState是否为空。另外,谷歌更推荐使用onRestoreInstanceState进行数据恢复。

七、如何退出 Activity? 如何安全退出已调用多个 Activity 的 Application?

  1. 通常情况用户退出一个 Activity 只需按返回键,我们写代码想退出 activity 直接调用 finish() 方法就行.
  2. 发送特定广播: 在需要结束应用时, 发送一个特定的广播, 每个 Activity 收到广播后, 关闭即可.

//给某个 activity 注册接受接受广播的意图 registerReceiver(receiver, filter)

//如果接受到的是 关闭 activity 的广播 activity finish()掉

  1. 递归退出 就调用 finish()方法 把当前的在打开新的 Activity 时使用 startActivityForResult, 然后自己加标志, 在 onActivityResult 中处理, 递归关闭.
  2. 其实也可以通过 intent 的 flag 来实现 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)激活一个新的 activity。 此时如果该任务栈中已经有该 Activity, 那么系统会把这个 Activity 上面的所有 Activity 干掉. 其实相当于给 Activity 配置的启动模式为 SingleTop.
  3. 记录打开的 Activity: 每打开一个 Activity, 就记录下来. 在需要退出时, 关闭每一个 Activity
List<Activity> lists; // 在 application 全局的变量里面 
lists = new ArrayList<Activity>;

lists.add(this);

for(Activity activity: lists) {
    activity.finish();
}

lists.remove(this);

八、Context, Activity, Appliction 有什么区别?

相同:

Activity 和 Application 都是 Context 的子类

Context 从字面上理解就是上下文的意思, 在实际应用中它也确实是起到了管理上下文环境中各个参数和变量的作用, 方便我们可以简单的访问到各种资源.

不同:

维护的生命周期不同.

  • Activity 维护的是当前的 Activity 的生命周期. 所以其对应的Context也只能访问该 Activity 内的各种资源
  • Application 维护的是整个项目的生命周期.

使用 context 的时候, 小心内存泄露, 防止内存泄露, 注意一下几个方面:

  1. 不要让生命周期长的对象引用 activity context, 即保证引用 activity 的对象要与 activity 本身生命周期是一样的.
  2. 对于生命周期长的对象,可以使用 application context。
  3. 避免非静态的内部类, 尽量使用静态类, 避免生命周期问题, 注意内部类对外部对象引用导致的生命周期变化.

九、一个应用程序有几个Context

在应用程序中Context的具体实现子类就是: Activity, Service, Application. 那么Context数量 = Activity数量 + Service数量 + 1

Broadcast Receiver, Content Provider 并不是 Context 的子类, 它们所持有的 Context 都是其他地方传过去的, 所以并不计入Context总数.

十、横竖屏切换时候Activity的生命周期

总结:

  1. 不设置 Activity 的 android:configChanges 时, 切屏会重新调用各个生命周期, 切横屏时会执行一次, 切竖屏时会执行两次.
  2. 设置 Activity 的 android:configChanges="orientation" 时, 切屏还是会重新调用各个生命周期, 切横\/竖屏时只会执行一次.
  3. 设置 Activity 的 android:configChanges="orientation|keyboardHidden" 时, 切屏不会重新调用各个生命周期, 只会执行onConfigurationChanged方法
Activity 会自己管理配置的变化. 当运行的时候, 配置变了, 它会自己关闭并重新启动, 但是如果设置了 android:configChanges="某一个配置", 它就不会在这个配置发生改变时重新启动.

具体过程

  1. 新建一个Activity,并把各个生命周期打印出来
  2. 运行Activity,得到如下信息onCreate-->onStart-->onResume-->
  3. 切换成横屏时onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->
  4. 再切换成竖屏时,发现打印了两次相同的logonSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume
  5. 修改AndroidManifest.xml, 把该 Activity 添加 android:configChanges="orientation" onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->
  6. 再执行步骤4, 发现不会再打印相同信息, 但多打印了一行 onConfigChanged onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->onConfigurationChanged-->
  7. 把步骤5的 android:configChanges="orientation" 改成 android:configChanges="orientation|keyboardHidden",执行步骤3, 就只打印onConfigChangedonConfigurationChanged-->
  8. 执行步骤4onConfigurationChanged-->onConfigurationChanged-->
只竖屏显示 android:screenOrientation="portrait"只横屏显示 android:screenOrientation="landscape"

参考:

活动的四种启动模式:https://www.jianshu.com/p/b4472dc6911e

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值