今天是国庆的最后一天,在这里先祝大家国庆过的happy呀。国庆过后,我们又要开始全新的搬砖生活了,那么就让我们扬帆起航,开始新的征程吧。
谈起android,大家脑子里反映出来的第一个组件是什么呢?没错,就是今天的主题activity。在网上有很多篇博客关于activity的,但是我看来很多都是片段式的,所以很多时候总有一种让人看罢不能的感觉,所以这几天正好有时间我就把activity的知识点梳理了一遍,和大家一起讨论讨论,欢迎大家在下面留言哈。
这篇博客主要讲解的知识点有以下几个要点:
1》 activity的生命周期
2》 activity的启动模式
3》IntentFilter的匹配原则
4》从源码的角度来分析activity的启动
1.activity的生命周期
这张图相信大家都不会陌生吧,没错,这正是activity的生命周期。但是,大家都知道,因为手机的内存限制,导致系统分配给每个app的起始内存都不会很大,当你在手机后台开了N个进程后,就会常常出现一种问题:即app被系统杀死。那么这个时候的app的生命周期是怎么样的呢?后面我们再讨论。
我们先来了解一下activity的正常情况下的生命周期:
activity的生命周期常见的方法有:
onCreate():表示activity正在被创建,可以做一些初始化的工作。例如我们常见的setContentView()加载布局资源
onStart():表示activity正在被启动,即将开始,注意这个时候activity已经处于可见的状态,但是还没有出现在前台
onResume():表示activity已经被启动并位于前台,可以开始活动了
onPause():表示activity正在停止,注意onPause必须先执行完,新activity的onResume才会执行
onStop():表示activity已经停止,可以做一些重量级的回收工作
onDestory():表示activity即将被销毁,可以做最终的资源释放
onRestart():表示activity由后台转入前台(即由停止的状态转入活动状态)
onSaveInstanceState():activity被杀死时执行的方法,用来存储一些临时数据
onRestoreInstanceState():与onSaveInstanceState()配套使用,用来获取被临时存储的数据
上面的这些方法基本涵盖了activity的全部的生命周期,下面我们就来详细分析一下具体情况下的生命周期:
1.1》 针对一个特定的activity第一次启动:onCreate()----->onStart()----->onResume()
1.2》 切换到桌面(按Home键):执行当前activity的onPause()----->onStop()
1.3》 打开新的activity:执行当前activity的onPause()----->执行要跳转的activity的onCreate()----->onStart()----->onResume()----->执行当前activity的onStop()
1.4》当前activity回退:执行当前activity的onPause()----->执行回退activity的onRestart()----->onStart()----->onResume()----->执行当前activity的onStop()----->onDestroy()
1.5》从后台返回:执行当前activity的onRestart()----->onStart()----->onResume
以上的情况基本涵盖了activity的大部分情况,有没有很简单,当然,实际情况远不止这些,我只是把常见的情况列举出来供大家参考而已。
异常情况下的生命周期
在实际的开发过程中,我们很容易忽略异常情况下的生命周期,但是在实际的开发过程中,我们又很容易碰到app被系统自动杀死的情况,所以掌握异常情况下的生命周期也是很有必要的,它有助于你写出高质量的代码,那么下面我们就一起来看看异常情况下的生命周期到底是什么样子的。
其实异常情况下的生命周期和正常情况下的生命周期相比,只是多了两个方法:onSaveInstanceState()和onRestoreInstanceState(),在这里我着重讲解两种:
1> activity的横竖屏切换:
10-08 13:05:45.933 6586-6586/com.example.zhoufan.androidactivity I/zf: NormalActivity --- > onPause
10-08 13:05:45.979 6586-6586/com.example.zhoufan.androidactivity I/zf: NormalActivity --- > onStop
10-08 13:05:45.985 6586-6586/com.example.zhoufan.androidactivity I/zf: NormalActivity --- > onDestroy
10-08 13:05:46.017 6586-6586/com.example.zhoufan.androidactivity I/zf: NormalActivity --- > onCreate
10-08 13:05:46.019 6586-6586/com.example.zhoufan.androidactivity I/zf: NormalActivity --- > onStart
10-08 13:05:46.025 6586-6586/com.example.zhoufan.androidactivity I/zf: NormalActivity --- > onRestoreInstanceState
10-08 13:05:46.034 6586-6586/com.example.zhoufan.androidactivity I/zf: NormalActivity --- > onResume
仔细观察不难发现,在进行横竖屏切换的时候,activity所奉行的原则是先销毁后创建,即先销毁当前activity,然后重新创建一个全新的activity,只不过在重新创建activity的时候多了一个onRestoreInstanceState(),这里有的童靴就会有一个疑问了,onSaveInstanceState方法不是应该也会执行的吗?为什么没有出现呢?其实对于这个问题我也感觉很疑惑,所以希望明白这个问题的童靴在下面留言给我,不胜感激~~~~
2> 内存不足导致的activity杀死,老规矩,用事实来说明
10-08 13:19:15.845 17341-17341/? I/zf: NormalActivity --- > onCreate
10-08 13:19:15.848 17341-17341/? I/zf: NormalActivity --- > onStart
10-08 13:19:15.851 17341-17341/? I/zf: NormalActivity --- > onRestoreInstanceState
10-08 13:19:15.854 17341-17341/? I/zf: NormalActivity --- > onResume
在这里我只打印了当app被系统杀死后重新运行后的生命周期,有没有觉得和上面的很像。其实这也好理解,当app被系统杀死后,会执行onPause()----->onStop()----->onDestory()方法,然后在重新打开app,那这里我就不废话了,相信大家后面的都懂。看到这里,有些好奇的童靴又要问了,那有什么方法来尽量避免activity重新创建呢?哈哈哈哈,刚开始我也和大部分的童靴一样,有着相同的疑问,既然android开发团队知道这个问题,那肯定也有办法来解决这个问题呀。对的,下面我就来讲讲如何解决这个问题。
解决这个问题时我们先需要理解一个概念:configChanges(中文意思:配置改变)
当我们需要由于某项配置发生改变时执行对应的操作时,我们就需要用到这个参数,使用它很简单:
<activity android:name=".NormalActivity"
android:configChanges="screenSize|orientation"/>
那么android中系统为我们提供了哪些系统配置呢?在这里我不进行 一 一 讲解,我着重挑选几个比较重要的进行说明
1》locale:设备的本地位置发生改变,一般指切换了系统语言
2》orientation:屏幕方向发生了改变
3》screenSize:当屏幕的尺寸信息发生改变,当旋转设备屏幕时,屏幕尺寸会发生变化
具体情况:在系统配置中有很多内容,当某项内容发生改变时(横竖屏切换属于发生改变的一种),我们不想系统重新创建activity,可以给activity指定configChanges属性,需要注意的是如果你只是单纯的只指定
android:configChanges="orientation" 是没有任何效果的,正确的写法是
android:configChanges="screenSize|orientation"。当然你也可以在activity里面重写
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.i(TAG,"NormalActivity --- > onConfigurationChanged"+newConfig.orientation);
}
那么在进行横竖屏切换的时候只会执行该方法,其他的任何方法都不会执行。
2.activity的启动模式
activity的启动模式相信大家也不陌生了,在这里我就简单的讲解下,因为我觉得其实activity的启动模式只要你理解了就会觉得很容易,但是我们在理解启动模式之前必须先了解一个概念:堆和栈(具体可以看我另外一篇博客,http://blog.youkuaiyun.com/qq_27970997/article/details/78184427)
1》 标准模式:这是系统默认也是实际开发中最常用的一种启动模式,写法很简单:android:launchMode="standard",即每次启动一个activity的时候系统都会创建一个新的activity实例并置于栈顶(无论在该栈中是否已经存在该activity实例)
2》栈顶复用模式(singleTop):在这种模式下,如果创建的activity已经位于栈顶的话,那么系统不会再重新创建实例,而是复用该activity的实例,即系统会调用该activity的onNewIntent()方法
注意:只会执行onNewIntent()和onResume()方法,onCreate()和onStart()方法并不会执行
3》栈内复用模式(singleTask):这是一种单实例模式。即如果在栈内没有该实例,则创建该实例并压入栈内,而如果在栈内存在该实例,则会移除该实例上面的所有实例并将该实例置于前台,当然,它执行的方法有onNewIntent(),onStart(),onResume()方法
4》singleInstance:单实例模式,即在一个栈内只允许一个实例的存在(不做过多的讲解,有兴趣的童靴可以自己查找资料看看)
在这里大家需要注意的一个词就是栈:在默认情况下,所有activity所需的任务栈就是应用的包名,所以大部分情况下一个APP程序只有一个栈。
3.IntentFilter的匹配规则
启动activity分为两种,显示调用和隐式调用。显示调用比较简单,这里就不再陈述,免的大家嫌我啰嗦。这里着重讲解一下隐式调用。那么隐式调用是通过什么方法来找到目标activity的呢?这里大家就需要了解另外一个词了:IntentFilter(过滤器),顾名思义就是对Intent进行筛选,找到适合自己的那个Intent并进行响应。那么在IntentFilter中需要匹配的有哪些呢?
IntentFilter中的过滤信息有action,category,data。为了匹配过滤信息,需要同时匹配过滤列表中的action,category,data信息,否则匹配失败。这里需要注意以下几点:
1》activity中允许有多组<IntentFilter></IntentFilter>的存在,且当多组中有一组匹配成功也可以启动该activity
2》匹配成功需要action,category,data同时匹配成功
那么action,category,data的匹配规则又是什么呢?
1) action: 匹配要求是Intent中的action存在且必须和过滤规则中的一个action写法完全一样,区分大小写
2) category: 当启动activity没有设置category时,目标activity必须设置category
<category android:name="android.intent.category.DEFAULT"/>否则匹配不成功
当启动activity设置category时,不管有几个category,它都要求在目标activity的Intent中找到对应的category
3) data: 相比较前面的两种,data的匹配规则稍微复杂一点。和action相似,如果过滤规则中定义了data,那么Intent中必须也要定义可匹配的data。data由两部分组成:mimeType和URI.
mimeType指媒体类型,比如image/*,audio/*,video/*
URI结构:<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
例如:content://com.example.project:200/folders/etc
http://www.baidu.com:80/search/info
下面来具体分析URI结构:
3.1》 scheme:URI的模式,比如http,file,content,如果URI中没有指定scheme,那么整个URI的其他参数无效,即URI无效。
3.2》host:URI的主机名,如果host未指定,那么整个URI的其他参数无效,即URI无效。
3.3》port:URI中的端口号,仅当URI中指定了scheme和host参数时port才有意义。
3.4》path表示完整的路径信息;pathPattern也表示完整的路径信息,但是它里面可以包含通配符"*","*"表示0个或者多个任意字符。
注意:在进行data匹配时,系统允许只有mimeType而没有URI结构,但是如果有URI结构则必须保证匹配后的字符一致,否则会出现匹配失败的情况。
当然,光说不练是假把式,下面我就把我写的一个很简单例子的关键代码贴出来
Intent intent = new Intent();
intent.setAction("android.intent.action.a");
intent.addCategory("com.example.zhoufan.a");
intent.setType("text/*");
startActivity(intent);
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="android.intent.action.a" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.zhoufan.a"/>
<data android:mimeType="text/*" />
</intent-filter>
</activity>
有木有很简单,当然app跳转的时候也是可以允许带值过去的
Intent intent = getPackageManager().getLaunchIntentForPackage("你要跳转到App的包名");
if (intent != null){
intent.putExtra("name", "xiaodong");
intent.putExtra("sex", "nan");
startActivity(intent);
}else {
Toast.makeText(MainActivity.this, "Intent为空!!", Toast.LENGTH_SHORT).show();
}
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
if (bundle != null){
name = bundle.getString("name");
sex = bundle.getString("sex");
}
很简单是不是

