Android四大组件 - Activity知识点总结

Activity

Activity是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。

如图所示,了解视图的层级对于我们理解view的绘制,事件分发,事件冲突以及源码分析的处理很重要.

Activity 的用户界面是由层级式视图 — 衍生自 View 类的对象 — 提供的。每个视图都控制 Activity 窗口内的特定矩形空间,可对用户交互作出响应,当你用手机打开手机系统的显示布局边界的时候就会非常的明显.

Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图。DecorView就是个顶层视图,是所有View的最外层布局。ViewRoot像个连接器,负责沟通,通过硬件的感知来通知视图,进行用户之间的交互。

想了解更多请查看:https://www.jianshu.com/p/8766babc40e0

Activity内容基本会在本篇涉及到,我们将围绕以下主要知识点进行分析:

1 在清单文件中声明Activity以及重要的属性讲解

2 使用Intent过滤器,如何去隐式启动分享Activity,分享指定app

3 Activity的启动及其基本原理

4 Activity声明周期及其处于不同启动模式和任务栈下的生命周期的动态改变

5 异常生命周期下,保存Activity基本状态

6 使用ActivityManager管理Activity,直接在项目中使用(附代码)

7 处理变更配置,特别是键盘弹起的解决方案

8 Activity任务栈的理解

1 在清单文件中声明 Activity

您必须在清单文件中声明您的 Activity,这样系统才能访问它。 要声明您的 Activity,请打开您的清单文件,并将 <activity> 元素添加为 <application> 元素的子项。例如:

<application
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name="com.example.android.architecture.blueprints.MainActivity"
            android:theme="@style/AppTheme.OverlapSystemBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

您还可以在此元素中加入几个其他特性,以定义 Activity 标签、Activity 图标或风格主题等用于设置 Activity UI 风格的属性。 

重要属性:

android:name 属性是唯一必需的属性—它指定 Activity 的类名。

android:launchMode 属性设置启动模式.包括:standard,singleTop,singleInstance,singleTask

android:taskaffinity 属性设置当前activty归属的任务栈,一般情况下在同一个应用中,启动的Activity都在同一个Task中,它们在该Task中度过自己的生命。每个Activity都有taskAffinity属性,这个属性指出了它希望进入的Task。如果一个Activity没有显式的指明taskAffinity,那么它的这个属性就等于Application指明的taskAffinity,如果Application也没有指明,那么该taskAffinity的值就等于应用的包名。

android:theme 设置当前activity的主题(注意,android8.0以后透明主题和屏幕横竖屏方向冲突)

具体可以查看我总结的android8.0适配方案 https://blog.csdn.net/yang1349day/article/details/80016607

具体属性还有很多,可以自己查看

android:exported Activity 是否可由其他应用的组件启动 —“true”表示可以,“false”表示不可以。若为“false”,则 Activity 只能由同一应用的组件或使用同一用户 ID 的不同应用启动。

android:screenOrientation Activity 在设备上的显示方向。如果 Activity 是在多窗口模式下运行,系统会忽略该属性。

更多属性可以查看文档: https://developer.android.google.cn/guide/topics/manifest/activity-element#nm

2 使用 Intent 过滤器

<activity> 元素还可指定各种 Intent 过滤器—使用 <intent-filter> 元素—以声明其他应用组件激活它的方法。

此处可以设置当前应用被其他应用调起所需要的条件,一般我们分享文章或者视频的时候会在系统自带的弹框中显示

具体可以看我写的 > Android调起系统分享,并指定分享的应用 https://blog.csdn.net/yang1349day/article/details/81514133

3 activity的启动

activity分为隐式启动和显示启动

显式启动:直接指定要跳转的Activity类名,不用过滤,效率高,适用于同一个应用中的不同Activity跳转

隐式启动:需要过滤,相对耗时,但可以找到所有之匹配的应用。适用于不同应用之间的Activity跳转。

此处要注意的是隐式启动,可能会导致启动失败,需要注意做判断.

隐式启动的流程示意图:

图 1. 隐式 Intent 如何通过系统传递以启动其他 Activity 的图解:

[1] Activity A 创建包含操作描述的 Intent,并将其传递给 startActivity()

[2] Android 系统搜索所有应用中与 Intent 匹配的 Intent 过滤器。 找到匹配项之后,

[3] 该系统通过调用匹配 Activity(Activity B)的 onCreate() 方法并将其传递给 Intent,以此启动匹配 Activity。

写的比较好的博客可以参考:https://www.jianshu.com/p/27d0ec1c52b2

启动Actiivty获得结果

有时,您可能需要从启动的 Activity 获得结果。在这种情况下,请通过调用 startActivityForResult()(而非 startActivity())来启动 Activity。 要想在随后收到后续 Activity 的结果,请实现 onActivityResult() 回调方法。 当后续 Activity 完成时,它会使用 Intent 向您的 onActivityResult() 方法返回结果。

实际情况是,很多人会用eventbus或者其他响应式框架来更新上一个界面获取的结果,我觉得越简单越好.

同时需要注意的是,fragment跳转activity获取结果,同时复写了onActivityResult() 导致的异常处理

4 管理activity生命周期

官方生命周期图:

onCreate: 首次创建 Activity 时调用。 您应该在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。 系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态(走异常的生命周期,此处才有值)

onReStart: 在 Activity 已停止并即将再次启动前调用。

onStart: 在 Activity 即将对用户可见之前调用。如果 Activity 转入前台,则后接 onResume(),如果 Activity 转入隐藏状态,则后接 onStop()

onResume: 在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。

注意: 此处会被频繁调用,不要在此处去处理view的创建和绘制等耗时操作

onPause: 当系统即将开始继续另一个 Activity 时调用。 因为当前activity可能会在此处结束生命周期,所以此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。

如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接 onStop()

onStop: 在 Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。

如果 Activity 恢复与用户的交互,则后接 onRestart(),如果 Activity 被销毁,则后接 onDestroy()

onDestroy: 在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(有人对 Activity 调用了 finish()),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 您可以通过 isFinishing() 方法区分这两种情形。

activity可能会被销毁或者结束的回调包括(onPause()onStop() 和 onDestroy())。由于 onPause() 是这三个方法中的第一个,因此 Activity 创建后,onPause() 必定成为最后调用的方法,然后才能终止进程 — 如果系统在紧急情况下必须恢复内存,则可能不会调用 onStop() 和 onDestroy()。因此,您应该使用 onPause() 向存储设备写入至关重要的持久性数据(例如用户编辑)。不过,您应该对 onPause() 调用期间必须保留的信息有所选择,因为该方法中的任何阻止过程都会妨碍向下一个 Activity 的转变并拖慢用户体验。

思考:

1生命周期需要动态的去理解. 比如设置了几个不同启动模式的Activity切换的时候的生命周期是怎么样的,遇到异常的时候生命周期是怎么样的

2 考虑到任务栈的不同. 当app处于前台的时候,当前activity的任务栈处于前台,处于后台的是其他的任务栈(其他activity任务栈,launcer桌面系统任务栈),当我们在前后台切换任务栈的时候,由于任务栈的不同,导致的界面的显示和生命周期也是不一样的

关于启动模式和任务栈比较好的文章: https://blog.csdn.net/javazejian/article/details/52071885

5 保存Activity状态(异常的生命周期下)

当系统为了恢复内存而销毁某项 Activity 时,Activity 对象也会被销毁,因此系统在继续 Activity 时根本无法让其状态保持完好,而是必须在用户返回 Activity 时重建 Activity 对象。在这种情况下,您可以实现另一个回调方法对有关 Activity 状态的信息进行保存,以确保有关 Activity 状态的重要信息得到保留:onSaveInstanceState()。同时系统会先调用 onSaveInstanceState(),然后再使 Activity 变得易于销毁。系统会向该方法传递一个 Bundle,您可以在其中使用 putString() 和 putInt() 等方法以名称-值对形式保存有关 Activity 状态的信息。然后,如果系统终止您的应用进程,并且用户返回您的 Activity,则系统会重建该 Activity,并将 Bundle 同时传递给 onCreate() 和 onRestoreInstanceState()。您可以使用上述任一方法从 Bundle 提取您保存的状态并恢复该 Activity 状态。如果没有状态信息需要恢复,则传递给您的 Bundle 是空值(如果是首次创建该 Activity,就会出现这种情况)。

异常生命周期图:

注:

1 无法保证系统会在销毁您的 Activity 前调用 onSaveInstanceState(),因为存在不需要保存状态的情况(例如用户使用“返回”按钮离开您的 Activity 时,因为用户的行为是在显式关闭 Activity)。 如果系统调用 onSaveInstanceState(),它会在调用 onStop() 之前,并且可能会在调用 onPause() 之前进行调用。

2 由于无法保证系统会调用 onSaveInstanceState(),因此您只应利用它来记录 Activity 的瞬态(UI 的状态)— 切勿使用它来存储持久性数据,而应使用 onPause() 在用户离开 Activity 后存储持久性数据(例如应保存到数据库的数据)。

6 管理Activity

一般在项目中我们自己去写ActivityManager来管理activity,包括获取context,查找对应的activity是否存在于栈中等等,以下是工具类:

package com.reds.didi.view;


import android.app.Activity;

import com.reds.didi.utils.ListUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2016/5/14 0014.
 * KActivity是基类
 */
public class ActivityManager {

    private static final List<KActivity> ActivityStack = new ArrayList<>();

    public static List<KActivity> getStack(){
        return ActivityStack;
    }

    public static void add( KActivity activity ) {
        ActivityStack.add( activity );
    }

    public static void remove( Activity activity ) {
        for( int i = ActivityStack.size()-1; i >= 0; i-- )
        {
            if( ActivityStack.get(i) == activity ) {
                ActivityStack.remove(i);
                break;
            }
        }
    }

    public static KActivity getTopActivity() {
       if( ActivityStack.size() > 0 ) {
           return ActivityStack.get( ActivityStack.size()-1 );
       } else {
          throw new NullPointerException("栈顶没有Activity");
       }
    }

    public static int getActivityCount() {
        if( ListUtil.isEmpty(ActivityStack) ) return 0;
        return ActivityStack.size();
    }

    public static KActivity getActivity( int position ) {
        if( position < getActivityCount() ) {
            return ActivityStack.get(position);
        } else {
            return null;
        }
    }

    public static void removeActivity( int position ) {
        if( position < getActivityCount() ) {
            ActivityStack.remove( position );
        }
    }

    public static boolean contains( Class zClass ) {
        for( int i = 0; i < ActivityStack.size(); i++ ) {
            if( ActivityStack.get(i).getClass().toString().equals( zClass.toString() ) ) return true;
        }
        return false;
    }

}

7 处理变更配置

有些设备配置可能会在运行时发生变化(例如屏幕方向、键盘可用性及语言)。 发生此类变化时,Android 会重建运行中的 Activity(系统调用 onDestroy(),然后立即调用 onCreate())。此行为旨在通过利用您提供的备用资源(例如适用于不同屏幕方向和屏幕尺寸的不同布局)自动重新加载您的应用来帮助它适应新配置。

如果您对 Activity 进行了适当设计,让它能够按以上所述处理屏幕方向变化带来的重启并恢复 Activity 状态,那么在遭遇 Activity 生命周期中的其他意外事件时,您的应用将具有更强的适应性。

经常处理的包括:

要声明由 Activity 处理配置变更,请在清单文件中编辑相应的 <activity> 元素,以包含 android:configChanges 属性以及代表要处理的配置的值。android:configChanges 属性的文档中列出了该属性的可能值(最常用的值包括 "orientation" 和 "keyboardHidden",分别用于避免因屏幕方向和可用键盘改变而导致重启)。您可以在该属性中声明多个配置值,方法是用管道 | 字符分隔这些配置值。

<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">

同时,我们还可以设置键盘弹起的状态,例如键盘弹起之后设置布局压缩或者设置布局往上挤压

在manifest文件中可以设置Activity的android:windowSoftInputMode属性,这个属性值常见的设置如下: 
android:windowSoftInputMode=”stateAlwaysHidden|adjustPan” 
那么这里值的含义列表如下: 
stateUnspecified:软键盘的状态并没有指定,系统将选择一个合适的状态或依赖于主题的设置 
stateUnchanged:当这个activity出现时,软键盘将一直保持在上一个activity里的状态,无论是隐藏还是显示 
stateHidden:用户选择activity时,软键盘总是被隐藏 
stateAlwaysHidden:当该Activity主窗口获取焦点时,软键盘也总是被隐藏的 
stateVisible:软键盘通常是可见的 
stateAlwaysVisible:用户选择activity时,软键盘总是显示的状态 
adjustUnspecified:默认设置,通常由系统自行决定是隐藏还是显示 
adjustResize:该Activity总是调整屏幕的大小以便留出软键盘的空间 
adjustPan:当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分

<activity  
    android:name="com.bear.softkeyboardlistener.MainActivity"  
    android:label="@string/app_name"  
    android:windowSoftInputMode="stateAlwaysHidden|adjustResize" >  
    <intent-filter>  
        <action android:name="android.intent.action.MAIN" />  

        <category android:name="android.intent.category.LAUNCHER" />  
    </intent-filter>  
</activity>  

如果是处理Android键盘面板冲突 布局闪动处理方案的话,可以看这个开源库https://github.com/Jacksgong/JKeyboardPanelSwitch

8 Activity任务栈

1. android任务栈又称为Task,它是一个栈结构,具有后进先出的特性,用于存放我们的Activity组件。

2. 我们每次打开一个新的Activity或者退出当前Activity都会在一个称为任务栈的结构中添加或者减少一个Activity组件(一般当我们启动一个activity的时候,activity就处于启动它的那个activity的同一个任务栈中),因此一个任务栈包含了一个activity的集合, android系统可以通过Task有序地管理每个activity,并决定哪个Activity与用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互。

3. 在我们退出应用程序时,必须把所有的任务栈中所有的activity清除出栈时,任务栈才会被销毁。当然任务栈也可以移动到后台, 并且保留了每一个activity的状态. 可以有序的给用户列出它们的任务, 同时也不会丢失Activity的状态信息。注意此处,我们可以通过在AndroidManifest文件中<activity>属性节点去设置他的任务栈去改变默认的行为

4. 需要注意的是,一个App中可能不止一个任务栈,某些特殊情况下,单独一个Actvity可以独享一个任务栈。还有一点就是一个Task中的Actvity可以来自不同的App,同一个App的Activity也可能不在一个Task中。

关于启动模式和任务栈比较好的文章: https://blog.csdn.net/javazejian/article/details/52071885

activity的知识点很多,以后还会补充

参考资料: https://developer.android.google.cn/guide/components/activities

猜你喜欢

转载自blog.csdn.net/yang1349day/article/details/81736263