Activity: 负责用户界面的展示和用户交互,学习Activity就要学习Fragment,虽然它不是四大组件之一,但是它在我们的开发工作中也是频频被使用到,且必须和Activity一块使用,常用于分模块开发,比如慕课首页的几个tab,每个tab都是对应着一个Fragment
图片和部分文字转载自https://www.songyubao.com/book/primary/activity/Activity.html
1.Activity 生命周期
方法 | 作用 |
---|---|
onCreate() | 该方法会在 Activity 第一次创建时进行调用,在这个方法中通常会做 Activity 初始化相关的操作,例如:加载布局、绑定事件等。 |
onStart() | 这个方法会在 Activity 由不可见变为可见的时候调用,但是还不能和用户进行交互。 |
onResume() | 表示Activity已经启动完成,进入到了前台,可以同用户进行交互了。 |
onPause() | 这个方法。可以在这里释放系统资源,动画的停止,不宜在此做耗时操作。 发生场景1.在系统准备去启动另一个 Activity 的时候调用 2.按下返回键 3.从前台切换到后台 |
onStop() | 当Activity不可见的时候回调此方法。需要在这里释放全部用户使用不到的资源。可以做较重量级的工作,如对注册广播的解注册,对一些状态数据的存储。此时Activity还不会被销毁掉,而是保持在内存中,但随时都会被回收。通常发生在启动另一个Activity或切换到后台时 发生场景和onPause()相同 |
onDestroy() | Activity即将被销毁。此时必须主动释放掉所有占用的资源。 |
onRestart() | 这个方法在 Activity 由停止状态变为运行状态之前调用,也就是 Activity 被重新启动了(APP切到后台会进入onStop(), 再切换到前台时会触发onRestart()方法) |
按home键 : 运行中状态 转变 开始 执行onPause()
onStop()
返回键: onPause()
—> onResume()
回到这个Activity : 从onStop()
转变 开始执行 onRestart()
—> onStart()
—>onResume()
2.Activity组件创建
class SecondActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
Tips:
选择复写只有一个参数的onCreate方法
3.Activity组件注册
四大组件需要在AndroidManifest文件中配置否则无法使用,类似Activity无法启动,
一般情况下: 在新建一个activity后,为了使intent可以调用此活动,我们要在androidManifest.xml文件中添加一个标签,标签的一般格式如下:
<activity android:name=".SecondActivity"
android:exported="true">
<intent-filter>
<!--自定义 action时一般包名.action.全大写类名-->
<action android:name="com.example.componentLearn.action.SECONDACTIVITY"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
- android:name是对应Activity的类名称
- android:exported 是否支持其它应用调用当前组件。默认值:如果包含有intent-filter 默认值为true; 没有intent-filter默认值为false。
- android:label是Activity标题栏显示的内容. 现已不推荐使用
- intent-filter 是意图过滤器. 常用语隐式跳转
- action android:name 是动作名称,是指intent要执行的动作
- category android:name 是过滤器的类别, 一般情况下,每个 中都要显示指定一个默认的类别名称,即
<category android:name="android.intent.category.DEFAULT" />
但是上面的代码中没有指定默认类别名称,这是一个例外情况,因为其 中的是"android.intent.action.MAIN",意思是这个Activity是应用程序的入口点,这种情况下可以不加默认类别名称。
4. Activity启动与参数传递
Tips
在Android中我们可以通过下面两种方式来启动一个新的Activity,注意这里是怎么启动,分为显示启动和隐式启动!
4.1 显式启动:通过包名来启动
无参跳转
textView.setOnClickListener {
//MainActivity@this : Context
val intent = Intent(MainActivity@this , SecondActivity::class.java)
startActivity(intent)
}
日志 ActivityA跳转到ActivityB
2021-11-03 22:08:59.781 4562-4562/com.example.componentlearn E/MainActivity:: onCreate
2021-11-03 22:08:59.790 4562-4562/com.example.componentlearn E/MainActivity:: onStart
2021-11-03 22:08:59.797 4562-4562/com.example.componentlearn E/MainActivity:: onResume
2021-11-03 22:09:07.295 4562-4562/com.example.componentlearn E/MainActivity:: onPause
2021-11-03 22:09:07.332 4562-4562/com.example.componentlearn E/SecondActivity:: onCreate
2021-11-03 22:09:07.337 4562-4562/com.example.componentlearn E/SecondActivity:: onStart
2021-11-03 22:09:07.338 4562-4562/com.example.componentlearn E/SecondActivity:: onResume
2021-11-03 22:09:08.009 4562-4562/com.example.componentlearn E/MainActivity:: onStop
有参跳转
textView.setOnClickListener {
//有参跳转
val intent = Intent(MainActivity@this , SecondActivity::class.java)
intent.putExtra("extra_data","extra_data")
intent.putExtra("extra_int_data",100)
startActivity(intent)
}
ActivityB接受参数
val stringExtra = intent.getStringExtra("extra_data")
val intExtra = intent.getIntExtra("extra_int_data" , 0 )
期待从目标页获取数据
registerForActivityResult---->比如启动相册获取图片
lateinit延迟初始化
①假设从A--->B页面,以registerForActivityResult方式启动
registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
){result ->
val data = result.data
val resultCode = result.resultCode
val stringExtraResult = data?.getStringExtra("result_extra_string")
val intExtraResult = data?.getIntExtra("result_extra_int",0)
textView.text = "MainActivity result [${resultCode}] ${stringExtraResult} ---- ${intExtraResult}"
}.launch(Intent(this,SecondActivity::class.java))
②如果B页面返回时,调用了
textView.setOnClickListener {
val resultIntent = Intent()
resultIntent.putExtra("result_extra_string" , "result_extra_string")
resultIntent.putExtra("result_extra_int" , 1000)
setResult(Activity.RESULT_OK , resultIntent)
finish() //关闭SecondActivity 等同于点击返回键
}
startActivityForResult 过时用 registerForActivityResult代替
https://www.zbug.cc/index.php/archives/registerForActivityResult.html
这个事件中两个Activity的生命周期
2021-11-04 11:19:12.221 4381-4381/com.example.componentlearn E/MainActivity:: onCreate
2021-11-04 11:19:12.233 4381-4381/com.example.componentlearn E/MainActivity:: onStart
2021-11-04 11:19:12.235 4381-4381/com.example.componentlearn E/MainActivity:: onResume
2021-11-04 11:19:12.267 4381-4381/com.example.componentlearn E/MainActivity:: onPause
2021-11-04 11:19:12.705 4381-4381/com.example.componentlearn E/SecondActivity:: onCreate
2021-11-04 11:19:12.709 4381-4381/com.example.componentlearn E/SecondActivity:: onStart
2021-11-04 11:19:12.709 4381-4381/com.example.componentlearn E/SecondActivity:: onResume
2021-11-04 11:19:12.883 4381-4381/com.example.componentlearn E/MainActivity:: onStop
2021-11-04 11:19:20.553 4381-4381/com.example.componentlearn E/SecondActivity:: onPause
2021-11-04 11:19:20.565 4381-4381/com.example.componentlearn E/MainActivity:: onRestart
2021-11-04 11:19:20.568 4381-4381/com.example.componentlearn E/MainActivity:: onStart
2021-11-04 11:19:20.569 4381-4381/com.example.componentlearn E/MainActivity:: onResume
2021-11-04 11:19:21.141 4381-4381/com.example.componentlearn E/SecondActivity:: onStop
2021-11-04 11:19:21.142 4381-4381/com.example.componentlearn E/SecondActivity:: onDestroy
4. 2. 隐式启动
通过指定 action
和 category
的信息,让系统去分析这个 Intent
,并找出合适的 Activity 去启动。
<activity android:name=".SecondActivity"
android:exported="true">
<intent-filter>
<!--自定义 action时一般包名.action.全大写类名-->
<action android:name="com.example.componentLearn.action.SECONDACTIVITY"/>
<category android:name="com.example.componentLearn.category.SecondActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
textView.setOnClickListener {
val intent = Intent()
intent.action = "com.example.componentLearn.action.SECONDACTIVITY"
intent.addCategory("com.example.componentLearn.category.SECONDACTIVITY")
intent.putExtra("extra_data" , "extra_data")
intent.putExtra("extra_data_int" , 111)
//隐式启动
startActivity(intent)
}
5.原生常见的Activtiy
拨打电话
给 11111 拨打电话
val uri: Uri = Uri.parse("tel: 11111")
val intent = Intent(Intent.ACTION_DIAL,uri)
startActivity(intent)
发送短信
给 11111 发送内容为“发送短信”的短信
val uri:Uri = Uri.parse("smsto:1111")
val intent = Intent(Intent.ACTION_SENDTO , uri)
intent.putExtra("sms_body" , "发送短信")
startActivity(intent)
打开浏览器:
打开baidu主页
val uri:Uri = Uri.parse("http://www.baidu.com")
val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent)
多媒体播放:
val intent = Intent(Intent.ACTION_VIEW)
val uri = Uri.parse("file:///sdcard/foo.mp3")
intent.setDataAndType(uri, "audio/mp3")
startActivity(intent)
打开摄像头拍照:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 0);
>>> 在Activity的onActivityResult方法回调中取出照片数据
Bundle extras = intent.getExtras();
Bitmap bitmap = (Bitmap) extras.get("data");
从图库选图并剪切
// 获取并剪切图片
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.putExtra("crop", "true"); // 开启剪切
intent.putExtra("aspectX", 1); // 剪切的宽高比为1:2
intent.putExtra("aspectY", 2);
intent.putExtra("outputX", 20); // 保存图片的宽和高
intent.putExtra("outputY", 40);
intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp"))); // 保存路径
intent.putExtra("outputFormat", "JPEG");// 返回格式
startActivityForResult(intent, 0);
>>>> 在Activity的onActivityResult方法中去读取保存的文件
剪切指定图片文件
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setClassName("com.android.camera", "com.android.camera.CropImage");
intent.setData(Uri.fromFile(new File("/mnt/sdcard/temp")));
intent.putExtra("outputX", 1); // 剪切的宽高比为1:2
intent.putExtra("outputY", 2);
intent.putExtra("aspectX", 20); // 保存图片的宽和高
intent.putExtra("aspectY", 40);
intent.putExtra("scale", true);
intent.putExtra("noFaceDetection", true);
intent.putExtra("output", Uri.parse("file:///mnt/sdcard/temp"));
startActivityForResult(intent, 0);
>>>> 在Activity的onActivityResult方法中去读取保存的文件
进入手机的无线网络设置页面
// 进入无线网络设置界面(其它可以举一反三)
Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
startActivityForResult(intent, 0);
6. Activity四种启动模式
standard
默认值,多实例模式。每启动一次,都会创建一个新的Activity实例。
启动的生命周期为:onCreate()->onStart()->onResume()
singleTop
栈顶复用模式
如果任务栈顶已经存在需要启动的目标Activity,则直接启动,并会回调onNewIntent()方法,生命周期顺序为: onPause() ->onNewIntent()->onResume()
如果任务栈上顶没有需要启动的目标Activity,则创建新的实例,此时生命周期顺序为: onCreate()->onStart()->onResume()
两种情况如下图,从图中可以看出,此模式下还是会出现多实例,只要启动的目标Activity不在栈顶的话。
singleTask
栈内复用模式,一个任务栈只能有一个实例。
有几种情况:
- 当启动的Activity目标任务栈不存在时,则以此启动Activity为根Activity创建目标任务栈,并切换到前面
- D为singleTask模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CqBhkA0i-1636119213858)(https://cdn.jsdelivr.net/gh/hairlyOwl/photo@master/singletask-(1)].497h6l5ebky0.png)
- 当启动的Activity存在时,则会直接切换到Activity所在的任务栈,并且任务栈中在Activity上面的所有其他Activity都出栈(调用destroy()),此时启动的Activity位于任务栈顶,并且会回调onNewIntent()方法。
singleInstance
singleInstance名称是单例模式,即App运行时,该Activity只有一个实例。既然只有一个,那么也就说明很重要、很特殊,我们需要将其“保护起来”。单例模式的“保护措施”是将其单独放到一个任务栈中。
Bug记录
1**.LifecycleOwners must call register before they are STARTED**
java.lang.IllegalStateException: LifecycleOwner com.example.componentlearn.MainActivity@1e0bae9 is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
把registerForActivityResult
移除 OnClickListener
但仍在onCreate