[TOC]
An activity is a single, focused thing that the user can do.
生命周期
启动Activity的请求会由Instruction来处理,然后通过Binder向AMS发送请求,AMS内部维护着一个ActivityTask并负责栈内的Activity的状态同步,AMS通过ActivityThread去同步Activity的状态从而完成生命周期的调用。
比如说onCreate和onStart是由方法performLaunchActivity()调用。
onResume是由方法handleResumeActivity调用。
系统资源的释放
一般将系统资源的释放放在onPause中,比如Camera、Sensor、Receivers
但是因为onPause是切换前台Activity必须经历的过程,必须旧Activity执行onPause之后,新Activity才能呈现在屏幕上(即使是用singleTop的启动模式,启动相同的Activity,也会执行onPause(),onNewIntent(),onResume()),所以不能在onPause执行耗时的操作。
启动模式
Activity启动模式
launchMode
- standard
- 注意并不是在默认包名的任务栈,是出现在启动它时候的任务栈。比如TaskB中的Activity启动了它,它会继续停留在TaskB中
- singleTop
singleTask
检查当前栈中是否存在需要启动的Activity, 清理栈顶直到此Activity位于栈顶
很有趣的一点,应该网上都没有讲到,栈里分别有ABC三个Activity,A为栈底且为singleTask启动模式,C启动A,则会先调用B的onDestroy,然后再调用C的onPause,启动A,销毁C
1
2
3
4
5
6
705-14 22:35:10.961 12758-12758/com.hgdendi.test D/Main2Activity: ====onDestroy:
05-14 22:35:10.970 12758-12758/com.hgdendi.test D/Main3Activity: ====onPause:
05-14 22:35:10.980 12758-12758/com.hgdendi.test D/MainActivity: onNewIntent:
05-14 22:35:10.983 12758-12758/com.hgdendi.test D/MainActivity: onResume:
05-14 22:35:11.017 12758-12793/com.hgdendi.test D/OpenGLRenderer: endAllActiveAnimators on 0x7cfc1fec00 (RippleDrawable) with handle 0x7cfc0f1d40
05-14 22:35:11.288 12758-12758/com.hgdendi.test D/Main3Activity: ====onStop:
05-14 22:35:11.288 12758-12758/com.hgdendi.test D/Main3Activity: ====onDestroy:可以与TaskAffinity配合使用,而TaskAffinity也主要与singleTask和allowTaskReparenting配合使用
- singleInstance
- 会出现在一个新的任务栈中,且此任务栈中只存在这一个Activity
- 此新的任务栈可被多个应用共享
- 应用于需要与程序分离的界面,如调用紧急呼叫,就是使用这种启动模式
- standard
- clearTaskOnLaunch
- 只对根Activity生效
- 每次返回该TASK,都会将该TASK之上的所有其他Activity清除
- finishOnTaskLaunch
- 当用户离开这个TASK,再返回的时候,该Activity就会被finish掉
- alwaysRetainTaskState
- 只对根Activity生效
- 所在的TASK栈不接受任何清理命令,一直保持Task状态
- allowTaskReparenting
- 在应用退到后台时,能否从启动它的那个Task移动到具有公共affinity的task
- 比如app启动了web浏览器的一个Activity,这个activity此时在app的task上,当app退到后台再进入前台,会发现这个activity已经到了浏览器应用的task中了
IntentFlag启动模式
- NEW_TASK
- Service因为不依附任何栈,所以若从Service中创建新的Activity需要使用此方法
- SINGLE_TOP
- 同single_top
- CLEAR_TOP
- 同single_task
- 但是如果不与SINGLE_TOP同时指定,可能会导致启动的Activity被销毁然后再以该Intent进行重建(而不是直接调用onNewIntent生命周期)
- NO_HISTORY
- 该Activity启动其他Activity后,就消失了
Android任务栈
Android任务栈被称为一个TASK,一个TASK中的Activity可以来自不同的APP,同一个App的Activity也可能不在一个Task中
不同TASK栈之间不能传递数据,只能由Intent传递
- 比如调用startActivityForResult启动一个SingleInstance模式的Activity,会立即返回Activity.RESULT_CANCELED
Intent
显式
隐式
一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity
action
- 存在且必须和任意的一个相同
category
- 0~多个
- 必须各个都匹配
- 在startActivity时系统会加上DEFAULT,故而为了使Activity接收隐式调用,必须指定DEFAULT
Data
要求Intent中必须有data数据,并且能够完全匹配过滤规则中的某一个data
1 | <data android:scheme = "string" |
主要分为URI + MIMETYPE
Mimetype
指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*
可以表示图片、文本、视频等不同的媒体格式
URI
://:/[ ||]
比如content://com.example.project:200/folder/subfolder/etc
Scheme : URI的模式,比如http、file、content。不指定则只支持file和content
Host : URI的主机名,比如www.baidu.com
Port: URI中的端口号,
Path、pathPattern、pathPrefix: 表述路径信息(*前面加两个斜杠,表示单斜杠需要连打四个斜杠)
同时指定Uri和mimetype需要调用setDataAndType方法
1 | intent.setDataAndType(Uri.parse("file://abc"),"text/plain"); |
打开应用市场对App进行评分
1 | public ArrayList<String> queryInstalledMarketPkgs(Context context) { |
内存重启
出现异常时候,会由Activity调用onSaveInstantceState方法,之后再由window调用顶层布局各个View的onSavedInstantceState方法。
原则 View的记录信息由View记录,Fragment的记录信息由Fragment记录。
- intent(Activity)、argument(Fragment)中的信息会被自动保存
- 其他需要持久化保存的信息需要在onSavedInstantceState()中保存
1 | // 自定义View的状态存储一般模式 |
故而带Fragment的Activity的建议写法为
1 | public class ActivityWithFragment{ |
onSavedInstantceState是在onStop之前启动,但是和onPause没有必然联系。
横竖屏切换
横竖屏切换时Activity的生命周期
1、默认情况下,切屏会重新调用各个生命周期
2、设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法(3.2之前还是会调用各个生命周期)
如果横竖屏的界面布局不同,可以在res下新建layout-land和layout-port目录,然后把布局文件扔到这两个目录文件中
Configuration
描述了设备的所有配置信息,会影响到应用程序检索的资源。包括了用户指定的选项(locale和scaling),也包括设备本身的配置(例如input modes,screen size and screen orientation),可以在该类里查看所有影响Configuration Change的属性
常见的引发Configuration Change的属性:
- 横竖屏切换
- android:configChanges=”orientation”
- 键盘可用性
- android:configChanges=”keyboardHidden”
- 屏幕大小变化
- android:configChanges=”screenSize”
- 语言的更改
- android:configChanges=”locale”
可能引发的问题
重走生命流程
界面中用户选择了checkbox和radiobutton选项或者通过网络请求显示在界面上的数据在屏幕旋转后Activity被destroy-recreate,这些空间上被选择的状态和界面上的数据都会消失。
进入某个Activity时加载页面进行网络请求,此时旋转屏幕会重新创建网络请求,这样的用户体验非常不好。
伴随异步操作显示一个progressDialog的话,异步任务未完成去旋转屏幕,程序会因为Activity has leaked window而终止。而当old activity被销毁后,线程执行完毕后还是会把结果返回给old activity而非新的activity
建议要override onSaveInstanceState()方法
onConfigChange()
在onConfigChange()中获取不到新的Layout和控件的尺寸位置信息,必须通过消息异步或者延时调用
不可以动态加载xml,可能会有很多硬代码
Activity过渡动画
提供了三种Transition类型
- 进入
- 决定Activity中的所有的视图怎么进入屏幕
- 退出
- 决定一个Activity中的所有视图怎么退出屏幕
- 共享元素
- 决定两个Activities之间的过渡,怎么共享他们的视图
进入、退出
- explode
- 从屏幕中间进或出,移动视图
- slide
- 滑动,从屏幕边缘进或出,移动视图
- fade
- 淡出,通过改变屏幕上的视图的不透明度达到添加或者移除视图
共享元素
- changeBounds
- 改变目标视图的布局边界
- changeClipBounds
- 裁剪目标视图边界
- changeTransform
- 改变目标视图的缩放比例和旋转角度
- changeImageTransform
- 改变目标图片的大小和缩放比例
代码
普通动画
1 | //ActivityA |
淡出效果
1 | 在ActivityA和ActivityB中同时定义 |
定义在Theme中
1 | <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> |