【Android练级之路】Activity


大纲

在这里插入图片描述


1. 生命周期

Activity的生命周期可以分为两部分内容,一部分是典型情况下的生命周期,另一部分是异常情况下的生命周期。所谓典型情况下的生命周期,是指在有用户参与的情况下,Activity所经过的生命周期的改变;而异常情况下的生命周期是指Activity被系统回收或者由于当前设备的Configuration发生改变从而导致Activity被销毁重建,异常情况下的生命周期的关注点和典型情况下略有不同。

1.1 典型情况下的生命周期

在这里插入图片描述

  • onCreate():表示Activity正在被创建,这个方法可以做一些初始化工作,比如调用setContentView去加载界面布局资源、初始化Activity所需数据等。
  • onRestart():表示Activity正在重新启动。一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart就会被调用。这种情形一般是用户行为所导致的,比如用户按Home键切换到桌面或者用户打开了一个新的Activity,这时当前的Activity就会暂停,也就是onPause和onStop被执行了,接着用户又回到了这个Activity,就会出现这种情况。
  • onStart():表示Activity正在被启动,即将开始,这时Activity 已经可见了,但是还没有出现在前台,还无法和用户交互。这个时候其实可以理解为Activity已经显示出来了,但是我们还看不到。
  • onResume():表示Activity已经可见了,并且出现在前台并开始活动。要注意这个和onStart的对比,onStart和onResume都表示Activity已经可见,但是onStart的时候Activity还在后台,onResume的时候Activity才显示到前台
  • onPause():表示Activity正在停止,正常情况下,紧接着onStop就会被调用。此时可以做一些存储数据、停止动画等工作,但是注意不能太耗时,因为这会影响到新Activity的显示,onPause必须先执行完,新Activity的onResume才会执行。
  • onStop():表示Activity即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。
  • onDestroy():表示Activity即将被销毁,这是Activity生命周期中的最后一个回调,在这里,我们可以做一些回收工作和最终的资源释放。

此外,有几种特殊情况需要补充说明:

  1. 针对一个特定的Activity,第一次启动,回调如下:onCreate -> onStart ->onResume
  2. 当用户打开新的Activity或者切换到桌面的时候,回调如下:onPause ->onStop。这里有一种特殊情况,如果新Activity采用了透明主题,那么当前Activity不会回调onStop
  3. 当用户再次回到原Activity时,回调如下:onRestart -> onStart ->onResume
  4. 当用户按back键回退时,回调如下:onPause -> onStop -> onDestroy
  5. 当Activity被系统回收后再次打开,生命周期方法回调过程和(1)一样,注意只是生命周期方法一样,不代表所有过程都一样

1.2 异常情况下的生命周期

1.2.1 资源相关的系统配置发生改变导致Activity被杀死并重新创建

当前Activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了改变,在默认情况下,Activity就会被销毁并且重新创建,当然我们也可以阻止系统重新创建我们的Activity。在默认情况下,如果我们的Activity不做特殊处理,那么当系统配置发生改变后,Activity就会被销毁并重新创建,其生命周期下图所示。
在这里插入图片描述
由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState()来保存当前Activity的状态。需要强调的一点是, 这个方法只会出现在Activity被异常终止的情况下,正常情况下系统不会回调这个方法。 当Activity被重新创建后,系统会调用onRestoreInstanceState(),并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。因此,我们可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState的调用时机在onStart之后。

1.2.2 资源内存不足导致低优先级的Activity被杀死

Activity按照优先级从高到低,可以分为如下三种:

  • 前台Activity——正在和用户交互的Activity,优先级最高。
  • 可见但非前台Activity——比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互。
  • 后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低。

当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数数据。

1.3 onSaveInstanceState()方法

当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用。用来保存状态,调用在onStop之前,但和onPause没有时序关系。onSaveInstanceState()适用于对临时性状态的保存,而onPause()适用于对数据的持久化保存

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // 保存数据
    savedInstanceState.putString("data", "这是保存的数据");
    super.onSaveInstanceState(savedInstanceState);
}
  • 什么时候调用
    • 当用户按下HOME键时。
    • 长按HOME键,选择运行其他的程序时。
    • 按下电源按键(关闭屏幕显示)时。
    • 从activity A中启动一个新的activity时
    • 屏幕方向切换时,例如从竖屏切换到横屏时(与android:configChanges属性相关联)

configChanges中有很多内容,如果当某项内容发生改变后,我们不想系统重新创建Activity,可以给Activity指定configChanges属性。比如不想让Activity在屏幕旋转的时候重新创建,就可以在AndroidManifest种给Activity添加configChanges属性添加orientation这个值.
如果我们想指定多个值,可以用“|”连接起来,比如android:configChanges="orientation|keyboardHidden"。系统配置中所含的项目是非常多的,下面介绍每个项目的含义,如下表所示。
如果我们没有在Activity的configChanges属性中指定该选项的话,当配置发生改变后就会导致Activity重新创建。
在这里插入图片描述

1.4 onRestoreInstanceState()方法

可以简单地理解,系统只在Activity异常终止的时候才会调用onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,其他情况不会触发这个过程。onSaveInstanceState 和 onRestoreInstanceState 并不是一定成双出现的,终于当 Activity 真正的被销毁的时候才会执行 onRestoreInstanceState()。

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

    // 恢复数据
    if (savedInstanceState != null) {
        String data = savedInstanceState.getString("data");
    }
}

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

    // 也可以在 onRestoreInstanceState() 方法中恢复数据
    if (savedInstanceState != null) {
        String data = savedInstanceState.getString("data");
    }
}

2. 启动模式

2.1 Activity的LaunchMode

  • Standard:系统默认,每次启动都会创建一个新的Activity实例
  • SingleTop:栈顶复用,如果处于栈顶,直接调用,非栈顶则创建新的Activity实例
  • SingleTask:栈内复用,如果存在栈内,则在其上所有Activity全部出栈,使得其位于栈顶,生命周期和SingleTop一样,app首页基本是用这
  • SingleInstance:这个是SingleTask加强本,系统会为要启动的Activity单独开一个栈,这个栈里只有它

2.2 Activity的Flag

Activity的Flags有很多,这里主要分析一些比较常用的标记位。标记位的作用很多,有的标记位可以设定Activity的启动模式,比如FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_SINGLE_TOP等;还有的标记位可以影响Activity的运行状态,比如FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS等。下面主要介绍几个比较常用的标记位,剩下的标记位读者可以查看官方文档去了解,大部分情况下,我们不需要为Activity指定标记位,因此,对于标记位理解即可。在使用标记位的时候,要注意有些标记位是系统内部使用的,应用程序不需要去手动设置这些标记位以防出现问题。

  • FLAG_ACTIVITY_NEW_TASK
    这个标记位的作用是为Activity指定“singleTask”启动模式,其效果和在XML中指定该启动模式相同。
  • FLAG_ACTIVITY_SINGLE_TOP
    这个标记位的作用是为Activity指定“singleTop”启动模式,其效果和在XML中指定该启动模式相同。
  • FLAG_ACTIVITY_CLEAR_TOP
    具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个模式一般需要和FLAG_ACTIVITY_NEW_TASK配合使用,在这种情况下,被启动Activity的实例如果已经存在,那么系统就会调用它的onNewIntent。如果被启动的Activity采用standard模式启动,那么它连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈顶。singleTask启动模式默认就具有此标记位的效果。
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    具有这个标记的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。它等同于在XML中指定Activity的属性android:excludeFromRecents=“true”。


3.页面跳转

3.1 startActivity()

3.2 startActivityForResult()

AActivity ➡ BActivity

AActivity:

	@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //...
    int resultCode = 1;
    startActivityForResult(new Intent(this, BActivity.class), resultCode);
}

/**
 * 接收 OtherActivity 返回的数据
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    String result = data.getExtras().getString("result");
}

BActivity:

public void feekBack(){
    Intent intent = new Intent();
    // 把返回数据存入 Intent
    intent.putExtra("result", "我是返回数据");
    // 设置返回数据
    setResult(RESULT_OK, intent);
    // 关闭当前 Activity
    finish();
}

3.3 URL Scheme

Android 中的 Scheme 是一种页面内跳转协议,是一种非常好的实现机制。通过定义自己的 Scheme 协议,可以非常方便跳转 App 中的各个页面。

使用场景:

  • 通过小程序,利用 Scheme 协议打开原生 App。
  • H5 页面点击锚点,根据锚点具体跳转路径 App 端跳转具体的页面。
  • App 端收到服务器端下发的 Push 通知栏消息,根据消息的点击跳转路径跳转相关页面。
  • App 根据URL跳转到另外一个 App 指定页面。
  • 通过短信息中的 URL 打开原生 App。

Scheme 路径的规则:

<scheme> :// <host> : <port> [<path>|<pathPrefix>|<pathPattern>]

设置 Scheme
在 AndroidManifest.xml 中对标签增加设置 Scheme。

<activity
    android:name=".ui.activity.SchemeActivity"
    android:screenOrientation="portrait">
    <!--Android 接收外部跳转过滤器-->
    <!--要想在别的 App 上能成功调起 App,必须添加 intent 过滤器-->
    <intent-filter>
        <!--协议部分配置,注意需要跟 web 配置相同-->
        <!--协议部分,随便设置 aa://bb:1024/from?type=meetliang-->
        <data android:scheme="aa"
            android:host="bb"
            android:port="1024"
            android:path="/from"/>
        <!--下面这几行也必须得设置-->
        <category android:name="android.intent.category.DEFAULT" />
        <!--表示 Activity 允许通过网络浏览器启动,以显示链接方式引用,如图像或电子邮件-->
        <category android:name="android.intent.category.BROWSABLE" />
        <action android:name="android.intent.action.VIEW" />
    </intent-filter>
</activity>

原生调用:

Uri uri = Uri.parse("aa://bb:1024/from?type=meetliang");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

网页调用:

<a href="aa://bb:1024/from?type=meetliang">打开 App</a>

在 SchemeActivity 中可以处理 Scheme 跳转的参数:

public class SchemeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Uri uri = getIntent().getData();
        if (uri != null) {
            //获取指定参数值
            String type = uri.getQueryParameter("type");
            Log.e("SchemeActivity", "type:" + type);

            if(type.equals("meetliang")){
                ActivityUtils.startActivity(XXXActivity.class);
            }else if(type.equals("main")){
                ActivityUtils.startActivity(MainActivity.class);
            }
        }
        finish();
    }
}

如何判断一个 Scheme 是否有效:

PackageManager packageManager = getPackageManager();
Uri uri = Uri.parse("aa://bb:1024/from?type=meetliang");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isValid = !activities.isEmpty();
if (isValid) {
    startActivity(intent);
}

4. Activity、Windows、View

  • Activity是安卓四大组件之一,负责界面展示、用户交互与业务逻辑处理,但是无法处理显示什么控件(view)的,是通过PhoneWindow进行显示的;
  • Window就是负责界面展示以及交互的职能部门,就相当于Activity的下属,Activity的生命周期方法负责业务的处理;
  • View就是放在Window容器的元素,Window是View的载体,View是Window的具体展示。

在这里插入图片描述


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值