如何知晓当前是在哪一个Activit
在 Android 开发中,获取当前正在显示的 Activity 可以通过以下方法实现(Kotlin 代码示例):
方法 1:通过基类 Activity 记录当前实例
为所有 Activity 添加一个公共基类,通过静态变量记录当前 Activity。
步骤 1:创建基类 BaseActivity
kotlin
open class BaseActivity : AppCompatActivity() {
companion object {
// 使用弱引用避免内存泄漏
private var currentActivityRef: WeakReference<Activity>? = null
// 获取当前 Activity
fun getCurrentActivity(): Activity? {
return currentActivityRef?.get()
}
}
override fun onResume() {
super.onResume()
currentActivityRef = WeakReference(this)
}
override fun onPause() {
super.onPause()
// 可选:根据需要清空引用
// currentActivityRef?.clear()
}
}
步骤 2:其他 Activity 继承 BaseActivity
kotlin
class MainActivity : BaseActivity() { /* ... */ }
class DetailActivity : BaseActivity() { /* ... */ }
步骤 3:获取当前 Activity
kotlin
val currentActivity = BaseActivity.getCurrentActivity()
if (currentActivity is MainActivity) {
Log.d("CurrentActivity", "当前是 MainActivity")
}
方法 2:通过 Application 监听 Activity 生命周期
利用 Application.ActivityLifecycleCallbacks
全局监听所有 Activity 的生命周期。
步骤 1:注册生命周期回调
kotlin
class MyApp : Application() {
companion object {
private var currentActivityRef: WeakReference<Activity>? = null
fun getCurrentActivity(): Activity? {
return currentActivityRef?.get()
}
}
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityResumed(activity: Activity) {
currentActivityRef = WeakReference(activity)
}
override fun onActivityPaused(activity: Activity) {
// 可选:清空引用
// if (currentActivityRef?.get() == activity) {
// currentActivityRef = null
// }
}
// 其他生命周期方法留空即可
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
})
}
}
步骤 2:在 AndroidManifest.xml 中声明 Application
xml
<application
android:name=".MyApp"
... >
</application>
步骤 3:获取当前 Activity
kotlin
val currentActivity = (applicationContext as MyApp).getCurrentActivity()
Log.d("CurrentActivity", "当前 Activity: ${currentActivity?.javaClass?.simpleName}")
方法 3:通过反射获取 Activity 栈(需权限)
直接访问系统 Activity 栈(适用于调试,但可能不兼容所有设备)。
代码示例
kotlin
fun getCurrentActivityViaReflection(): Activity? {
return try {
val activityThreadClass = Class.forName("android.app.ActivityThread")
val activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null)
val activitiesField = activityThreadClass.getDeclaredField("mActivities")
activitiesField.isAccessible = true
val activities = activitiesField.get(activityThread) as Map<*, *>
for (entry in activities.values) {
val activityRecordClass = entry?.javaClass
val pausedField = activityRecordClass?.getDeclaredField("paused")
pausedField?.isAccessible = true
if (pausedField?.getBoolean(entry) == false) { // 找到未暂停的 Activity
val activityField = activityRecordClass.getDeclaredField("activity")
activityField.isAccessible = true
return activityField.get(entry) as Activity
}
}
null
} catch (e: Exception) {
e.printStackTrace()
null
}
}
注意事项
- 需要权限:部分设备需要
android.permission.GET_TASKS
(已废弃)。 - 兼容性差:不同 Android 版本实现可能不同,不推荐正式环境使用。
方法 4:使用第三方库
例如 Android-ActivityManager
或自定义工具类。
示例:通过 ActivityManager
获取前台 Activity
kotlin
fun getForegroundActivity(context: Context): String? {
val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val tasks = am.getRunningTasks(1) // 已废弃,仅适用于旧版本
return if (tasks.isNotEmpty()) {
tasks[0].topActivity?.className
} else {
null
}
}
注意:
- Android 10+ 限制:
getRunningTasks
已废弃,无法获取其他应用的信息。 - 仅限调试:正式环境需使用前两种方法。
最佳实践
- 推荐方法:
- 优先使用 方法 2(Application 监听),无需侵入 Activity 代码。
- 若需要兼容性,使用 方法 1(基类)。
- 避免场景:
- 不要依赖当前 Activity 处理核心业务逻辑(应通过 ViewModel 或数据驱动)。
- 避免在后台线程频繁获取当前 Activity。
总结
方法 | 优点 | 缺点 |
---|---|---|
基类记录 | 简单直接 | 需所有 Activity 继承基类 |
Application 监听 | 全局监听,无侵入性 | 需自定义 Application |
反射 | 无需修改代码 | 兼容性差,可能被系统限制 |
第三方库/系统 API | 快速实现 | 权限限制,Android 版本差异 |
根据需求选择合适方案,推荐 方法 2 作为通用解决方案。
随时随地退出程序
在 Android 开发中,要实现在任何界面都能「彻底退出应用」,需结合 任务栈管理 和 进程终止。以下是 Kotlin 的完整解决方案:
方案 1:优雅退出(推荐)
通过清理任务栈并跳转到主界面,适用于需要保留应用进程但关闭所有 Activity 的场景(如退出到登录页)。
步骤 1:创建退出工具类
kotlin
object AppExitUtils {
/**
* 退出到指定 Activity(如登录页),并清空任务栈
* @param context 上下文
* @param targetActivity 目标 Activity 类(如 LoginActivity::class.java)
*/
fun exitToActivity(context: Context, targetActivity: Class<*>) {
val intent = Intent(context, targetActivity).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
}
context.startActivity(intent)
(context as? Activity)?.overridePendingTransition(0, 0) // 禁用过渡动画
}
/**
* 完全退出应用(关闭所有 Activity 并终止进程)
*/
fun exitApp(context: Context) {
// 1. 关闭所有 Activity
ActivityCollector.finishAll()
// 2. 终止进程(可选,通常不需要)
// android.os.Process.killProcess(android.os.Process.myPid())
// System.exit(0)
}
}
// Activity 管理器(用于跟踪所有 Activity)
object ActivityCollector {
private val activities = mutableListOf<WeakReference<Activity>>()
fun addActivity(activity: Activity) {
activities.add(WeakReference(activity))
}
fun removeActivity(activity: Activity) {
activities.removeAll { it.get() == activity }
}
fun finishAll() {
activities.forEach { ref ->
ref.get()?.finish()
}
activities.clear()
}
}
步骤 2:在 BaseActivity 中集成
kotlin
abstract class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityCollector.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
ActivityCollector.removeActivity(this)
}
}
使用示例
kotlin
// 退出到登录页
AppExitUtils.exitToActivity(this, LoginActivity::class.java)
// 完全退出应用
AppExitUtils.exitApp(this)
方案 2:强制终止进程(不推荐)
直接杀死应用进程,可能导致资源未正确释放,仅用于极端情况。
kotlin
fun forceExitApp() {
// 关闭所有 Activity
ActivityCollector.finishAll()
// 终止进程
android.os.Process.killProcess(android.os.Process.myPid())
System.exit(0)
}
关键细节说明
任务栈清理:
Intent.FLAG_ACTIVITY_CLEAR_TASK
:清空当前任务栈。Intent.FLAG_ACTIVITY_NEW_TASK
:在新任务栈中启动 Activity(避免残留历史记录)。
进程终止:
killProcess()
和System.exit()
会强制终止进程,但可能导致后台服务或通知被意外关闭。- 通常只需关闭所有 Activity,系统会在需要时自动回收进程。
用户体验优化:
- 禁用跳转动画:
overridePendingTransition(0, 0)
。 - 在退出前保存必要数据(如用户偏好设置)。
- 禁用跳转动画:
完整流程图
text
[用户点击退出按钮]
↓
[关闭所有 Activity(ActivityCollector.finishAll())]
↓
[跳转到登录页/主页(清理任务栈)]
↓
[可选:终止进程(仅限极端需求)]
最佳实践
场景 1:退出到登录页
使用exitToActivity(this, LoginActivity::class.java)
,确保用户无法通过返回键回到之前的界面。场景 2:完全退出应用
调用exitApp()
,适合在「设置」页提供「退出应用」按钮。避免直接杀进程:除非确定需要立即释放内存,否则让系统管理进程生命周期。
通过上述方案,可优雅实现「随时随地退出应用」,同时兼顾系统规范与用户体验。
启动Activity的最佳写法
在 Android 开发中,启动 Activity 的「最佳写法」需兼顾 代码简洁性、类型安全 和 可维护性。以下是基于 Kotlin 的现代实践方案:
1. 基础启动(显式 Intent)
标准写法
kotlin
// 启动目标 Activity
val intent = Intent(this, TargetActivity::class.java)
startActivity(intent)
优化:封装扩展函数
kotlin
// 为 Context 添加扩展函数
inline fun <reified T : Activity> Context.navigate(
block: Intent.() -> Unit = {}
) {
val intent = Intent(this, T::class.java).apply(block)
startActivity(intent)
}
// 使用示例(在 Activity/Fragment 中)
navigate<TargetActivity>()
2. 传递参数
安全传参(使用 Bundle 或 Parcelable)
kotlin
// 发送方
navigate<TargetActivity> {
putExtra("key_string", "Hello")
putExtra("key_int", 100)
putExtra("user", User("Alice", 25)) // User 需实现 Parcelable
}
// 接收方(在 TargetActivity 中)
private fun parseIntent() {
val stringValue = intent.getStringExtra("key_string") ?: ""
val intValue = intent.getIntExtra("key_int", 0)
val user = intent.getParcelableExtra<User>("user")
}
推荐:使用 SafeArgs
或 Bundle
工具类
kotlin
// 定义参数常量
object Args {
const val KEY_STRING = "key_string"
const val KEY_INT = "key_int"
const val KEY_USER = "key_user"
}
// 发送方
navigate<TargetActivity> {
putExtra(Args.KEY_STRING, "Hello")
}
// 接收方
val stringValue = intent.getStringExtra(Args.KEY_STRING) ?: ""
3. 处理返回结果
旧方案:startActivityForResult
(已弃用)
kotlin
// 启动 Activity 并等待结果
val intent = Intent(this, TargetActivity::class.java)
startActivityForResult(intent, REQUEST_CODE)
// 处理结果
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
val result = data?.getStringExtra("result_key")
}
}
新方案:Activity Result API
(推荐)
kotlin
// 定义 Activity Result Launcher
private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
val data = result.data?.getStringExtra("result_key")
}
}
// 启动 Activity
val intent = Intent(this, TargetActivity::class.java)
resultLauncher.launch(intent)
4. 高级场景
清空任务栈跳转(如退出到登录页)
kotlin
navigate<LoginActivity> {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
带共享元素动画
kotlin
// 发送方
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
imageView, // 共享的 View
"transition_name" // 与目标 Activity 中 View 的 transitionName 一致
).toBundle()
navigate<DetailActivity>(options = options)
// 接收方(在 DetailActivity 的布局中)
<ImageView
android:transitionName="transition_name" />
5. 最佳实践总结
场景 | 写法 |
---|---|
普通跳转 | 使用 navigate<TargetActivity>() 扩展函数 |
传参 | 通过 putExtra + 常量 Key,或使用 SafeArgs |
接收结果 | 优先使用 Activity Result API |
任务栈管理 | 合理使用 Intent Flags (如 FLAG_ACTIVITY_CLEAR_TOP ) |
动画 | 使用 ActivityOptionsCompat 实现平滑过渡 |
完整工具类封装
kotlin
object NavigationUtils {
/**
* 安全跳转到目标 Activity
* @param context 上下文
* @param target 目标 Activity 类
* @param extras 参数构建器(可选)
* @param flags Intent 标志(可选)
* @param options 动画选项(可选)
*/
inline fun <reified T : Activity> navigate(
context: Context,
extras: Intent.() -> Unit = {},
flags: Int = 0,
options: Bundle? = null
) {
val intent = Intent(context, T::class.java).apply {
this.flags = flags
extras()
}
if (intent.resolveActivity(context.packageManager) != null) {
ContextCompat.startActivity(context, intent, options)
} else {
Log.e("Navigation", "Target activity not found: ${T::class.java.name}")
}
}
}
// 使用示例
NavigationUtils.navigate<DetailActivity>(
context = this,
extras = { putExtra("key_data", "Hello") },
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
)
注意事项
- 避免内存泄漏:在 Fragment 或 View 中启动 Activity 时,确保使用
Activity
作为 Context。 - 数据大小限制:传递的 Bundle 数据不宜超过 1MB。
- 类型安全:使用
Parcelable
替代Serializable
提升性能。 - 兼容性:检查隐式 Intent 的目标是否存在(
resolveActivity
)。
通过以上写法,可显著提升代码的可读性和健壮性,同时适配现代 Android 开发规范。