Android Activity的生命周期和基础知识

一点关于Activity的常识

Activity作为APP与用户交互的窗口, 得到了开发者们足够的重视,傲娇的雄踞Android各组件的一哥位置, 没办法, 谁让这是一个看脸的时代呢.

我们平时看到的APP界面, 就是建立在Activity之上的, 比如地图, 邮件, 新闻, 聊天等等的一切活动, 都离不开Activity, 他就好像Android上面的画板, 开发者可以在上面任性发挥.

一款APP通常包含多个Activity, 他们之间可以按照编辑好的逻辑相互调用, 其中每次开启APP第一个启动的Activity是”Main Activity”, Activity们被保存在一个堆栈中, 当在Activity_A上启动Activity_B的时候,Activity_B将会被压入栈顶, 它们遵循后进先出原则, 当Activity_B被关闭的时候, Activity_A将会重新位于栈顶. 更多堆栈相关信息可以看这里.

当一个Activity_A因为Activity_B的启动而停止的时候, 对应的接口会被回调, 这部分将在后面介绍.

创建Activity

创建Activity的时候必须要继承Activity类或者它的子类, 同时在该类中需要重写一些父类中的回调. 当Activity的状态改变的时候, 系统会调用它们. 其中最重要的两个方法是onCreate()和onPause().

onCreate():该方法必须被重写, 当Activity被创建的时候系统将会调用其onCreate方法,这里应该加入Activity的初始化代码. 其中最重要的是setContentView(),它是指定Activity的layout的方法. 也是决定我们Activity颜值的地方, 高颜值的Activity一向倍受青睐.

onPause():当用户将要离开一个Activity的时候, 该方法会被调用, 通常这意味着这个Activity要被销毁了(虽然不总是这样), 所以在这应该处理用户输入的数据, 以防它们丢失.

@Override
protected void onCreate(BundlesavedInstanceState) {
   super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

在Mainifest.xml中声明Activity

在启动Activity之前你还必须在Mainifest.xml中声明它, 要不然系统会不认识它.


<manifest …>
    <application …>
        <activity android:name=".MainActivity" />
…
     </application …>
…
</manifest >

<activity>标签必须包含android:name,该参数指定了Activity的名字. <activity>标签还支持其他参数, 但并不是必须的, 它们可以指定activity的主题, 图标, 标签等内容.欲知详情?

<activity>还可以用<intent-filter>标签指定过滤器, 以指定其它应用如何启动该Activity,当我们使用Android Studio创建项目的时候, 它会自动创建一个Main Activity, 它在Mainifest.xml中长这样:

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

很帅, 啊不对, 很复杂吧? 第一次看到的时候我的内心也是崩溃的…

<action>标签指定了这个Activity是该应用的”main”入口.

<category>标签指定了该应用应该在系统的应用启动列表中.

在所有的Activity中只有”main activity”才能拥有MAIN action和LAUNCHER category. 如果你不打算让自己的Activity被其它应用调用, 那么在Mainifest.xml文件中则不该出现其它的<Intent-filter>. 你可以通过显式的intent来启动它们.

如果该Activity不幸的要被其它的应用调用, 则它必须被指定一个包含<action>标签的<intent-filter>, <category>和<data>标签则是可选的,欲知详情?

启动一个Activity

启动一个Activity有两个方法: 当你不关心Activity的反馈信息的时候, 可以直接使用startActivity(), 当你关注Activity反馈的信息的时候, 需要调用startActivityForResult(), 它可以接收Activity返回的结果.

当需要启动一个Activity的时候, 可以直接调用startActivity(), 它需要一个intent参数. Intent对象需要指定要启动的Activity的类. 我们可以这样创建一个intent对象(假如我们要启动的Activity叫SignInActivity):

Intent intent = new Intent(this, SignInActivity.class);
这样就有了一个intent对象, 启动Activity只需要这样:

startActivity(intent);

然而有的时候你可能希望带点儿数据给刚启动的Activity以防止其不知所措, 那么intent可以帮你达到这个目的, 它不止指明要启动的Activity, 还可以充当邮差的功能, 传递一些数据, 比如你想启动一个聊天的对话框, 希望启动的对话框知道自己正在跟谁聊天, 那么可以在调用startActivity()之前这样:

intent.putExtra("username", "Michael Jordan");

intent的用法还不止于此, 欲知详情?

这样在调用startActivity()之后, 新的Activity就可以启动了.

有的时候会有这样的场景: 我们启动了一个新的Activity, 并且关心这个Activity的执行结果, 或者接收这个Activity返回的数据. 在这种情况我们只需要使用startActivityForResult()代替startActivity(), 然后重写onActivityResult()方法来接收返回的结果即可.当被启动的Activity结束的时候, 就会将带有结果的intent传给onActivityResult()方法.

启动和接收返回的代码是这样:

btnNormalActivity = (Button) findViewById(R.id.btnNormalActivity);
btnNormalActivity.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        startActivityForResult(new Intent(MainActivity.this, NormalActivity.class), NORMAL_ACTIVITY);
    }
});
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == NORMAL_ACTIVITY) {
        Log.i(TAG, "NormalActivity return result:" + resultCode);
        Log.i(TAG, "NormalActivity return userName:" + data.getStringExtra("userName"));
    }
}
被启动的Activity中, 需要用setResult()设置要返回的结果:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_normal);
    Log.i(TAG, "onCreate");

    Intent intent = new Intent();
    intent.putExtra("userName", "YaoMing");
    setResult(RESULT_OK, intent);
}
执行结果:

ResultOfonActivityResult

关闭一个Activity

关闭一个Activity只需要在该Activity内部调用finish()即可, 也可以用finishActivity()直接关闭一个指定的Activity. 多数情况下只需要按返回键即可关闭一个Activity, 系统会自动调用相关的callbacks.

Activity的状态和Activity的生命周期

Activity有三个状态:

Resumed: 唤醒状态. Activity处于前台并且处于激活状态,这时候它可以相应用户的请求. 也可以理解为是”运行”状态. 唤醒状态(或者叫运行状态)就是”可见+激活”.

Paused: 暂停状态. Activity可见, 但是没有拥有用户焦点的时候, 该Activity就处于暂停状态. 比如处于一个透明主题的Activity后面的时候, 透明的Activity拥有用户的焦点. 这时候用户无法点击到处于暂停状态的Activity. 处于这种状态下的Activity在可用内存非常低的时候依然可能被系统回收. 所以暂停状态就是”可见+未激活”.

Stopped: 停止状态. 当一个Activity被另一个Activity完全覆盖并不可见得时候, 它会处于stopped状态. 处于stopped状态下的Activity在系统需要内存的时候可能会被系统回收. 所以Stopped状态就是”不可见+未激活”.

 

当Activity的状态改变的时候, Android会调用Activity的callbacks以让我们处理Activity的状态变化. Activity拥有的callbacks如下:

public class MainActivity extends Activity {

    private final static String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate");
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i(TAG, "onRestart");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy");
    }

}

在上面的代码中, onCreate和onDestroy是成对出现的, 他们代表Activity整个生命周期的开始和结束,

onStart和onStop是成对的, 它们代表Activity是否可见的开始和结束.
而onResume和onPause是成对的, 它们代表Activity是否激活可以相应用户的操作.

当Activity在各种状态之间切换的时候, 调用callbacks的规则如下:

Activity生命周期经典图片

onCreate: 当Activity第一次被创建的时候, 该方法会被调用,onCreate主要负责初始化Activity, 比如界面等. onCreate后总是接onStart方法. 关于该方法接收的参数会在后面介绍.

onRestart: 当Activity不可见之后再次可见时,onRestart方法会被调用, 在onRestart方法之后总是会调用onStart.

onStart: 当Activity变得可见之后, 该方法会被调用. 如果在这之后Activity来到了前台处于被激活可操作状态, onStart之后会调用onResume, 否则会调用onStop.

onResume: 当Activity来到前台, 可以响应用户操作的时候, 该方法会被调用. 此时Activity位于栈顶.

onPause: 当系统即将唤醒另一个Activity的时候, 该方法会被调用, 此时Activity不再响应用户操作. 该方法通常会用来提交或者保存Activity中尚未被保存的数据, 停止动画或者其它占用CPU的活动. 该方法不宜做过多的操作, 因为会影响下一个即将出现的Activity的显示速度, 如果操作过多可能会造成卡顿现象. 如果再次回到前台可以相应用户操作, 那么onPause之后会调用onResume, 如果Activity变得不再可见, 那么在onPause之后会调用onStop方法. 在onPause被调用之后, 系统有可能在内存严重不足的情况下回收该Activity.

onStop: 当前Activity不再可见的时候,onStop方法会被调用. 通常在一个Activity即将关闭或者被其他Activity覆盖之后. onStop方法之后, 如果Activity重新变得可见, 那么会调用onRestart方法, 相反不再可见则会调用onDestroy方法. 在onStop方法被调用之后, 如果系统需要内存, 则很可能会回收该Activity.

onDestroy: 在Activity被销毁之前调用onDestroy,这是Activity会接收到的最后一个方法调用. 通常在Activity调用了finish方法或者Activity被系统回收之后. 在这里应该做好Activity的善后工作, 释放资源等.

保存Activity的状态

当Activity处于onPause或者onStop的状态下时, 系统如果觉得内存不够用了, 会优先回收onStop状态下的Activity, 如果还是不够就考虑拿onPause状态下的Activity开刀收回内存了. 如果用户这时候想返回到该Activity, 那么就会有一些麻烦事, 系统会重建这个Activity, 但是并不能保证重建了之后跟用户之前看到的一模一样. 比如有些自定义的控件.

为了防止这样的现象出现, 我们在弹出新的Activity之前应该保存前一个Activity的状态信息, 以防其跟回收之前看到的不一样. Android提供了这样的接口, 它就是onSaveInstanceState().

以及跟它配对的onRestoreInstanceState()方法.

onSaveInstanceState经典图片

该接口会在系统自动回收Activity之前调用, 并且传入一个Bundle参数, 我们可以使用putString()或者putIntent()这样的保存键值对的方法将需要保存的数据存入Bundle中.

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    String userName = "Michael Jordan";
    outState.putString("userName", userName);
}

Activity被系统回收后再次创建的时候, 如果我们的数据已经保存在了Bundle里面. 那么在Activity重新被创建的时候, onCreate和onRestoreInstanceState方法都会带一个非空的Bundle,即我们保存数据的Bundle.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null) {
        String userName = savedInstanceState.getString("userName");
        Log.i(TAG, userName);
    }
}

另外并非每次Activity将要销毁的时候都会调用onSaveInstanceState, 比如用户点击返回键关闭Activity, 这时候Activity是明确需要关闭销毁的, onSaveInstanceState就不会被调用.

需要特别指出的是当Activity被系统回收又重建的过程中, 默认的情况下Activity会为每一个View控件调用onSaveInstanceState, Activity会自动保存这些数据, 比如EditText或者checkBox, 但是你必须在布局文件中为这些控件指定android:id标签. 通过android:saveEnabled可以设置这个属性, 如果设置为false, 则这些控件的状态不会被自动恢复. 当然, 通常情况下我们不应该去设置这个属性.

onSaveInstanceState和onRestoreInstanceState方法本意是辅助Activity保存其状态, 而不是给我们保存更加持久化的信息, 比如用户资料, 这些数据应该在onPause方法中保存.

测试Activity保存状态能力的最简单的方法是旋转手机(当然还要开启自动旋转的选项), 这时候Android会重新创建Activity以适应屏幕的旋转. onSaveInstanceState方法将会在此时被触发.

处理配置/环境的变化

当配置改变的时候(比如屏幕旋转等), Android会重新创建Activity, onDestroy之后直接调用onCreate方法. 这样可以帮助我们的APP自动适应屏幕的变化, 这时候就需要保存Activity的状态, 以便在屏幕旋转之后Activity还保持在之前的状态, 假如屏幕旋转之后Activity的按钮, 输入框数据发生了变化, 显然体验是很差的.

此时我们应该在onSaveInstanceState和onRestoreInstanceState(或者onCreate)中处理这些变化.

Activity之间的协作

当一个Activity启动另一个Activity的时候, 它们的状态都在发生改变. 第一个Activity会暂停(onPause)和停止(onStop)(如果它已经不可见了的话), 同时另一个Activity会被创建(onCreate), 启动(onStart), 激活/唤醒(onResume). 那么它们的执行顺序是怎样的呢? 这里做一个简单的Demo, 在MainActivity中启动一个NormalActivity, 在各callbacks方法中加入打印, 然后我们可以看到如下结果:

可以看到在NormalActivity启动的时候, MainActivity和NormalActivity的执行顺序是先执行MainActivity的onPause, 然后是NormalActivity的onCreate, onStart和onResume方法. 最后才执行MainActivity的onStop

这样的方法执行顺序我们该如何实现自己的逻辑呢? 由于NormalActivity的启动在MainActivity的onPause方法之后, 那么在onPause方法中我们应该尽量避免执行重量级的操作, 以防止NormalActivity启动速度受到影响. 另外如果MainActivity需要用写数据库或者写文件等方法传递数据给NormalActivity, 那么这个操作应该放到onPause中而不是onStop中, 原因显而易见.

 

参考: http://developer.android.com/guide/components/activities.html

相关的Demo: https://github.com/Harvey-chen/ActivityLifecycle

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值