在Android开发中承担的主要角色
1、App的入口
你要想用一个app肯定要打开这个app才能使用,主Activity是用户点击这个app之后第一个启动的Activity,这个Activity启动后才能与用户有其他更多的交互,就像是Java程序的入口函数main一样,main函数的执行才能使整个应用跑起来,所以Activity是app的入口。所有的程序都有入口,找到入口才能慢慢的解析整个程序的运行过程。
2、显示和控制用户的逻辑界面
所有写给用户使用的程序必然会设计一个好的交互界面,用户在这个界面进行功能选择和使用,在Android系统中,承担起与用户交互的界面是由View来呈现的,但是View的显示是靠Activity来控制。
所以Activity总是承担着显示用户界面和控制用户操作逻辑的角色。
生命周期
程序都是有生命周期的,目前程序在什么生命周期就做该生命周期内的事,别做超越生命周期范围内的事,就像是人生一样,是什么阶段就做什么阶段该做的事,不多做,也别少做。Android系统已经给Activity的生命周期定义很多回调函数,你需要做的就是熟悉这些回调函数,知道在执行这个回调函数的时候Activity的状态和需要在这个状态下进行什么操作。
public class ExampleActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The activity is being created.
}
@Override
protected void onStart() {
super.onStart();
// The activity is about to become visible.
}
@Override
protected void onResume() {
super.onResume();
// The activity has become visible (it is now "resumed").
}
@Override
protected void onPause() {
super.onPause();
// Another activity is taking focus (this activity is about to be "paused").
}
@Override
protected void onStop() {
super.onStop();
// The activity is no longer visible (it is now "stopped")
}
@Override
protected void onDestroy() {
super.onDestroy();
// The activity is about to be destroyed.
}
}
下图是Google官方的Activity生命周期图,已经非常详尽的说明个Activity生命周期之间的跳转和哪种情况下Activity会被kill掉。
创建生命周期
- onCreate:设置Activity要呈现的View,创建Activity需要的资源。
- onDestory:释放onCreat中创建的资源。
可见生命周期
- onStart :Activity 开始变得可见。
- onStop : Activity 开始变得不可见。onPause中不能进行耗时操作,会影响到新Activity的显示。因为onPause必须执行完,新的Activity的onResume才会执行。
- onRestart :如果一个Activity在onStop状态,开始变得可见的时候调用。
可交互生命周期
- onResume: Activity可以获取焦点并与用户交互。
- onPause : Activity丢失焦点,不再与用户交互。
以下是摘自<Google官方文档>:
名为“是否能事后终止?”的列表示系统是否能在不执行另一行 Activity 代码的情况下,在方法返回后随时终止承载 Activity 的进程。 有三个方法带有“是”标记:(onPause()、onStop() 和 onDestroy())。由于 onPause() 是这三个方法中的第一个,因此 Activity 创建后,onPause() 必定成为最后调用的方法,然后才能终止进程 — 如果系统在紧急情况下必须恢复内存,则可能不会调用 onStop() 和 onDestroy()。因此,您应该使用 onPause() 向存储设备写入至关重要的持久性数据(例如用户编辑)。不过,您应该对 onPause() 调用期间必须保留的信息有所选择,因为该方法中的任何阻止过程都会妨碍向下一个 Activity 的转变并拖慢用户体验。
在是否能在事后终止?列中标记为“否”的方法可从系统调用它们的一刻起防止承载 Activity 的进程被终止。 因此,在从 onPause() 返回的时间到 onResume() 被调用的时间,系统可以终止 Activity。在 onPause() 被再次调用并返回前,将无法再次终止 Activity。
注:无法保证系统会在销毁您的 Activity 前调用 onSaveInstanceState(),因为存在不需要保存状态的情况(例如用户使用“返回”按钮离开您的 Activity 时,因为用户的行为是在显式关闭 Activity)。 如果系统调用 onSaveInstanceState(),它会在调用 onStop() 之前,并且可能会在调用 onPause() 之前进行调用。
注:由于无法保证系统会调用 onSaveInstanceState(),因此您只应利用它来记录 Activity 的瞬态(UI 的状态)— 切勿使用它来存储持久性数据,而应使用 onPause() 在用户离开 Activity 后存储持久性数据(例如应保存到数据库的数据)。
您只需旋转设备,让屏幕方向发生变化,就能有效地测试您的应用的状态恢复能力。 当屏幕方向变化时,系统会销毁并重建 Activity,以便应用可供新屏幕配置使用的备用资源。 单凭这一理由,您的 Activity 在重建时能否完全恢复其状态就显得非常重要,因为用户在使用应用时经常需要旋转屏幕。
启动模式
任务栈:任务是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。设备主屏幕是大多数任务的起点。
standard 标准模式
每次都会创建新的Activity;
singleTop 栈顶复用
当创建的Activity是栈顶时,它的启动类型是singleTop的那么不会新建Activity,而是调用onNewIntent方法,如果不是栈顶则会新建。
singleTask 栈内复用
该模式是一种单例模式,即一个栈内只有一个该Activity实例。该模式,可以通过在AndroidManifest文件的Activity中指定该Activity需要加载到那个栈中,即singleTask的Activity可以指定想要加载的目标栈。singleTask和taskAffinity配合使用,指定开启的Activity加入到哪个栈中。
执行逻辑:
- 在这种模式下,如果Activity指定的栈不存在,则创建一个栈,并把创建的Activity压入栈内。
- 如果Activity指定的栈存在,如果其中没有该Activity实例,则会创建Activity并压入栈顶,如果其中有该Activity实例,则把该Activity实例之上的Activity杀死清除出栈,重用并让该Activity实例处在栈顶,然后调用onNewIntent()方法。
应用场景:
大多数App的主页。对于大部分应用,当我们在主界面点击回退按钮的时候都是退出应用,那么当我们第一次进入主界面之后,主界面位于栈底,以后不管我们打开了多少个Activity,只要我们再次回到主界面,都应该使用将主界面Activity上所有的Activity移除的方式来让主界面Activity处于栈顶,而不是往栈顶新加一个主界面Activity的实例,通过这种方式能够保证退出应用时所有的Activity都能报销毁。在跨应用Intent传递时,如果系统中不存在singleTask Activity的实例,那么将创建一个新的Task,然后创建SingleTask Activity的实例,将其放入新的Task中。
singleInstance 单例模式
作为栈内复用模式(singleTask)的加强版,打开该Activity时,直接创建一个新的任务栈,并创建该Activity实例放入新栈中。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例。
应用场景:
呼叫来电界面。这种模式的使用情况比较罕见,在Launcher中可能使用。或者你确定你需要使Activity只有一个实例。建议谨慎使用。
开发的场景
1、从A启动B的流程:
Activity A 和 Activity B
- 调用A的onPause方法,A失去焦点不可交互;
- 调用B的onCreate,onStart,onResume,现在B处于前台可交互;
- 如果A在屏幕上变得不可见,则调用A的onStop;
2、关于用户配置信息的保存:
要测试应用能否在保持应用状态完好的情况下自行重启,您应该在应用中执行各种任务时调用配置变更(例如,更改屏幕方向)。 您的应用应该能够在不丢失用户数据或状态的情况下随时重启,以便处理如下事件:配置发生变化,或者用户收到来电并在应用进程被销毁很久之后返回到应用。
但是,您可能会遇到这种情况:重启应用并恢复大量数据不仅成本高昂,而且给用户留下糟糕的使用体验。 在这种情况下,您有两个其他选择:
在配置变更期间保留对象
允许 Activity 在配置变更时重启,但是要将有状态对象传递给 Activity 的新实例。利用Fragment实现。当 Android 系统因配置变更而关闭 Activity 时,不会销毁您已标记为要保留的 Activity 的片段。 您可以将此类片段添加到 Activity 以保留有状态的对象。
- 扩展 Fragment 类并声明对有状态对象的引用。
- 在创建片段后调用 setRetainInstance(boolean)。
- 将片段添加到 Activity。
- 重启 Activity 后,使用 FragmentManager 检索片段。
public class RetainedFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
注意:尽管您可以存储任何对象,但是切勿传递与 Activity 绑定的对象,例如,Drawable、Adapter、View 或其他任何与 Context 关联的对象。否则,它将泄漏原始 Activity 实例的所有视图和资源。 (泄漏资源意味着应用将继续持有这些资源,但是无法对其进行垃圾回收,因此可能会丢失大量内存。)
public class MyActivity extends Activity {
private RetainedFragment dataFragment;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
// create the fragment and data the first time
if (dataFragment == null) {
// add the fragment
dataFragment = new DataFragment();
fm.beginTransaction().add(dataFragment, “data”).commit();
// load the data from the web
dataFragment.setData(loadMyData());
}
// the data is available in dataFragment.getData()
...
}
@Override
public void onDestroy() {
super.onDestroy();
// store the data in the fragment
dataFragment.setData(collectMyLoadedData());
}
}
在此示例中,onCreate() 添加了一个片段或恢复了对它的引用。此外,onCreate() 还将有状态的对象存储在片段实例内部。onDestroy() 对所保留的片段实例内的有状态对象进行更新。
自行处理配置变更
阻止系统在某些配置变更期间重启 Activity,但要在配置确实发生变化时接收回调,这样,您就能够根据需要手动更新 Activity。利用 android:configChanges 和onConfigurationChanged() 实现。
注:自行处理配置变更可能导致备用资源的使用更为困难,因为系统不会为您自动应用这些资源。 只能在您必须避免 Activity 因配置变更而重启这一万般无奈的情况下,才考虑采用自行处理配置变更这种方法,而且对于大多数应用并不建议使用此方法。
3、onSaveInstanceState和 onRestoreInstanceState(Bundle)
Activity异常情况下(系统资源紧张,横竖屏切换)会被调用用来保存和恢复Activity状态,调用的时序:onSavInstanceState->onStop(If called, this method will occur before onStop(). There are no guarantees about whether it will occur before or after onPause()),onStart->onRestoreInstanceState->onResume.
其中onCreate和onRestoreInstanceState方法来恢复Activity的状态的区别:onRestoreInstanceState回调则表明其中Bundle对象非空,不用加非空判断。onCreate需要非空判断。建议使用onRestoreInstanceState。
横竖屏切换的生命周期:onPause()->onSaveInstanceState()-> onStop()->onDestroy()->onCreate()->onStart()->onRestoreInstanceState->onResume()
可以通过在AndroidManifest文件的Activity中指定如下属性:
android:configChanges = "orientation| screenSize"
来避免横竖屏切换时,Activity的销毁和重建,会回调了下面的方法:
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
4、前后台栈交互
调用SingleTask模式的后台任务栈中的Activity,会把整个栈的Actvity压入当前栈的栈顶。singleTask会具有clearTop特性,会把之上的栈内Activity清除。
前台栈T1中有Activity A,B,后台栈T2中有 singleTask标志的Activity C,D,当从B启动D时会合并这两个栈成为:A B C D,此时从D开始回退,回退的顺序则是D,C,B,A。
当从B启动C时会合并为A,B,C,D会被清除。
Activity的Flags很多,这里介绍集中常用的,用于设定Activity的启动模式。可以在启动Activity时,通过Intent的addFlags()方法设置。
(1)FLAG_ACTIVITY_NEW_TASK
其效果与指定Activity为singleTask模式一致。
(2)FLAG_ACTIVITY_SINGLE_TOP
其效果与指定Activity为singleTop模式一致。
(3)FLAG_ACTIVITY_CLEAR_TOP
具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。如果和singleTask模式一起出现,若被启动的Activity已经存在栈中,则清除其之上的Activity,并调用该Activity的onNewIntent方法。如果被启动的Activity采用standard模式,那么该Activity连同之上的所有Activity出栈,然后创建新的Activity实例并压入栈中。
5、是否所有的数据保存都应该在onPause中执行,并且onPause会一定被执行吗?
onPause的执行概率会大于onStop,所以在onPause中执行保存最好,但是要快,免得影响程序的执行效率。onPause不是一定会被执行,在关机的时候不会,所以保险起见数据在操作之后最好就保存,而不是非要等到onPause。