为了账号安全,请及时绑定邮箱和手机立即绑定

Android Activity要点(1)

标签:
Android

这几天一直在看“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)

重写之

@Overridepublic 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”模式



作者:leavesC


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
34
获赞与收藏
119

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消