初识Activity:
我们都知道android中有四大组件(Activity活动,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收者),Activity是我们用的最多的,也是最为基本的组件,因为应用的所有操作都与用户相关,Activity提供窗口来和用户进行交互。
官方文档这么说:
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View).
大概意思:activity是一个独立的,能够单独处理用户操作的组件,几乎所有的activity都是用来和用户做交互的,所以activity类会创建一个窗口,开发者可以通过setContent(View)的接口方法把UI放到activity组件上。
Android中activity全都归属于task管理。task:是一个activity的集合,这些activity按照启动的顺序排队存入一个栈(即"back stack")。android默认会为每一个App维持一个task来存放该app的所有的activity,task的默认name为该app的packagename。
Activity的内部调用过程:
上面已经讲过了,系统通过堆栈来管理activity,当一个新的activity开始时,它被放置在堆栈的顶部和成为运行活动,以前的activity始终保持低于它在堆栈的位置(也就是说当前活动的activity位于栈顶,以前使用过的activity位于栈低)而不会再一次到达前台,直到新的活动退出。
下面分析Activity的生命周期过程:
- ** 启动Activity:** onCreate() -> onstart() -> onResume(),Activity进入运行状态。
- Activity退居后台:当前Activity转到新的Activity界面或者按手机Home键回到主屏:onPause() -> onStop(),进入停滞状态。
- Activity从后台返回前台:onRestart() -> onStart() -> onResume(),再次回到运行状态。
- Activity退居后台,且系统内存不足时,系统会杀掉这个后台转态的Activity(此时这个Activity引用仍然处在任务栈中,只是这个时候引用指向的对象已经为null),若再次回到这个Activity,则会走onCreate() -> onStart() -> onResume()(将重新走一次Activity的初始化生命周期)。
- 锁屏:onPause() -> onStop()
- 解锁:onStart() -> onResume()
- Activity的完整生存期会在onCreate()调用和onDestroy()调用之间发生。
- Activity的可见生存期会在onStart()调用和onStop()调用之间发生。系统会在activity的整个生存期内多次调用onStart()和onStop(),因为activity可能会在显示和隐藏之间不断的来回切换。
- Activity的前后台切换会在onResume()调用和onPause()调用之间发生。因为这个状态可能会经常发生转换,为了避免切换迟缓引起的用户等待,这两个方法中的代码应该相当地轻量化。
启动模式是什么?为什么要定义启动模式呢?
简单的说启动模式就是定义activity实例与task的关联方式。
定义启动模式是为了实现一些默认启动(standard)模式之外的需求:
1.让某个activity启动一个崭新的task(而不是被放入到当前的task中)
2.让activity启动时只是调出已有的某个实例(而不是在back stack栈顶创建一个崭新的实例)
3.或者,你想在用户离开task时,只保留根activity,而back stack中的其他activity都要清空。
以上的这些需求,就是启动模式,所起到的作用。
怎样定义启动模式?
定义启动模式的方法有两种:
1.在manifest 文件中,通过Acitivty的xml标签来改变任务栈的默认行为(启动模式):
- 使用
android:launchMode="standard|singleInstance|singleTask|singleTop"
来控制Activity任务栈。在 manifest 文件中activity声明时,利用 activity 元素的 launchMode 属性来设定 activity 与 task 的关系。
** 任务栈 是一种后进先出的结构。位于栈顶的Activity处于焦点状态,当按下back按钮的时候,栈内的Activity会一个一个的出栈,并且调用其onDestory()方法。如果栈内没有Activity,那么系统就会回收这个栈,每个App默认只有一个栈,以App的包名来命名。
** <1>.standard : 标准模式,每次启动Activity都会创建一个新的Activity实例,并且将其压入任务栈栈顶,而不管这个Activity是否已经存在。Activity的启动三回调(onCreate() -> onStart() -> onResume()都会执行)。
** <2>.singleTop :** 栈顶复用模式,这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,所以它的启动三回调就不会执行,同时Activity的onNewIntent()方法会被回调,如果Activity已经存在但是不在栈顶,那么作用与standard模式一样。
** <3>.singleTask:** 栈内复用模式,创建这样的Activity的时候,系统会先确认它所需任务栈已经创建,否则先创建任务栈,然后放入Activity,如果栈中已经有一个Activity实例,那么这个Activity就会被调到栈顶,onNewIntent(),并且singleTask会清理在当前Activity尚明的所有Activity。(clear top)
** <4>.singleInstance :**加强版的singleTask模式,这种模式的Activity只能单独位于一个任务栈内,由于栈内复用的特性,后续请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了。
Activity的堆栈管理以ActivityRecord为单位,所有的ActivityRecord都放在一个List里面,可以认为一个ActivityRecord就是一个Activity栈。
2.使用 Intent 标志
在要启动activity时,你可以在传给startActivity()的intent中包含相应标志,以修改activity与task的默认关系。
代码如下:
Intent i = new Intent(this,NewActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);```
还可以通过标志修改默认模式,如下所示:
* **FLAG_ACTIVITY_NEW_TASK**
与“singleTask”模式相同,在新的task中启动activity。如果要启动的activity已经运行于某个task中,则那个task将调入前台。
* **FLAG_ACTIVITY_SINGLE_TOP**
与“singleTop”模式相同,如果要启动的activity位于back stack栈顶,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。
* **FLAG_ACTIVITY_CLEAR_TOP**
此种模式在launchMode中没有对应的属性值。如果要启动的activity已经在当前task中运行,则不再启动一个崭新的实例,且所有在其上面的activity将被销毁。
**关于启动模式的一些建议:**
一般不要改变activity和task默认的工作方式。如果你确定有必要修改默认方式时,请保持谨慎,并确保activity在启动和从其他activity返回时的可用性,多做测试和安全方面的工作。
#####Activity缓存方法:
**情景再现:**有a,b两个Activity,当从a进入b之后一段时间,可能系统会把a回收,这时候按back键,执行的不是a的onRestart(),而是onCreate()方法,a被重新创建一次,这时a种的临时数据和状态可能丢失了。
**解决办法:**可以用Activity的 onSaveInstanceState()回调方法保存临时的数据和状态,这个方法一定会在活动被回收之前调用。方法中有一个Bundle参数,putString(),putInt()等方法需要传入两个参数,一个键一个值。数据保存之后会在onCreate()中恢复,onCreate也有一个Bundle类型的参数。
**实例代码:**
```@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这里,当Acivity第一次被创建的时候为空
//所以我们需要判断一下
if( savedInstanceState != null ){
savedInstanceState.getString("anAnt");
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("anAnt","Android");
}```
* **onSaveInstanceState(Bundle outState)**
当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按Back键的时候。
注意上面的双引号,何为“容易”?言下之意就是该activity还没有被销毁,而仅仅是一种可能性。这种可能性有哪些呢?下面,我们通过重新一个activity的所有的生命周期的onXxx()方法,包括onSaveInstanceState()和onRestoreInstanceSatae()方法,我们可以清楚地知道,当某个activity(假定为activity A)显示在当前task的最上层时,其onSaveInstanceState()方法会在什么时候被执行,有这么几种情况:
**1.当用户按下Home键时。**
这中情况是用户显而易见的操作,系统不知道你按下Home键后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下的几种情况得分析都遵循该原则。
**2.长按Home键,选择运行其他的程序时。**
**3.按下电源键(关闭屏幕显示)时。**
**4.从activity A中启动一个新的activity时。**
**5.屏幕方向切换时,例如从竖屏切换到横屏时。**(如果不指定configchange属性)在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState()一定会被执行。
**总而言之,onSaveInstanceState()的调用遵循一个重要原则**,即当系统“未经过许可”时销毁了你的activity,则onSaveInstanceState()会被系统调用,这是系统的责任。因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随你了)。
**另外,需要注意一下几点:**
**<注意点 1>:**布局中的每一个View默认实现了onSaveInstanceState()方法,这样的话,这个UI的任何改变都会自动地存储和在activity重新创建的时候自动地恢复。但是这种情况只有在你的这个UI提供唯一的ID之后才起作用,如果没有提供ID,app将不会存储它的状态。
**<注意点 2>:**由于默认的onSaveInstanceState()方法的实现帮助UI存储它的状态,所以如果你需要覆盖这个方法去存储额外的转态信息,你应该在执行任何代码之前都调用父类的onSaveInstanceState()方法(super.onSaveInstanceState())。既然有现成的可用,那么我们到底还要不要自己实现onSaveInstanceState()?这就得看情况了,如果你自己的派生类中有变量影响到UI,或影响到你的程序的行为,当然就要把这个变量也保存了,那么就需要自己去实现了,否则就不需要了。
**<注意点 3>:**由于onSaveInstanceState()方法调用的不正确性,你应该子使用这个方法去记录activity的瞬间状态(UI状态)。不应该用这个方法去存储持久化数据。当用户离开这个activity的时候应该在onPause()方法中存储持久化数据(例如应该被存储到数据库中的数据)。
**<注意点 4>:**onSaveInsatnceState()如果被调用,这个方法会在onStop()前被触发,但是系统并不保证是否在onPause()之前或者之后触发。
* **onRestoreInstanceState(Bundle outState)**
至于onRestoreInstanceState()方法,需要注意的是,**onSaveInstanceState()方法和onRestoreInstanceState()方法“不一定”是成对被调用的。(通过代码实践得到的结论,并不一定会两个方法成对被调用)**
**onRestoreInstanceState()被调用的前提是**:activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示的activity A的时候,用户按下Home键回到主界面,然后用户紧接着又返回到activty A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState()方法不会被执行。
**另外,onRestoreInstanceState()方法的bundle参数也会传递到onCreate()方法中**,你也可以选择在onCreate()方法中做数据的还原。还有onRestoreInstanceState()在onStart()方法之后执行。至于这两个函数的使用,给出示范代码(注:要特别留意自定义代码在调用super的前或后):
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.在super() 前 些自己的自定义的代码
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// etc.在super() 后 些自己的自定义的代码
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}
#####Fragment的生命周期和Activity之间的关系:
先上一张Fragment和Activity生命周期综合图:
![Fragment和Activity生命周期综合图.jpg](http://upload-images.jianshu.io/upload_images/3416944-baf47cfa0f0a4132.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
* **为什么程序要在Service中创建子线程,而不是Activity中创建呢?**
这是因为Activty很难对Thread进行控制,当Activity被销毁之后,就没有任何其他的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,在另一个Activity中是无法对其进行操作的。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中的Binder的实例了。因此,使用Service来处理后台任务,Activity就可以放心的finish(),完全不需要担心无法对后台任务进行控制的情况了。
* **Intent的使用方法,可以传递哪些数据类型。**
通过查询Intent/Bundle的API文档,我们可以获知,Intent/Bundle支持传递基本类型的数据和基本类型的数组数据,以及String/CharSequence类型的数据和String/CharSequence类型的数组数据。而对于其它类型的数据貌似无能为力,其实不然,我们可以在Intent/Bundle的API中看到Intent/Bundle还可以传递Parceable(包裹化,邮包)和Serializable(序列化)类型的数据,以及它们的数据/列表数据。
所以要让非基本类型的和非String/CharSequence类型的数据通过Intent/Bundle来进行传输,我们需要在数据类型中实现Parceable接口或是实现Serializable接口。
[http://blog.csdn.net/kkk0526/article/details/7214247](http://blog.csdn.net/kkk0526/article/details/7214247)
#####解析Intent Filter:
android的3个核心组件---Activity,Services,Braodcast Receiver---是通过Intent传递消息的。Intent消息用于在运行时绑定不同的组件。在Android的AndroidMainfest.xml配置文件中可以通过intent-filter节点为一个Activity指定其Intent Filter,以便于告诉该Activity可以响应什么类型的Intent。
* intent-filter的三大属性:
**Action :**一个Intent Filter可以包含多个Action,Action列表用于标志Activity所能接受的“动作”,它是一个用户自定义的字符串。
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<action android:name="com.wang.app" />
……
</intent-filter>```
也可以在代码中使用以下语句便可以启动该Intent对象:
Intent i=new Intent();
i.setAction("com.wang.app");```
Action列表中包含了"com.wang.app"的Activity都将会匹配成功。
**URL :**在inter-filter节点中,通过data节点匹配外部数据,也就是通过URL携带外部数据给目标组件。
<data android:mimeType="mimeType"
android:scheme="scheme"
android:host="host"
android:port="port"
android:path="path"/>```
注意:只有data的所有的属性都匹配成功时,URL数据匹配才会成功。
Category :为组件定义一个类别列表,当Intent中包含这个类别列表的所有项目时,才会匹配成功。
<intent-filter . . . >
<action android:name="code.android.intent.action.MAIN" />
<category android:name="code.android.intent.category.LAUNCHER" />
</intent-filter>```
**Activity中Intent Filter的匹配过程:**
(1)加载所有的Intent Filter列表
(2)去掉action匹配失败的Intent Filter
(3)去掉url匹配失败的Intent Filter
(4)去掉Category匹配失败的Intent Filter
(5)判断剩下的Intent Filter数目是否为0,如果为0查找失败返回异常;如果大于0,就按优先级排序,返回最高优先级的Intent Filter。
#####开发中Activity的一些问题:
一般设置Activity为非公开的
<activity
......
android:exported="false" /> ```
注意:非公开的Activity不能设置intent-filter,以免被其他的activity唤醒(如果拥有相同的intent-filter)。
- 不要指定activity的taskAffinity属性。
- 不要设置activity的LaunchMode(保持默认),注意Activity的intent最好也不要设定为FLAG_ACTIVITY_NEW_TASK
- 在匿名内部类中使用this时,加上activity类名(类名.this,不一定是当前的activity)
- 设置activity全屏:在其onCreate()方法中加入代码:
// 设置全屏模式
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 去除标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);```
**结束语:我害怕,自己在最该努力的时光中,却选择了安逸。 ---王令
QQ:2585085940
邮箱:wang91ling@163.com
欢迎大家光临寒舍。**