Android Activity

本文总结《第一行代码 Android》第3版的内容

环境:
Android Studio Giraffe | 2022.3.1 Patch 3

Activity 是什么?

Activity 简单将就是UI界面,包含两部分 Activity 类 和应用布局文件,如果是 Compose 则另说,一般入门Android,都是从Activity开始的。
我们写一个程序,常常找应用启动入口,像 java 的 main()函数,那么 Activity 的应用入口在哪?下面通过创建项目认识。
在创建 Android 项目时,要选择 No Activity,是因为 Android Studio 2022 中如果选择 Empty Activity,会默认使用 Compose UI。在创建完成后,手动添加“Empty Activity”:

如果勾选 Launcher Activity,那么该 Activity 一般就是应用启动的时候,首先显示的。
在创建 Activity 之后,会自动在 AndroidManifest 注册 Activity:
应用入口在 AndroidManifest.xml 中一般会有标识:

<activityandroid:name=".FirstActivity"android:launchMode="singleTask"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>

Acttivity 的基本用法

在Android Studio 添加 Activity 之后,会创建两个文件:一个类,一个xml文件。类用来写逻辑,xml写UI布局。
修改 xml 布局,使用LinearLayout,默认的太复杂,添加 Button 控件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".FirstActivity"><Buttonandroid:id="@+id/button1"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Button 1"/></LinearLayout>

然后在 Activity中添加逻辑:

// button 点击事件button1.setOnClickListener{Toast.makeText(this,"你点击了按钮",Toast.LENGTH_SHORT).show()

一个最简单的应用就完成了。

使用 Toast 消息提示

Toast 用于提示用户,用法如上面,第三个参数有两种 Toast.LENGTH_SHORT 和 Toast.LENGTH_LONG,显示时长不一样。

Activity 界面添加 Menu

步骤1: 添加 Menu 资源文件

<menu xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:id="@+id/add_item"android:title="添加"/><itemandroid:id="@+id/remove_item"android:title="删除"/></menu>

步骤二:在 Activity中 重写 onCreateOptionsMenu() 方法

override fun onOptionsItemSelected(item: MenuItem): Boolean {when(item.itemId){R.id.add_item -> Toast.makeText(this,"添加菜单项",Toast.LENGTH_SHORT).show()R.id.remove_item -> Toast.makeText(this,"删除菜单项",Toast.LENGTH_SHORT).show()}return true}

效果:

销毁Activity

在按返回键时,Activity 可以被销毁,在代码中使用 finish() 方法

不同 Activity 中跳转

首先理解什么是 Intent” />val intent = Intent(this, SecondActivity::class.java)startActivity(intent)

在上述代码中,我们明确指定了要启动的Activity是SecondActivity,因此这是一个显式Intent。

FirstActivity 跳转到 SecondActivity 使用 显式 Intent。

隐式Intent:隐式Intent并没有明确指定要启动的组件,而是指定了一种动作(Action)和数据(Data),然后由系统解析这个Intent,找到合适的组件来处理这个Intent。这种Intent通常用于启动其他应用的组件。例如,打开网页、拨打电话等。

val intent = Intent(Intent.ACTION_VIEW)intent.data = Uri.parse("https://www.baidu.com")startActivity(intent)

在上述代码中,我们指定了一个动作ACTION_VIEW和一个数据https://www.baidu.com,系统会找到能处理这个动作和数据的应用(如浏览器)来启动。因此这是一个隐式Intent。
如果让我们的程序能够响应隐式Intent,需要在 AndroidManifest.xml 中注册:

<activityandroid:name=".ThirdActivity"android:exported="true" ><intent-filter tools:ignore="AppLinkUrlError"><action android:name="android.intent.action.VIEW"/><category android:name="android.intent.category.DEFAULT"/><data android:scheme="https"/></intent-filter></activity>

调用方法:

 // 隐式Intent val intent = Intent("com.alex.activitytest.ACTION_START") intent.addCategory("com.alex.activitytest.MY_CATEGORY") startActivity(intent)

不同Activity传递数据

FirstActivty 启动 SecondActivity 并传递数据:

// 启动Activity并传递数据val data="Hello SecondActivity"val intent = Intent(this,SecondActivity::class.java)intent.putExtra("extra_data",data)startActivity(intent)

SecondActivity 接收数据:在 onCreate方法中

val extraData=intent.getStringExtra("extra_data")Toast.makeText(this,extraData,Toast.LENGTH_SHORT).show()

如果 SecondActivity 需要向 FirstActivty 返回数据,需要三步,在 FirstActivty 调用的时候:把 startActivity(intent) 替换为

startActivityForResult(intent,1)

SecondActivity 返回数据:

val intent = Intent()intent.putExtra("data_return","Hello, FirstActivity")setResult(RESULT_OK,intent)// 销毁当前Activityfinish()

FirstActivty 接收数据:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { 1 -> if (resultCode == RESULT_OK) { val returnedData = data?.getStringExtra("data_return") Log.d("FirstActivity", "returned data is $returnedData") } }}

startActivityForResult 与 onActivityResult 被废弃了,可以参考这篇文章进行更新:

startActivityForResult被标记为废弃?Activity Result API闪亮登场!

Activity 生命周期

Android 使用 task 来管理 Activity,一个任务就是一组存放在返回栈里的 Activity的集合。启动Activity,Activity入栈,Activity销毁就会出栈。显示的Activity总是在栈顶。

Activity 的四种状态

  • 运行状态
    Activity处于栈顶
  • 暂停状态
    比如一个Activity上弹出一个对话框,此时Activity处于暂停状态
  • 停止状态
    Activity 不处于栈顶,并且完全不可见
  • 销毁状态
    Activity 从返回栈中移除

Activity 生命周期

  • onCreate()。会在Activity第一次被创建的时候调用。应该在这个方法中完成Activity的初始化操作,比如加载布局、绑定事件等。
  • onStart()。这个方法在Activity由不可见变为可见的时候调用。
  • onResume()。这个方法在Activity准备好和用户进行交互的时候调用。此时的Activity一定位于返回栈的栈顶,并且处于运行状态。
  • onPause()。这个方法在系统准备去启动或者恢复另一个Activity的时候调用。我们通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶Activity的使用。
  • onStop()。这个方法在Activity完全不可见的时候调用。它和onPause()方法的主要区别在于,如果启动的新Activity是一个对话框式的Activity,那么onPause()方法会得到执行,而onStop()方法并不会执行。
  • onDestroy()。这个方法在Activity被销毁之前调用,之后Activity的状态将变为销毁状态。
  • onRestart()。这个方法在Activity由停止状态变为运行状态之前调用,也就是Activity被重新启动了。


结合GPT,谈谈应用场景:
onCreate():
应用场景: 初始化 Activity,设置布局,初始化成员变量和数据绑定。
例子: 在一个音乐播放器应用中,在 onCreate() 中加载布局,初始化播放控制按钮和音轨信息。

onStart():
应用场景: 让 Activity 对用户可见,进行一些轻量级的资源初始化或恢复操作。
例子: 在新闻应用中,当 Activity 变为可见时,可以在 onStart() 中开始加载新闻列表。

onResume():
应用场景: 当 Activity 与用户交互前,恢复暂停的动画,初始化相机等。
例子: 在相机应用中,在 onResume() 方法中打开相机预览。

onPause():
应用场景: 暂停或调整不再需要的操作,如暂停动画,释放相机资源。
例子: 如果用户在编辑照片的应用中接到电话,onPause() 可用于暂停编辑进度或保存状态。

onStop():
应用场景: 当 Activity 不再可见时,释放或调整资源和操作,如停止播放媒体,取消网络请求。
例子: 在视频播放应用中,当用户离开播放界面,可以在 onStop() 中停止视频播放并释放播放器资源。

onDestroy():
应用场景: 清理资源,如关闭数据库连接,注销广播接收器。
例子: 在社交应用中,当 Activity 销毁时,关闭数据库连接和注销监听网络变化的广播接收器。

onRestart():
应用场景: 在 Activity 从停止状态回到活动状态时调用,用于重新初始化在 onStop() 中释放的资源。
例子: 在购物应用中,如果用户从购物车界面切换到其他应用然后返回,可以在 onRestart() 中刷新购物车数据。

如果从网络请求新闻列表数据,可以写在 onStart 或 onResume 方法中。如果你希望每次 Activity 变得可见时都刷新数据,那么 onStart() 是合适的地方。如果你希望在 Activity 每次回到用户交互前台时刷新数据,则 onResume() 更为合适。此外,考虑到网络请求可能会耗时并影响用户体验,推荐使用异步处理(如使用 AsyncTask 或 Retrofit 等网络库)来进行网络请求,避免阻塞主线程。

Activity 保存状态数据

如果 Activity 在停止状态时,有可能被回收,再返回时,如果没有数据,Activity可能无法正常显示,此时需要保存状态,可以使用 onSaveInstanceState方法,确保在回收前保存

override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) val tempData = "Something you just typed" outState.putString("data_key", tempData)}

在onCreate() 恢复数据:

override fun onCreate(savedInstanceState: Bundle" />) { super.onCreate(savedInstanceState) Log.d(tag, "onCreate") setContentView(R.layout.activity_main) if (savedInstanceState != null) { val tempData = savedInstanceState.getString("data_key") Log.d(tag, "tempData is $tempData") } ...}

Activity 四种启动模式

Android 中Activity的四种启动模式及使用场景如下:

  1. Standard

用途: 每次启动 Activity 时都会创建一个新的实例,无论该 Activity 是否已经存在于任务栈中。

应用场景: 这是默认模式。适用于大多数标准的 Activity,如显示列表项的详细信息。例如,在邮件应用中,用户点击一个邮件时,每次都会创建一个新的邮件详情 Activity,即使是同一个邮件。


2. SingleTop

用途: 如果新的 Activity 已经是任务栈的顶端,则不会创建新的实例,而是复用栈顶的实例,并通过 onNewIntent() 方法接收新的 Intent。

应用场景: 适用于需要接收新信息且频繁启动但不需要多实例的 Activity。例如,在聊天应用中,如果用户已经在聊天界面,新消息的通知点击后只更新当前的聊天界面,而不重新创建 Activity。

  1. SingleTask

用途: 只创建一个实例,该实例会在新的任务栈中。如果 Activity 已经存在,则把这个 Activity 之上的所有其他 Activity 出栈,使这个 Activity 显示在最前。

应用场景: 适用于作为应用中单一入口的 Activity,如主页。例如,在浏览器应用中,主页 Activity 设置为 singleTask,无论从哪里启动,都只有一个主页实例。

4. SingleInstance

用途: 类似于 singleTask,但系统会为这种 Activity 创建一个新的任务栈,并且在这个栈中只有这个 Activity 实例。

应用场景: 适用于需要与应用完全分离的模块,例如,一个应用内的浮动小窗口或者是需要与其他应用共享的 Activity。例如,一款具有浮动小窗功能的音乐播放器,其播放控制界面可以设置为 singleInstance 模式。

Activity最佳实践

通过BaseActivity知晓当前在哪一个Activity

open class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle" />) { super.onCreate(savedInstanceState) Log.d("BaseActivity", javaClass.simpleName) }}

全局管理Activity,随时退出程序

新建单例类ActivityCollector

package com.alex.activitytestimport android.app.Activity/** * @Author: alex * @Date: on 2023/12/28 15:59. * @Description :描述 */object ActivityCollector {private val activities = ArrayList<Activity>()fun addActivity(activity: Activity){activities.add(activity)}fun removeActivity(activity: Activity){activities.remove(activity)}fun finishAll(){for(activity in activities){if(!activity.isFinishing){activity.finish()}}activities.clear()}}

这个方法在测试的时候不行,比如点击按钮执行 ActivityCollector.finishAll(), 系统会重启App,即时加了下面的代码也一样。

android.os.Process.killProcess(android.os.Process.myPid())

可参考:https://blog.csdn.net/shulianghan/article/details/116402759