Android中与Activity相关的知识扫盲和备忘

Activity是什么

四大组件之一,Activity 提供窗口来和用户进行交互,开发者可以通过setContentView把UI放到窗口上显示

Task

Android中的activity全都归属于task管理,task 是多个 activity 的集合,这些 activity 按照启动顺序排
队存入一个栈(即“back stack”)。android默认会为每个App维持一个task来存放该app的所有activity,task的默认name为该app的packagename。但是一个Task是可以由一个应用或者多个应用的Activity组成的。比如我们可以通过一个Activity跳转到另一个第三方的Activity,所以说这两个应用的Activity在一个Task中。

我们也可以在AndroidMainfest.xml中申明activity的taskAffinity属性来自定义task,但不建议使
用,如果其他app也申明相同的task,它就有可能启动到你的activity,带来各种安全问题。

Task是跨应用的,不属于任何一个应用

什么是Affinity

就是task的名称,表示activity到底要放在哪个task里面
在这里插入图片描述
如果不标注,表示就是包名,如果带了affinity,activity就会启动到这个名称下的栈中里面,下面红框中就是affinity
Android中与Activity相关的知识扫盲和备忘_第1张图片

如何查看Task

  • 通过手机的Menu按键,在recent列表中显示的就是正在运行的Task
  • 通过命令
adb shell dumpsys activity activities | sed -En -e '/Stack #/p' -e '/Running activities/,/Run #0/p'

Android中与Activity相关的知识扫盲和备忘_第2张图片

哪几种方式启动Activity

我们可以通过Launcher、通知、第三方应用scheme

Activity的生命周期

Android中与Activity相关的知识扫盲和备忘_第3张图片
图片来自https://developer.android.google.cn/guide/components/activities/activity-lifecycle

打开一个Activty:onCreate、onStart、onResume
这时是运行状态
退出一个Activity: onPause、onStop、onDestory

其中可见的状态是在onStart和onStop之间,可交互的状态是onResume和onPause之间
Android中与Activity相关的知识扫盲和备忘_第4张图片

需要注意的是:

  1. onCreate与onStart的区别:

    • 可见与不可见。onCreate是不可见状态,onStart是可见状态
    • 执行次数。onStart可能会执行多次,onCreate只在创建的收执行一次
    • onCreate中的代码完全可以放在onStart中(包括setContentView)但是,onStart中的代码可能不适合放入onCreate中,比如动画的初始化放在onStart方法中比较合适
      Android中与Activity相关的知识扫盲和备忘_第5张图片
  2. onStart与onResume的区别

    • 是否可以交互。onResume可以交互、onStart不可交互
    • onStart一般用于动画的初始化,onResume一般用于打开独占设备(进程应互斥地访问这类设备,即系统一旦把这类设备分配给了某进程后,便由该进程独占,直到用完释放)或者访问单例资源
      Android中与Activity相关的知识扫盲和备忘_第6张图片
  3. onPause与onStop的区别

    • 是否可见。onPause可见,onStop不可见
    • 在系统内存不足的时候,该Activity可能会被回收,回收的时候可能不会执行onStop方法,所以程序数据的保存、独占设备和动画的关闭,最好放在onPause中
  4. onStop和onDestory的区别

    • onStop Activity还没被销毁,可以切换回该Activity;onDestory阶段Activity被销毁

Activity的启动模式

为什么要有启动模式

为了实现一些默认启动(standard)模式之外的需求:

  • 让某个 activity 启动一个新的 task (而不是被放入当前 task )
  • 让 activity 启动时只是调出已有的某个实例(而不是在 back stack 顶创建一个新的实例)
  • 你想在用户离开 task 时只保留根 activity,而 back stack 中的其它 activity 都要清空

启动模式分类

standard (默认模式)

当通过这种模式来启动Activity时,
Android为目标 Activity创建一个新的实例,并将该Activity
添加到当前Task栈中。这种方式不会启动新的Task,只是将新的 Activity添加到原有的Task中。

这里需要注意的是:

  1. 当从非Activity的context启动Activity时,需要添加NEW_TASK的flag(比如在广播中打开一个Activity会报错:android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag)
  2. standard模式的Activity并不是每次都会被创建。当从Launcher启动的Activity启动后,点击home键返回桌面,再次点击应用图标,该Activity将不会重复创建
    而是走onRestart-onStart-onResume
  3. 如果在应用内启动的Activity 启动模式是standard且affinity是默认的affinity,不管task中有没有该Activity的实例,则会创建一个新的Activity
  4. 如果在应用内启动的Activity 启动模式是standard且affinity不是默认的,task中如果有该Activity的实例,则不会创建新的Activity

singleTop

该模式和standard模式基本一致,但有一点不同:当将要被启动的Activity已经位于Task栈顶时,系统
不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。即:栈顶复用模式

应用场景:

  1. 点击通知栏的一个通知后,通知的展示页面。
    这个情况主要看产品是否需要不重复打开Activity了,如果正在展示通知详情的Activity,再点击通知不重新创建新的Activity,则该Activity适用于SingleTop的启动模式(栈顶复用模式)
  2. 耗时操作的返回页面。
    如果从一个Activity中启动了一个Service进行耗时操作,然后点击home返回了桌面,经过一段时间耗时操作结束,返回该Activity。

singleTask

Activity在同一个Task内只有一个实例。
如果将要启动的Activity不存在,那么系统将会创建该实例,并将其加入Task栈顶;
如果将要启动的Activity已存在,且存在栈顶,直接复用Task栈顶的Activity。
如果Activity存在但是没有位于栈顶,那么此时系统会把位于该Activity上面的所有其他Activity全部移
出Task,从而使得该目标Activity位于栈顶。即:栈内复用模式

应用场景:
MainActivity: 当我们从MainActivity跳转到多级子页面后,想要跳回MainActivity并将之前打开的Activity出栈。

singleInstance

无论从哪个Task中启动目标Activity,只会创建一个目标Activity实例且会用一个全新的Task栈来装载
该Activity实例(全局单例).
如果将要启动的Activity不存在,那么系统将会先创建一个全新的Task,再创建目标Activity实例并将该
Activity实例放入此全新的Task中。
如果将要启动的Activity已存在,那么无论它位于哪个应用程序,哪个Task中;系统都会把该Activity所
在的Task转到前台,从而使该Activity显示出来。

应用场景:锁屏页面、来电页面等

定义启动模式的方法

在Manifest中定义



使用Intent.setFlags


Intent i = new Intent(this,NewActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);

两种方式的区别:
在Manifest中定义是面向创建者的, 使用Intent.setFlags是面向调用者,调用者想让所启动的Activity按照自己的意愿启动

需要注意的是:

  1. 如果一个Activity的启动模式是SingleTop,启动这个Activity的时候
  • 首先要判断是否存在和该affinity同名的task
  • 如果存在,判断栈顶的Activity是要启动的Activity吗。如果是该Activity,不执行onCreate创建,执行onNewIntent;如果不是该Activity,在栈顶创建该Activity。
  • 如果不存在,判断当前栈顶的Activity是要启动的Activity吗。如果是该Activity,不执行onCreate创建,执行onNewIntent;如果不是该Activity,则需要判断intent中是否携带了FLAG_ACTIVITY_NEW_TASK,如果携带了,就创建affinity定义的Task,并创建该Activity;如果没携带,在当前栈顶创建该Activity
  1. 使用intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)启动Activity。 如果该任务栈中已经有该 Activity,相当于给 Activity 配置的启动模式为 SingleTask,会使该Activity之上的其它Activity出栈

Activity之间跳转执行的生命周期

standard

A跳转B
A:onPause
B:onCreate-onStart-onResume
A:onStop

B返回A
B:onPause
A:onRestart-onStart-onResume
B:onStop-onDestory

singleTop

A是singleTop,A跳转A
onPause-onNewIntent-onResume
在栈顶时不会创建A的实例

singleTask

A是singleTask,A跳转A和 singleTop是一样的生命周期
onPause-onNewIntent-onResume

A跳转B再跳转A
A:onPause
B:onCteate-onStart-onResume
A:onStop

B:onPause
A:onRestart-onStart-onNewIntent-onResume
B:onStop-onDestory

可以看到当A在栈中存在时,再次跳转不会创建A的实例并使A上面的B出栈

singleInstance

无论从哪个Task中启动目标Activity,只会创建一个目标Activity实例且会用一个全新的Task栈来装载
该Activity实例(全局单例).
B是singleInstance A和C是standard
A跳转B再跳转C,生命周期和standard一样,需要注意的是:

  1. 如果在C中点返回,会直接返回到A中,因为B是单独一个task,A和C是同一个task
    C:onPause
    A:onRestart-onStart-onResume
    C:onStop-onDestory
  2. 再次执行A-B因为此时B已经存在,所以B不会重新创建
    A:onPause
    B:onRestart-onStart-onNewIntent-onResume
    A:onStop

Activity回收和数据恢复

为了避免系统回收Activity造成数据丢失,Android提供了onSaveInstanceState(Bundle outState)和onRestoreInstanceState(Bundle savedInstanceState)用于保存和恢复数据

onSaveInstanceState 调用时机

onSaveInstanceState在Activity可能被系统回收的情况下调用

  • 按home键返回桌面、查看最近用过的程序

  • 按电源键锁屏
    在这里插入图片描述

  • 从当前Activity跳转另一个Activity
    Android中与Activity相关的知识扫盲和备忘_第7张图片

  • 横竖屏切换
    targetSdkVersion:28 (Android9.0)
    Android中与Activity相关的知识扫盲和备忘_第8张图片

targetSdkVersion:27
Android中与Activity相关的知识扫盲和备忘_第9张图片

需要注意的是:

  • onPause和onSaveInstanceState以及onStop的顺序是不一定的,
    targetSdkVersion低于28的app,则会在onStop之前回调;28之后,onSaveInstanceState在onStop回调之后才回调。
  • onSaveInstanceState方法并不是一定会被调用的,例如按返回键退出Activity时, 想要关闭这个Activity, 此时是没有必要保存数据,onSaveInstanceState方法不会被调用。

onRestoreInstanceState 调用时机

onRestoreInstanceState只有在Activity被系统回收的了,重新创建Activity的时候才调用
比如上一节横竖屏切换的生命周期显示在onStart后调用了onRestoreInstanceState,因为横竖屏切换时伴随着Activity的回收和创建

onCreate和onRestoreInstanceState都可以用来恢复数据,区别是什么

  • onRestoreInstanceState被调用的时候一定是Activity被销毁了。此时只要在onSaveInstanceState中在B
    undle中存入了数据,一定可以取出数据
  • 当从其它页面打开这个Activity的时候也会走onCreate,这时候取onSaveInstanceState时存入Bundle中的数据肯定是为空的
  • 使用onRestoreInstanceState恢复数据,可以决定是否调用父类的onRestoreInstanceState方法即:super.onRestoreInstanceState(savedInstanceState);使用onCreate必须调用super.onCreate(savedInstanceState);

intent-filter

Activity、services、BroadcastReceiver都是通过intent传递消息的。
在 Android 的 AndroidManifest.xml 配置文件中可以通过 intent-filter 节点为一个 Activity 指定其
Intent Filter的过滤信息,以便告诉系统该 Activity 可以响应什么类型的 Intent。
intent-filter的过滤信息有:action、category、data。

需要注意的是

  • 只有Intent同时匹配了action、category、data,才能跳转目标Activity
  • 一个Activity中可以有多组intent-filter,一个Intent只要匹配任何一组intent-filter,就可以跳转目标Activity

intent-filter 的匹配规则

action

一个 Intent Filter 可以包含多个 Action,Action 列表用于标示 Activity 所能接受的“动作”,它是一个用
户自定义的字符串。



    
    
……


在代码中就可以使用,
action 列表中包含了“com.himi.myaction”的 Activity 都将会匹配成功

Intent intent=new Intent();
intent.setAction("com.himi.myaction");

  • 需要注意的是:
    如果设置了exported = "false"的化,是不能设置intent-filter的,可以防止因为相同的action被其它activity唤醒


action的匹配要求是:intent中必须有一个action,而且这个action必须和intent-filter中的一个action相同

category

category也是一个字符串。
它的匹配规则是:intent中如果有category,不管有几个category,那么必须是intent-filter中定义的category。
intent中可以不设置category,系统会默认在startActivity或者startActivityForResult的时候添加category:“android.intent.category.DEFAULT”

所以只有在intent-filter中配置了
才会接受到隐式调用

data

data匹配要求是:如果intent-filter中定义了data,intent中必须有一个data,而且这个data必须和intent-filter中的一个data相同

data有两部分构成:URI和mimeType

URI

URI的结构是:

://:/[||]
比如:
content://com.himi.project:8888/folder
  • scheme:URI的模式,比如http、file、content等,如果没有指定scheme,那么剩下的其它参数是无效的,URI也是无效的。
  • host:URI的主机名。比如www.baidu.com,如果没有指定host,剩下的其它参数也是无效的,URI也是无效的
  • port: URI的端口号,只有指定了scheme和host后,端口号才有效
  • path: 完成的路径信息
  • pathPrefix:表示路径的前缀信息
  • pathPattern:也表示完整的路径信息,但是它里面可以包含通配符” * “

下面进行使用举例

        
            
                
                
                
                
                
                
                
                
            

            
                
                
                
                
                
                
            
        
        Intent intent = new Intent("com.himi.action.module1");
        intent.addCategory("com.himi.category.c1");
        intent.setData(Uri.parse("test1://himi"));
        startActivity(intent);

或者使用

        Intent intent = new Intent("com.himi.action.module2");
        intent.addCategory("com.himi.category.c22");
        intent.setData(Uri.parse("test://gogogo"));
        startActivity(intent);
mimeType

mimeType是指媒体类型,比如image/jpeg、video/*等

Activity的进程优先级

前台进程>可见进程>service进程>后台进程>空进程

  • 前台进程:
  1. 当前进程activity正在与用户进行交互
  2. 当前进程service正在与activity进行交互或者当前service调用了startForground()属于前台进程或者当前service正在执行生命周期(onCreate(),onStart(),onDestory())
  3. 进程中有一个BroadcastReceiver,这个BroadcastReceiver正在执行onReceive()方法
  • 可见进程:
  1. 进程中的activity,处于onPouse()状态下(比如当前覆盖的activity是以dialog形式存在)
  2. 进程有一个service,这个service和一个可见的Activity进行绑定。
  • service进程:当前开启startSerice()启动一个service服务就可以认为进程是一个服务进程
  • 后台进程:Activity的onStop()被调用,但是onDestroy()没有调用的状态。该进程属于后台进程
  • 空进程:该进程没有任何运行的数据了,且保留在内存空间,属于空进程,该进程很容易被杀死。

结束

本文用于总结和学习
如有错误,欢迎指正
如果本文对你有帮助,麻烦点个赞或收藏哦
感谢

你可能感兴趣的:(Android,android,启动模式,Activity生命周期,Activity数据恢复,intent-filter)