Android四大组件之Activity
一、创建Activity
- 定义java类,继承Activity
- 在清单文件中配置Activity标签
- 设为启动的activity,需要在activity标签下配置
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
- 多个activity可以配置启动标签,每配置一个,系统就会创建一个快捷图标
二、Activity跳转
Activity的跳转需要创建Intent对象,通过设置intent对象的参数指定要跳转Activity
显式意图
- 通过设置Activity的包名和类名实现跳转,称为显式意图
- 跳转至同一项目下的另一个Activity,直接指定该Activity的字节码即可
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
startActivity(intent);
- 跳转至其他应用中的Activity,需要指定该应用的包名和该Activity的类名
Intent intent = new Intent();
//启动系统自带的拨号器应用
//这个不一定能启动(启动其他应用都是使用隐式启动)
intent.setClassName("com.android.dialer", "com.android.dialer.DialtactsActivity");
startActivity(intent);
- 跳转传递数据
把数据封装到intent对象中,数据可以是八大基本数据类型,字符串和它们的数组,两个序列化接口,还有Bundle对象
放在intent中
intent.putExtra("name","张三")
intent.putExtra("age",10)
val name = intent.getStringExtra("name")
val age = intent.getIntExtra("age", 0)
放在Bundle中
val bundle = Bundle()
bundle.putString("", "")
intent.putExtras(bundle)
intent.extras?.getString("","")
隐式意图
- 通过指定动作实现跳转,称为隐式意图。系统会在所有应用中寻找匹配的intent-filter,找到则启动,找不到则异常
- 在清单文件中的activity标签下,配置标签,设置action和category
- intent-filter下的标签
- action:指定动作(可以自定义,可以使用系统自带的)
- data:指定数据(操作什么内容)
- scheme
- mimetype
- category:类别 (默认类别,机顶盒,车载电脑)
- category必须在清单文件中指定,因为使用intent隐式启动时,默认是设置了intent.addCategory(Intent.CATEGORY_DEFAULT)
- 隐式意图启动Activity,需要为intent设置以上三个属性,且值必须与该Activity在清单文件中对三个属性的定义匹配
- intent-filter节点及其子节点都可以同时定义多个,隐式启动时只需与任意一个匹配即可
//隐式启动拨号界面
val intent = Intent()
intent.action = Intent.ACTION_DIAL
intent.addCategory(Intent.CATEGORY_DEFAULT)
startActivity(intent)
- 有多个action时,匹配一个就可以了,如果有多个data,但是有的data下还有mimetype,这时需要匹配mimetype才行。同时有data和type时,需要使用setDataAndType
val intent = Intent()
intent.action = "a.b.c"
//匹配schema
intent.data = Uri.parse("wzh2:ppp")
//匹配mimetype
intent.type = "text/haha"
//同时有data和mimetype时,不能使用上面的设置,会互相清除,需要调用下面的方法
intent.setDataAndType( Uri.parse("wzh:ppp"),"text/haha")
startActivity(intent)
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="a.b.c"/>
<data android:scheme="wzh"/>
<data android:scheme="wzh2" android:mimeType="text/haha"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
- 跳转传递数据
在清单文件中设置data标签,传递数据
<data android:scheme="wzh2" android:mimeType="text/haha"/>
在目标的activity中获取数据
val uri = intent.data
println("uri:${uri}")//uri:wzh2:xxx
应用场景
- 在同一应用中的activity,使用显式
- 启动不同应用中的activity,使用隐式,虽然显式也可以,但是一定要使用隐式,因为可以多个选择
- 显式的效率高于隐式(隐式启动,底层是遍历整个系统中的清单文件,找出对应的activity)
三、Activity的生命周期
生命周期方法
- onCreate():Activity已经被创建完毕
- onStart():Activity已经显示在屏幕,但没有得到焦点
- onResume():Activity得到焦点,可以与用户交互
- onPause():Activity失去焦点,无法再与用户交互,但依然可见
- onStop():Activity不可见,进入后台
- onDestroy():Activity被销毁
- onRestart():Activity从不可见变成可见时会执行此方法
手机内存不足时,会杀死之前启动的进程,按照LRU算法(最近最少使用)杀死进程
完整生命周期(entire lifetime)
onCreate–>onStart–>onResume–>onPause–>onStop–>onDestory
可视生命周期(visible lifetime)
onStart–>onResume–>onPause–>onStop
前台生命周期(foreground lifetime)
onResume–>onPause
四、任务和返回栈
task
点击最近任务列表按钮,出现的一个个「app」,其实是一个个的task,activity在任务栈中可以堆叠,并且每个任务栈也是可以进行堆叠的。同一应用也有可能出现多个不同的task。不同task堆叠时,顶部的task称为前台task,下面的称为后台task,点击home键或者最近任务列表时,此时堆叠的task会进行分离成不同的task(前台和后台的task分开)。在最近任务列表看见的task未必是「活的」,看不见的task也未必是「死的」(singleInstance的taskAffinity冲突时,只显示一个task,另一个还活着,被隐藏了)
launchMode
-
standard
每启动一个activity就会创建一个新的activity实例。在不同应用的情况下,启动新的activity会被放入当前task中,与另一个应用互不干扰 -
singleTop
被标记singleTop的activity实例如果存在,启动时会复用此activity,调用onNewIntent(),并移动到栈顶。不存在则会被创建
应用场景:浏览器的书签 -
singeTask
在同一应用中,如果activity被设置为singleTask,不存在,会创建一个实例,下次启动时复用该实例,同时堆叠在上面的activity会被移除。如果此时的taskAffinity与启动该activity的activity不同时,则会加入它所声明的taskAffinity的task中(可以是新的task,也可以是其他应用的task,具体看taskAffinity的定义)。
在不同的应用启动该activity时,连该应用的task都堆叠到当前task上面。
存在该实例,再次启动,会调用onNewIntent() -
singleInstance
启动模式非常特殊, activity会运行在自己(新的)的task里面,并且这个task里面只有一个实例存在。如果内存中没有此实例,则会创建新的任务栈,并且创建此activity实例,这个栈移至前台。整个系统中只有一个实例,存在该实例,再次启动,会调用onNewIntent()
应用场景:打电话界面
allowTaskReparenting
如果不使用singletask,而是使用allowTaskReparenting=true时,比如在A应用中打开B应用的allowTaskReparenting=true的activity,这时会进入A的task,并且在栈顶,然后回到桌面,点击B应用,会发现这个activity会跳回到B应用的栈顶,点击A应用时,这个activity消失了。
但是这个属性要慎用,有一些版本失效了
taskAffinity
任务的相关性,默认情况下,所有的activity的taskAffinity都是application中的taskAffinity,默认是当前包名。每个 Task 也都有它的 taskAffinity,它的值取自栈底 Activity 的 taskAffinity;我们可以通过 AndroidManifest.xml 来定制 taskAffinity,但在默认情况下,一个 App 里所有的 Task 的 taskAffinity 都是一样的,就是这个 App 的包名。当我们启动一个新的 Task 的时候,它的值就是它所启动的第一个 Activity 的 taskAffinity。当我们继续从已经打开的 Activity 再打开新的 Activity 的时候,taskAffinity 就会被忽略了,新的 Activity 会直接入栈,不管它来自哪。taskAffinity在launchmode为singletask或者singleinstance时才有意义。不同的taskAffinity在最近任务列表中以不同的task进行显示,根据taskAffinity入栈,没有则创建task
五、横竖屏切换
默认情况下 ,横竖屏切换, 销毁当前的activity,重新创建一个新的activity。之所以这样设计,是为了切换横竖屏的布局
禁用横竖屏切换的生命周期
- 将屏幕的方向固定
1.清单文件中配置
android:screenOrientation="landscape"
android:screenOrientation="portrait"
2.代码中设置
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- 切换方向时,不处理生命周期,不会更换布局文件
android:configChanges="orientation|screenSize|keyboardHidden"
六、Activity销毁时传递数据
-
开启activity并且获取返回值
startActivityForResult(intent, 0);
-
在新开启的界面里面实现设置数据的逻辑
Intent data = new Intent(); data.putExtra("phone", phone); //设置一个结果数据,数据会返回给调用者 setResult(0, data); finish();//关闭掉当前的activity,才会返回数据
-
在开启者activity里面实现方法
//通过data获取返回的数据 onActivityResult(int requestCode, int resultCode, Intent data)
-
通过判断请求码和结果码确定返回值的作用
- 请求码是区分哪个activity启动的
- 结果码是判断传递过来的是什么数据