Android 浅谈 Activity (下)

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

Android 浅谈 Activity(中)

上节讲了数据传递和数据回传,这节讲一讲状态保存以及启动模式。

在使用Bundle传递数据时,要注意,Bundle的大小是有限制的 < 0.5MB,如果大于这个值
是会报TransactionTooLargeException异常的!!!

Activity 状态保存

每次我们写一个Activity 的时候,都要重写onCreate()方法,onCreate(Bundle saveInstanceState) 方法里面有一个Bundle,这个Bundle究竟是什么呢?其实这个是系统用于给我们保存状态用的,简而言之,如果你的App 长时间处于stopped 形态而且系统需要更多内存或者系统内存极为紧张时,系统就会回收你的 Activity,这种被强制回收或者意外终止的情况用户体验是非常不好的。你可以想象一下,如果你正在看一个帖子,里面有100行文字,你看到了第50行,然后接了个电话,出去吃饭了,回来想继续看的时候却发现当前停留的是第0行,而不是第50行,这种体验肯定很不好。

所以,系统提供了保存状态的回调以作补偿。Bundle 里面可以存储一下简单的数据、状态。

既然讲清楚了这些,那 onSaveInstanceState(Bundle outState)onRestoreInstanceState(Bundle savedInstanceState) 这两个回调也就可以理解了。他们俩一个代表存,一个代表取。

onSaveInstanceState(Bundle outState) 什么时候调用呢?查阅了大量的资料,并经过测试发现,以下几种情形会被调用:

1.当点击 home 键回到主页或者长按 home 键选择其他应用程序时调用;
2.按下电源键锁屏的时候会调用;
3.启动一个新的 Activity 时调用;
4.横竖屏切换的时候,调用,这个很好理解,因为你横竖屏切换的时候,其实是销毁了当前 Activity 然后再创建 。

onRestoreInstanceState() 一般都是在onStart() 和 onResume() 之间调用。

比如综合上述 onSaveInstanceState() 的存数据的时机也可以想象,并不一定只有在Activity 销毁时才会去存,但是无论是哪种情况,当再次返回应用或者再次返回Activity 时,肯定调用了onStart()和onResume(),所以我们也就能理解为什么onRestoreInstanceState() 在这两个生命周期之间调用了。

注意:取数据的时候并不一定只有*onRestoreInstanceState()*才能做得到,我们天天重写的*onCreate()*也能做得到。

onRestoreInstanceState()onCreate() 的区别是 :

onRestoreInstanceState() 一旦被调用,其参数 Bundle savedInstanceState 一定是有值的,我们不用额外第判断是否为空;
但是onCreate() 不行,onCreate() 如果是正常启动,其参数Bundle savedInstanceState肯定为null,所以我们必须要额外判断。
这两个方法,官方推荐采用 onRestoreInstanceState() 去恢复数据。

讲了一堆概念性的东西,国际惯例,上代码。

package com.mesmerize.activitystate;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        System.out.println("MainActivity.onCreate");

        if (savedInstanceState != null) {

            String test_result = savedInstanceState.getString("test_result");
            System.out.println("[MainActivity.onCreate] :: test_result = " + test_result);
        }

    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        System.out.println("MainActivity.onSaveInstanceState");

        outState.putString("test_result","且随疾风前行,身后一许流星.");
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        String test_result = savedInstanceState.getString("test_result");

        System.out.println("[MainActivity.onRestoreInstanceState] :: test_result = " + test_result);
    }



    @Override
    protected void onStart() {
        super.onStart();

        System.out.println("MainActivity.onStart");
    }

    @Override
    protected void onPause() {
        super.onPause();

        System.out.println("MainActivity.onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();

        System.out.println("MainActivity.onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        System.out.println("MainActivity.onDestroy");
    }
}

代码很简单,就是给大家看看调用顺序,用于验证上述结论。
最简单的其实最有力道,工作后你就发现,理解了调用顺序,调换一下代码的执行顺序就能解决你2,300行代码解决不了的事情。

看现象,当我启动应用的时候:

09-22 09:30:21.607 1869-1869/com.mesmerize.activitystate I/System.out: MainActivity.onCreate
09-22 09:30:21.607 1869-1869/com.mesmerize.activitystate I/System.out: MainActivity.onStart

为了最直观有效,我只验证横竖屏切换的结果了,其他的结果,有兴趣可以自己验证,我就不在大篇幅的累述。
当我切换横屏时:

09-22 09:34:33.951 1869-1869/com.mesmerize.activitystate I/System.out: MainActivity.onPause
09-22 09:34:33.951 1869-1869/com.mesmerize.activitystate I/System.out: MainActivity.onSaveInstanceState
09-22 09:34:33.951 1869-1869/com.mesmerize.activitystate I/System.out: MainActivity.onStop
09-22 09:34:33.951 1869-1869/com.mesmerize.activitystate I/System.out: MainActivity.onDestroy
09-22 09:34:33.967 1869-1869/com.mesmerize.activitystate I/System.out: MainActivity.onCreate
09-22 09:34:33.967 1869-1869/com.mesmerize.activitystate I/System.out: [MainActivity.onCreate] :: test_result = 且随疾风前行,身后一许流星.
09-22 09:34:33.967 1869-1869/com.mesmerize.activitystate I/System.out: MainActivity.onStart
09-22 09:34:33.967 1869-1869/com.mesmerize.activitystate I/System.out: [MainActivity.onRestoreInstanceState] :: test_result = 且随疾风前行,身后一许流星.

可以看到,onPause() 之后onSaveInstanceState() 就执行了,在这里我存了一个 String 。而后Activity 重新创建,走了onCreate() 并判断saveInstanceState参数不为null,取出数据“且随疾风前行,身后一许流星。”然后走了onStart()和onRestoreInstanceState 并取出相同的数据“且随疾风前行,身后一许流星。”

以上演示了一个简单的存取字符串,其实它还能存更多更多类型的数据,比如Bundle、比如Byte、比如Char、比如Parcelable等等等等。

Activity 启动模式

为什么要有 LaunchMode

为什么 Activity 要有启动模式?
默认情况下,当我们多次启动同一个 Activity 的时候,系统就会创建多个实例并把它们一一放入任务栈中,当我们按返回键,会发现这些 Activity 会一一回退。 任务栈是一种“后进先出”的栈结构,这个比较好理解,每按一下返回键就会有一个 Activity 出栈,直到栈空位置,当栈中无任何 Activity 的时候,系统就会回收这个任务栈。直到了 Activity 的默认启动模式以后,我们可能就会发现一个问题:多次启动同一个 Activity,系统重复创建多个实例,这样不是很傻吗?这样的确有点傻,Android 在设计的时候不可能不考虑到这个问题,所以它提供了启动模式来修改系统的默认行为。目前有四种启动模式:standard、singleTop、singleTask 和 singleInstance,下面先介绍各种启动模式的含义。

standard

   默认的启动模式,如果不指定 Activity 的启动模式,则使用这种方式启动 Activity。每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例是否已经存在。被创建的实例的生命周期符合典型情况下 Activity 的生命周期,它的onCreate、onStart、onResume 都会被调用。这是一个典型的多实例实现,一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。在这种模式下,谁启动了这个 Activity,那么这个 Activity 就运行在启动它的那个 Activity 所在的栈中。
   比如 Activity A 启动了 Activity B(B是标准模式),那么 B 就会进入到 A 所在的栈中。

singleTop

   栈顶复用模式。在这种模式下,如果新 Activity 已经位于任务栈的栈顶,那么此 Activity 不会被重新创建,同时它的 onNewIntent 方法会被回调。而此时这个 Activity 的onCreate、onStart、onResume 不会被系统调用,因为它并没有发生改变。如果新 Activity 的实例已存在但不是位于栈顶,那么新 Activity 仍然会重新创建。
   举例:
   比如目前栈内的情况为ABCD,其中ABCD为四个 Activity,A位于栈底,D位于栈顶,这个时候如果要再次启动D,如果D的启动模式为 singleTop,那么栈内的情况仍然为ABCD;
   如果D的启动模式为standard,那么由于D被重新创建,栈内的情况就变为ABCDD;
   而如果启动的是A,并且A的启动模式是 singleTop 的话,由于A不在栈顶,A会被重新创建,栈内的情况就变为ABCDA。

singleTask

   栈内复用模式。这是一种单实例模式,在这种情况下,只要 Activity 在一个栈中存在,那么多次启动此 Activity 都不会重新创建实例,和singleTop一样,系统也会回调其 onNewIntent。具体一点,当一个具有 singleTask 模式的 Activity 请求启动后。
   比如 Activity A,系统首先会寻找是否存在 A 想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建 A 的实例并把 A 放到栈中。如果存在 A 所需的任务栈,这时要看 A 是否在栈中有实例存在,如果有实力存在,那么系统就会把 A 调到栈顶并调用它的 onNewIntent 方法,如果实例不存在,就创建 A 的实例并把 A 压入栈中。
   举例:
   比如目前任务栈 S1 中的情况为 ABC,这个时候 Activity D以 singleTask 模式请求启动,其所需要的任务栈为 S2,由于 S2 和 D 的实例均不存在,所以系统会先创建任务栈 S2 ,然后再创建 D 的实例并将其入栈到 S2。
   另外一种情况,假设 D 所需的任务栈为 S1,那么由于 S1 已经存在,所以系统会直接创建 D 的实例并将其入栈到 S1。
   如果 D 所需的任务栈为 S1,并且当前任务栈 S1 的情况为 ADBC,根据栈内复用原则,此时 D 不会重新创建,系统会把 D 切换到栈顶并调用其 onNewIntent,同时由于 singleTask 默认具有 clearTop 的效果,会导致栈内所有在 D 上面的 Activity 全部出栈,于是最终 S1 中的情况为 AD。

singleInstance

   单实例模式。这是一种加强的 singleTask 模式,它除了具有 singleTask 模式的所有特性外,还加强了一点,那就是具有此种模式的 Activity 只能单独地位于一个任务栈中,换句话说,比如 Activity A 是singleInstance 模式,当 A 启动后,系统会为它创建一个新的任务栈,然后 A 独自在这个新的任务栈中,由于栈内复用的特性,后续的请求均不会创建新的 Activity ,除非这个独特的任务栈被系统销毁了。

上面介绍了几种启动模式,有一种情况要指出。

比如目前有 2 个任务栈,一个前台一个后台,前台任务栈的情况为 AB,而后台任务栈的情况为 CD,这里假设 CD 的启动模式均为 singleTask。现在请求启动D,那么整个后台任务栈都会被切换到前台,这个时候整个后退列表就变成了 ABCD。当用户按后退键的时候,列表中的 Activity 会一一出栈,按照 D->C->B->A 的情况一一出栈。
而如果不是请求启动 D 而是启动 C ,那么情况就不一样了,整个后退列表就变成了 ABC ,后退的时候会按照 C->B->A 的顺序退出。

给 Activity 指定启动模式有两种方法,第一种是通过 AndroidMainifest 给 Activity 指定启动模式,如下所示:

<activity
    android:name="全类名"
    android:launchMode="singleTask"/>

另一种情况是通过在 Intent 中设置标志位来为 Activity 指定启动模式,比如:

Intent intent = new Intent(this,目标Activity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

这两种方式都可以为 Activity 指定启动模式,但是二者还是有区别的。首先,优先级不同,第二种方式的优先级要高于第一种,当两种同时存在时,以第二种方式为准;

Activity 的 Flags 有很多,作用也很多,有的标记为可以设定 Activity 的启动模式,比如 FLAG_ACTIVITY_NEW_TASK 和 FLAG_ACTIVITY_SINGLE_TOP 等;还有的标记位可以影响 Activity 的运行状态,比如FLAG_ACTIVITY_CLEAR_TOP 和 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 等。

  • FLAG_ACTIVITY_NEW_TASK

       这个标记位的作用是为 Activity 指定“singleTask”启动模式,其效果和在 XML 中指定该启动模式相同。
    
  • FLAG_ACTIVITY_SINGLE_TOP

       这个标记位的作用是为 Activity 指定“singleTop”启动模式,其效果和在 XML 中指定该启动模式相同。
    
  • FLAG_ACTIVITY_CLEAR_TOP

       具有此标记位的 Activity,当它启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈。这个标记位一般会和 singleTask 启动模式一起出现,在这种情况下,被启动 Activity 的实例如果已经存在,那么系统就会调用它的 onNewIntent。如果被启动的 Activity 采用 standard 模式启动,那么它连同它之上的 Activity 都要出栈,系统会创建新的 Activity 实例并放入栈顶。
    
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

       具有此标记的 Activity 不会出现在历史 Activity 的列表中,当某些情况下我们不希望用户通过历史列表回到我们的 Activity 的时候这个标记比较有用。它等同于在 XML 中指定 Activity 的属性 android:excludeFromRecents="true"。
    
  • FLAG_ACTIVITY_NO_HISTORY

       使用这种模式启动 Activity,当该 Activity 启动其他 Activity 后,该 Activity 就消失了,不会保留在 Activity 栈中,例如 A-B,B中以这种模式启动 C,C 再启动 D,则当前 Activity 栈为 ABD。
    
浅谈android中如何在两个Activity之间互传数据 废话不多说:android在两个Activity文件中互传数据 阅读详情

相关推荐

Android 事件分发机制详解/ 及Activity启动流程浅谈

目前网上的事件分发机制文章很多,可能大家看了很多,还是很懵逼,这篇文章是我自己总结的事件分发机制,提取了在事件分发机制中,最精华的部分,如果对事件分发还是不太清楚,可以看看这篇文章。

qq_45730283的博客 1569

Android 浅谈 Activity

在正文开始之前,我给大家来个段子: 乌龟正在河里洗澡被癞蛤蟆看见了, 乌龟:没见过像我这样的美女吗?看你眼珠子都快要蹦出来了。 癞蛤蟆:妹,你就别逗我了,没有看见我身上已经起鸡皮疙瘩了吗? 只是想让大家放松下,作为一名程序猿不要一直紧绷着大脑。相信大家对 Activity都不陌生,或多或少都有自己的理解,我会从什么是 Activity,创建一个 Activity,实现一个用户界面,如何在清单

文淑的博客 858

2024年安卓最新Android——浅谈Activity生命周期,海尔面试一般都会问什么

本人从事Android开发已经有十余年,算是一名资深的移动开发架构师了吧。根据我的观察发现,对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。所以在此将我十年载,从萌新小白一步步成长为Android移动开发架构师的学习笔记,从Android四大组件到手写实现一个架构设计,我都有一一的对应笔记为你讲解。当然我也为你们整理好了百度、阿里、腾讯、字节跳动等等互联网超级大厂的历年面试真题集锦。

2401_84123201的博客 839

Android 浅谈 Activity(上)

第一次写博客 , 记录一下

programming_path的博客 446

浅谈AndroidActivity生命周期

浅谈AndroidActivity生命周期  Activity作为四大组件之一,出现的频率相当高,基本上我们在android的各个地方都能看见它的踪影,因此深入了解Activity,对于开发高质量应用程序是很有帮助的。今天我们就来详细地聊聊Activity的生命周...

angyunlin5641的博客 222

Android——浅谈Activity生命周期

1.从硬件来说,移动设备的CPU、电量等受到了一定的限制,如果用户暂时不使用APP时,后台的运行会占用很大部分的内存等资源,这是一种浪费。Activity的生命周期机制就是为了终止不影响用户的无用事情,用这些资源去分配其他需要的对象。2.从手机的交互性质来说,只能显示一个APP界面在前台,而不在前台的APP自然优先级会降低,而后台的占用资源的事件不应该影响到前台用户的使用。而生命周期则是确认Activity创建的过程,根据流程创建Activity是该模式的本质,而Activity创建的流程是哪些呢?

2401_85729556的博客 756

浅谈androidactivity

Activity 的生命周期 和 J2ME 的 MIDlet 一样,在 android 中,Activity 的生命周期交给系统统一管理。与 MIDlet 不同的是安装在 android 中的所有的 Activity 都是平等的。 Activity 的状态及状态间的转换 在 android 中,Activity 拥有四种基本状态: Active/Runing一个新 Activit

gaohongijj的专栏 693

Android 浅谈 Activity(中)

上篇内容讲一些比较基本和简单的用法,这篇稍微说一些相对来来说比较高级一点的用法。数据传递举例来讲:当前页面上有一个EditText用于接收用户输入信息,当点击一个按钮,开启下一个页面,并在写个页面显示用户所输入的文字,怎么做? 这就涉及到了Activity之间数据传递。 而通过Intent就可以做到这个功能,查看官方API发现Intent有一个putExtra的方法,接收很多种数据类型,如图:

programming_path的博客 687

2024年安卓最新Android——浅谈Activity生命周期(1),2024最新阿里Android高级面试题总结

上面这些公司都是时下最受欢迎的互联网大厂,他们的职级、薪资、福利也都讲的差不多了,相信大家都是有梦想和野心的人,心里多少应该都有些想法。也相信很多人也都在为即将到来的金九银十做准备,也有不少人的目标都是这些公司。我这边有不少朋友都在这些厂工作,其中也有很多人担任过面试官,上面的资料也差不多都是从朋友那边打探来的。除了上面的信息,我这边还有这些大厂近年来的面试真题及解析,以及一些朋友出于兴趣和热爱一起整理的Android时下热门知识点的学习资料。

2401_84123201的博客 931

浅谈AndroidActivity启动流程介绍

4 Activity相关介绍 Activityandroid四大组件之一,主要用于显示用户界面,通过上面的章节我们已经知道,App Client如果要向系统屏幕输出图形数据,需要通过Surface来实现,那在开发过程中,调用Activity的setContentView来设置自定义视图,这个视图的图形数据是如何关联到Surface的呢?接下去就对相关内容做简单介绍。 Android通过

拿节 943

Android浅谈activity生命周期

Android浅谈activity生命周期 一.activity简介 ActivityAndroid组件中最基本也是最为常见用的四大组件(Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器)之一。 Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务。 Activity中所...

jhchen4的博客 446

浅谈androidactivity

  说道activity,大家可以说是熟悉的不能再熟悉,首先,先来个镇楼图,      个人觉得谷歌的这张图,比别的什么生命周期图都好;说下各个生命周期注意的:   1:onstart()时,activity可见;   2:oncreate()在生命周期中只执行一次;   3:onpause()时,activity处于半透明的状态,不可操作;   4:onstop()时,acti...

weixin_30701521的博客 69

浅谈Android四大组件之Activity

ActivityAndroid中最重要,最常见,也是最基本的组件,下面来详细介绍下Activity。 一:介绍 一个Acitvity作为一个显示在屏幕上的用户交互界面,比如在电子邮件应用中比如:一个用来显示收件列表的Activity,一个用来写邮件的Activity,一个阅读邮件内容的Activity,等等。Activity用来提供用户体验,许多不同体验的Activity聚集在一起...

229
上一篇: 作为一个Android开发者,你所需要掌握的东西
下一篇: Android Context 源码的理解
临风眠
博客等级 码龄10年 0粉丝 7原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值