一点关于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);
}
执行结果:
关闭一个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的规则如下:
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()方法.
该接口会在系统自动回收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