Android Activity要点(1)

这几天一直在看“Android开发艺术探索”这本书,从中学到了一些很不错的知识,这篇博文就当做我的读书笔记吧

Activity是安卓四大组件之一,也是使用最频繁的组件,这里就来记录下在使用Activity的过程中应该注意的一些小细节

一、Activity的生命周期

  • "onStart与onResume"、"onPause与onStop"在描述上来看都是类似的,当Activity开始创建调用了onCreate方法后,会依次调用"onStart与onResume",当Activity切换时到另一个界面时,会依次调用"onPause与onStop" 那么,它们之间有什么不同呢?可不可以只保留其中之一呢? 其实,onStart与onResume的差异在于要启动的Activity是否可见,两者都是Activity从创建到显示在前台这之间被调用的,不过onStart方法被调用时Activity还在后台,onResume被调用时表示Activity已经可见并开始活动了 onPause与onStop在用户切换Activity或者打开一个Dialog时调用。当用户直接切换到一个非透明Activity时,onPause与onStop会依次调用,如果下一个Activity是透明主题,或者打开的是一个Dialog(只遮挡了一部分当前Activity的界面)时,只会调用onPause,而不会调用onResume
  • 当前台Activity A切换到Activity B时,两者之间的回调函数调用顺序是怎样的呢?

首先新建一个MainActivity,重写其各个回调函数,添加一句Log输出语句,类似如下所示

    private final String TAG = "MainActivity";
    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart被调用");
    }

再在主布局文件中添加一个按钮,用于启动第二个Activity——Main2Activity 也重写Main2Activity的各大回调函数,与MainActivity类似

    private final String TAG = "第二个Activity";
     @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart被调用");
    }

启动MainActivity,输出语句如下:

com.czy.myapplication E/MainActivity: onCreate被调用
com.czy.myapplication E/MainActivity: onStart被调用
com.czy.myapplication E/MainActivity: onResume被调用

点击按钮切换Activity

com.czy.myapplication E/MainActivity: onPause被调用
com.czy.myapplication E/第二个Activity: onCreate被调用
com.czy.myapplication E/第二个Activity: onStart被调用
com.czy.myapplication E/第二个Activity: onResume被调用
com.czy.myapplication E/MainActivity: onStop被调用

点击back键回退到MainActivity

com.czy.myapplication E/第二个Activity: onPause被调用
com.czy.myapplication E/MainActivity: onRestart被调用
com.czy.myapplication E/MainActivity: onStart被调用
com.czy.myapplication E/MainActivity: onResume被调用
com.czy.myapplication E/第二个Activity: onStop被调用
com.czy.myapplication E/第二个Activity: onDestroy被调用

可以看出来,当启动新的Activity或回退到前一个Activity时,执行顺序是:之前的Activity的onPause方法被调用——要切换到的Activity的onResume方法被调用——之前的Activity的onStop方法被调用 因此,原Activity的onPause不能用于执行耗时操作,否则会影响新的Activity的显示


二、Activity异常情况下的数据保存

  1. 当Android系统资源相关的配置发生变化(如横竖屏切换)时,默认情况下当前Activity就会被销毁并重新创建 此时Activity是由于异常情况而销毁的,所以系统会调用如下方法来保存当前数据
protected void onSaveInstanceState(Bundle outState)

即我们可以向Bundle中存入需要恢复的数据 当Activity被重新创建后,以下方法会被自动调用

protected void onRestoreInstanceState(Bundle savedInstanceState)

该Bundle就是在onSaveInstanceState中所使用的,我们可以从中取出数据,用于恢复Activity的状态

此外,该Bundle同时也会传递给

protected void onCreate(Bundle savedInstanceState)

因此我们可以选择在两个方法其中之一来恢复数据,一般是在onRestoreInstanceState(Bundle savedInstanceState)当中执行恢复操作比较合理 且onRestoreInstanceState方法只在Bundle不为空时才会调用,而在onCreate方法中还需要再次判断Bundle的值

例如,如果当前Activity中有一个EditText,当屏幕发生旋转时,可以将其文本内容存入Bundle中,当重新创建Activity时再将其从Bundle取出并显示在EditText当中

@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        EditText editText = (EditText) findViewById(R.id.editText);
        if (!TextUtils.isEmpty(editText.getText())) {
            outState.putString("TextContent", editText.getText().toString());
        }
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        String content = savedInstanceState.getString("TextContent", "");
        EditText editText = (EditText) findViewById(R.id.editText);
        editText.setText(content);
    }

View本身也包含有该两个方法,某些View的数据保存与恢复操作系统已经自动为我们完成了,这也是我们旋转屏幕重新创建Activity后某些状态依然一样的原因

从Activity第一次创建到旋转屏幕,各回调函数的执行顺序如下所示:

com.czy.myapplication E/MainActivity: onCreate被调用
com.czy.myapplication E/MainActivity: onStart被调用
com.czy.myapplication E/MainActivity: onResume被调用

com.czy.myapplication E/MainActivity: onPause被调用
com.czy.myapplication E/MainActivity: onSaveInstanceState被调用
com.czy.myapplication E/MainActivity: onStop被调用
com.czy.myapplication E/MainActivity: onDestroy被调用

com.czy.myapplication E/MainActivity: onCreate被调用
com.czy.myapplication E/MainActivity: onStart被调用
com.czy.myapplication E/MainActivity: onRestoreInstanceState被调用
com.czy.myapplication E/MainActivity: onResume被调用

2 . 禁止因系统配置改变而重新创建Activity 如果不想在系统配置被改变后而导致Activity重新创建,可以在Activity声明时为之指定configChanges属性

android:configChanges="orientation|screenSize"

这样,当屏幕旋转时Activity就不会重新创建也不会去进行数据保存和数据恢复操作了,此时系统会改为去调用以下方法

public void onConfigurationChanged(Configuration newConfig)

重写之

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    Log.e(TAG, "onConfigurationChanged被调用,屏幕方向:" + newConfig.orientation);
}

这样,当多次旋转屏幕时,onSaveInstanceState和onRestoreInstanceState均不会被调用,调用的只是onConfigurationChanged方法,且Activity并没有销毁重建

com.czy.myapplication E/MainActivity: onCreate被调用
com.czy.myapplication E/MainActivity: onStart被调用
com.czy.myapplication E/MainActivity: onResume被调用
com.czy.myapplication E/MainActivity: onConfigurationChanged被调用,屏幕方向:2
com.czy.myapplication E/MainActivity: onConfigurationChanged被调用,屏幕方向:1
com.czy.myapplication E/MainActivity: onConfigurationChanged被调用,屏幕方向:2
com.czy.myapplication E/MainActivity: onConfigurationChanged被调用,屏幕方向:1

三、Activity的启动模式

Android共有四种Activity启动模式,分别为:standard、singleTop、singleTask、singleInstance Activity位于任务栈当中,栈是一种“后进先出”的结构,当点击back键时,栈顶的Activity会一一出栈,直到栈为空后退出程序 Activity的默认栈名为包名,可以通过taskAffinity属性来设置,该属性必须包含有包名分隔符“.”,而不能仅仅是一个单词

    <activity
        android:name=".AnotherActivity"
        android:launchMode="singleTask"
        android:taskAffinity="com.custom.task" />

taskAffinity属性主要和singleTask模式和allowTaskReparenting属性配合使用,在其他情况下没有作用

(1) standard 此为标准模式,即Activity的默认启动模式 在该启动模式下,每启动一个Activity,不管该Activity是否已存在,均会创建一个新的Activity实例,并将该Activity压入启动它的Acivity所在的任务栈的栈顶(singleInstance模式除外),不管该栈的栈名和新启动的Activity在AndroidManifest中指定的taskAffinity属性是否相同

(2) singleTop 此为栈顶复用模式 如果要启动的Activity已位于任务栈的栈顶,则此Activity不会被重新创建 不过,此时该Activity的以下方法会被调用

protected void onNewIntent(Intent intent)

可以通过该intent来传输数据 例如,在启动Activity时,向intent传入文本信息

    public void startAnotherActivity(View view) {
        Intent intent = new Intent(this, AnotherActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("Content", "要传输的内容");
        intent.putExtras(bundle);
        startActivity(intent);
    }

然后在onNewIntent方法中再来获取数据

@Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.e(TAG, "onNewIntent被调用");
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            String text = bundle.getString("Content", "默认文本");
            Toast.makeText(AnotherActivity.this, text, Toast.LENGTH_SHORT).show();
        }
    }

需要注意的是:如果要启动的Activity并不位于栈顶,则会创建该Activity实例,且onNewIntent方法不会被调用

(3) singleTask 即栈内复用模式 这是一种单实例模式,在该模式下,只要Activity存在于栈中,不管是否位于栈顶,均不会重新创建Activity实例,且系统也会回调其onNewIntent方法 如果该Activity存在但不位于栈顶,则系统会弹出该Activity之上的所有Activity,将该Activity置于栈顶

(4)singleInstance 即单实例模式 该模式与singleTask模式类似,不过该模式下的Activity只能单独地位于一个任务栈中 例如,如果Activity A是singleInstance模式,当A第一次启动时,系统会为之创建一个任务栈,然后将A单独置于该栈中,除非A被销毁了,否则之后每次启动A,都不会重新创建实例

指定启动模式有两种方法: 1.在AndroidManifest中直接为Activity指定启动模式

    <activity
        android:name=".AnotherActivity"
        android:launchMode="singleTask"
        android:taskAffinity="com.custom.task" />

2.在Intent中设置标志位Flags来为Activity指定启动模式

    Intent intent = new Intent(this, AnotherActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    startActivity(intent);

在优先级上,第二种方式的优先级要高于第一种,如果两者同时存在,则以第二种为准 两者在限定范围上有所差别,第一种方式无法设定“Intent.FLAG_ACTIVITY_CLEAR_TOP”模式,第二种方式无法设定“singleInstance”模式

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏腾讯Bugly的专栏

《Android基础:Fragment,看这篇就够了》

| 导语 Fragment作为Android最基本,最重要的基础概念之一,在开发中经常会和他打交道。本文从为什么出现Fragment开始,介绍了Fragment...

1K4
来自专栏Java学习网

Android中Context用法详解学习

Android中Context用法详解学习 本文我们一起来探讨一下关于Android中Context的作用以及Context的详细用法,这对我们学习Andro...

2945
来自专栏天天P图攻城狮

Android基础:Fragment,看这篇就够了

Fragment 作为 Android 最基本,最重要的基础概念之一。本文从为什么出现 Fragment 开始,介绍了相关的方方面面。

2.2K9
来自专栏程序员互动联盟

【Android基础】Fragment 详解之Fragment生命周期

Fragment的主要功能就是创建一个View,并且有一个生命周期来管理这个View的创建和销毁。Fragment的生命周期与Activity的生命周期类似,都...

3468
来自专栏Android 研究

Android Handler机制4之Looper与Handler简介

要理解Handler的消息机制,就不得不说Handler/Looper/Message/MessageQueue/Message这四4个类,下面我们先大概了解下...

803
来自专栏云加头条

Android 基础:Fragment,看这篇就够了 (上)

本文从为什么出现Fragment开始,介绍了Fragment相关的方方面面,包括Fragment的基本定义及使用、回退栈的内部实现、Fragment通信、Dia...

2.1K4
来自专栏程序员互动联盟

【Android基础】Fragment 详解之Fragment介绍

Fragment在Android 3.0( API 11)引入,是为了支持在大屏上显示更加动态、灵活的UI,比如在平板和电视上。Fragment可以看作是嵌套的...

3508
来自专栏7号代码

Android应用界面开发——Fragment(实现图书详情界面)

Fragment代表了Activity的子模块,因此可以把Fragment理解成Activity片段。

1102
来自专栏封碎

Android获取应用程序的大小 博客分类: Android AndroidOSF#Security

       今天碰到个问题,想获取某个已安装的包的大小,没找到合适的方法。搜索了一下,发现PackageManager里面有个getPackageSizeIn...

812
来自专栏图像识别与深度学习

《Android》Lesson08-Activity的生命周期

1988

扫码关注云+社区