面试官最爱问的 Android 数据传递问题

本系列为小说《逆袭西二旗》的技术讲解,用于详细说明剧情里涉及的开发细节。

Deep link 允许用户从外部来源(比如一个 URL 或通知)直接跳转到你 App 内的某个特定页面或功能。处理 Deep link ,需要在 AndroidManifest.xml 中定义对应的 intent-filter,并在相应的 ActivityFragment 中处理传入的 Intent

面试问题

如何在 Android 中测试 Deep link ?(这个问题厉害在,不仅仅要会写 Deep link,还要会测试)

有哪些常见的调试技巧,能确保它们在不同设备和场景下正常工作?

要启用 Deep link ,你需要在 AndroidManifest.xml 中为目标 Activity 声明一个 intent-filter。这个过滤器会指定你的 App 能响应的 URL 结构或协议。

xml 复制代码
<activity android:name=".MyDeepLinkActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="https"
            android:host="example.com"
            android:pathPrefix="/deepLink" />
    </intent-filter>
</activity>

它由以下三部分构成:

  • android:scheme:指定 URL 协议(例如 https)。
  • android:host:指定域名(例如 example.com)。
  • android:pathPrefix:定义 URL 中的路径前缀(例如 /deepLink)。

这个配置可以让类似 https://example.com/deepLink 的链接直接打开 MyDeepLinkActivity

Activity 内部,提取并处理传入的 Intent 数据,以便跳转到对应页面或执行特定操作。

Kotlin 复制代码
class MyDeepLinkActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my_deep_link)

        // 获取 intent 数据
        val intentData: Uri? = intent?.data
        if (intentData != null) {
            val id = intentData.getQueryParameter("id")  // 示例:获取查询参数
            navigateToFeature(id)
        }
    }

    private fun navigateToFeature(id: String?) {
        // 根据 Deep link 数据跳转到指定页面
        if (id != null) {
            Toast.makeText(this, "Navigating to item: $id", Toast.LENGTH_SHORT).show()
            // navigate(...) or doSomething(...)
        }
    }
}

这段代码会在 onCreate 中检查是否有传入的 URL 数据,如果有,就提取其中的参数(比如 id),然后跳转到对应的页面或执行相应逻辑。

步骤 3:测试一下

要测试 Deep link ,可以使用以下 adb 命令:

sh 复制代码
adb shell am start -a android.intent.action.VIEW \
-d "https://example.com/deepLink?id=123" \
com.example.myapp

该命令会模拟一个 Deep link,并启动你的 App 来处理它。

注意事项

  • 自定义协议 :你可以使用自定义协议(如 myapp://)来实现内部链接,但建议优先使用 HTTP(S) URL,以获得更好的兼容性。
  • 导航逻辑 :根据 Deep link 中的数据,使用 Intent 跳转到 App 内的其他 ActivityFragment
  • 异常处理 :确保 App 能正确处理 Deep link 数据无效或不完整的情况,避免崩溃或体验异常。
  • App Links :若希望 HTTP(S) Deep link 直接在你的 App 中打开而非浏览器,需配置 App Links

总结

处理 Deep link 的关键在于:在 AndroidManifest.xml 中定义 Deep link 模式,并在目标 Activity 中解析和响应这些数据。通过提取并理解 Deep link 内容,你可以将用户精准引导至 App 的特定功能或内容,从而提升用户体验和用户参与度。


什么是任务和返回栈?

一个任务是一组用户为完成特定目标而交互的 Activity。任务被组织成一个返回栈,它是一种后进先出的结构,当 Activity 启动时会被添加到栈中,当用户点击返回按钮或系统回收资源时,活动会从栈中移除。

面试问题

singleTasksingleInstance 启动模式有什么区别?在什么场景下你会选择使用它们?

有哪些不同的 Activity 启动模式?它们如何影响任务和返回栈的行为?

任务

当一个 Activity 被启动时(通常来自启动器或通过 Intent),就会创建一个任务。根据 IntentActivity 启动模式的配置,一个任务可以跨越多个应用程序及其 Activity

例如,在电子邮件应用中点击一个链接可能会在同一个任务中打开浏览器。任务会一直保持活跃状态,直到其关联的所有 Activity 都被销毁。

返回栈

返回栈用于维护任务中 Activity 的历史记录。

当用户导航到一个新的 Activity 时,当前 Activity 会被压入栈顶。

点击返回按钮会将栈顶的 Activity 弹出,并恢复其下方的 Activity。这种机制确保了用户操作流程中的直观导航和连续性。

任务和返回栈的行为受 Activity 启动模式(launch modes )和 Intent 标志(intent flags)的影响。

启动模式和 Intent 标志是用于控制任务和返回栈中 Activity 行为的机制。这些配置允许开发者定义 Activity 如何被启动以及它们如何与其他 Activity 进行交互。

启动模式

启动模式决定了 Activity 如何被实例化以及在返回栈中如何被处理。Android 中有四种主要的启动模式:

  1. standard :这是默认的启动模式。每次启动该 Activity 时,都会创建一个新的实例并将其添加到返回栈中,即使已经存在一个实例。
  2. singleTop :如果返回栈顶部已存在该 Activity 的实例,则不会创建新的实例。相反,现有实例会在 onNewIntent() 方法中处理该 Intent
  3. singleTask :在任务中仅存在该 Activity 的一个实例。如果该实例已存在,系统会将其置于前台,并调用其 onNewIntent() 方法。这种模式适用于作为应用入口点的 Activity
  4. singleInstance :与 singleTask 类似,但该 Activity 会被放置在一个独立的任务中,与其他 Activity 分离。这确保了没有其他 Activity 能成为该任务的一部分。

Intent 标志

Intent 标志用于修改 Activity 的启动方式,或影响发送 Intent 时返回栈的行为。一些常用的标志包括:

  • FLAG_ACTIVITY_NEW_TASK :在新任务中启动 Activity,或者如果该任务已存在,则将其带到前台。
  • FLAG_ACTIVITY_CLEAR_TOP :如果返回栈中已存在该 Activity 的实例,则清除其上方的所有 Activity,并由现有实例处理该 Intent
  • FLAG_ACTIVITY_SINGLE_TOP :确保如果该 Activity 位于返回栈顶部,则不会创建新实例。此标志通常与其他标志组合使用。
  • FLAG_ACTIVITY_NO_HISTORY :防止该 Activity 被添加到返回栈中,意味着它退出后不会保留在历史记录中。

实战

  • 启动模式 :主要在 AndroidManifest.xml 文件的 <activity> 标签中声明,允许开发者为 Activity 设置默认行为。
  • Intent 标志 :在创建 Intent 时通过代码动态应用,为特定场景提供了更大的灵活性。
Kotlin 复制代码
val intent = Intent(this, SecondActivity::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
startActivity(intent)

在本示例中,如果 SecondActivity 尚未存在,则会在新任务中启动它;如果已存在,则清除其上方的所有 Activity

总结

任务和返回栈是 Android 导航模型的核心,通过管理 Activity 的生命周期和导航历史,实现流畅、易用的操作流程。启动模式定义了 Activity 在任务中被启动和管理的默认行为,而 Intent 标志则在运行时提供对类似行为的精细控制。两者结合,能够精确地管理 Activity 生命周期和返回栈导航。


Bundle 的作用是什么?

Bundle 是一种键值对数据结构,用于在组件之间传递数据,比如 ActivityFragmentService。它常被用来在应用内部高效地传输少量数据。

Bundle 轻量且设计用于将数据序列化为 Android 系统易于管理与传输的格式。

面试问题

onSaveInstanceState() 是如何利用 Bundle 在配置变更(如屏幕旋转)期间保存 UI 状态的?

Bundle 中可以存储哪些类型的数据?

常见使用场景

  1. Activity 之间传递数据 :启动新 Activity 时,可以将 Bundle 附加到 Intent 上,把数据传给目标 Activity
  2. Fragment 之间传递数据 :在 Fragment 事务中,通过 setArguments()getArguments() 使用 BundleFragment 间传递数据。
  3. 保存和恢复实例状态 :在 onSaveInstanceState()onRestoreInstanceState() 等生命周期方法中,Bundle 用于在配置变更(如屏幕旋转)时保存和恢复临时的 UI 状态。
  4. Service 传递数据 :启动 Service 或向绑定的 Service 传参时,Bundle 可以携带数据。

工作原理

Bundle 通过将数据序列化为键值对结构来工作。键是字符串类型,值可以是基本类型、SerializableParcelable 对象,甚至是其他 Bundle。这种设计使得数据存储和传输更高效。

Activity 之间传递数据

Kotlin 复制代码
// 从 Activity A 发送数据
val intent = Intent(this, ActivityB::class.java).apply {
    putExtra("user_name", "John Doe")
    putExtra("user_age", 25)
}
startActivity(intent)

// 在 Activity B 中接收数据
val name = intent.getStringExtra("user_name")
val age = intent.getIntExtra("user_age", -1)

在这个例子中,数据通过 Intent.putExtra() 内部封装进一个 Bundle,实现跨组件的数据传递。

Fragment 之间传递数据

Kotlin 复制代码
// 向 Fragment 传递数据
val fragment = MyFragment().apply {
    arguments = Bundle().apply {
        putString("user_name", "Jane Doe")
        putInt("user_age", 30)
    }
}

// 在 Fragment 中获取数据
val name = arguments?.getString("user_name")
val age = arguments?.getInt("user_age")

保存和恢复状态

Kotlin 复制代码
override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putString("user_input", editText.text.toString())
}

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)
    val userInput = savedInstanceState.getString("user_input")
    editText.setText(userInput)
}

在这个场景中,Bundle 确保了用户输入在配置变更(如屏幕旋转)时不会丢失,能够被正确恢复。

总结

Bundle 是 Android 中用于在组件之间高效传递和保存数据的关键组件,尤其适用于跨生命周期事件的数据管理。它结构轻量、灵活,是处理应用状态和数据传输不可或缺的工具。


如何在 Activity 或 Fragment 之间传递数据?

ActivityFragment 之间传递数据,是构建交互性强、动态灵活界面的重要一环。Android 提供了多种机制来实现这一目标,在保证通信顺畅的同时,也遵循应用的整体架构设计。

面试问题

共享 ViewModel 是如何促进同一 Activity 内多个 Fragment 之间的通信的?

相比使用 Bundle 或直接的 Fragment 事务,它有哪些优势?

Activity 之间传递数据

从一个 Activity 向另一个传递数据,最常用的方式是使用 Intent。数据以键值对的形式(通过 putExtra())附加到 Intent 上,接收方则通过 getIntent() 获取这些数据。

Kotlin 复制代码
// 发送数据的 Activity
val intent = Intent(this, SecondActivity::class.java).apply {
    putExtra("USER_NAME", "John Doe")
    putExtra("USER_AGE", 25)
}
startActivity(intent)

// 接收数据的 Activity
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)

        val userName = intent.getStringExtra("USER_NAME")
        val userAge = intent.getIntExtra("USER_AGE", 0)
        Log.d("SecondActivity", "User Name: $userName, Age: $userAge")
    }
}

这种方式简单直接,适合传递少量结构化数据。注意:Intent 传输的数据有大小限制,不适合大文件或复杂对象。

Fragment 之间传递数据

Fragment 之间的通信可以使用 Bundle 来实现。发送方 Fragment 创建一个包含键值对的 Bundle,并通过 arguments 将其传递给接收方 Fragment

Kotlin 复制代码
// 发送数据的 Fragment
val fragment = SecondFragment().apply {
    arguments = Bundle().apply {
        putString("USER_NAME", "John Doe")
        putInt("USER_AGE", 25)
    }
}
parentFragmentManager.beginTransaction()
    .replace(R.id.fragment_container, fragment)
    .commit()

// 接收数据的 Fragment
class SecondFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_second, container, false)

        val userName = arguments?.getString("USER_NAME")
        val userAge = arguments?.getInt("USER_AGE")
        Log.d("SecondFragment", "User Name: $userName, Age: $userAge")

        return view
    }
}

这种方式是 Fragment 间传参的标准做法,数据在创建时就被封装进 arguments,并随 Fragment 生命周期一起保存和恢复,确保了状态的一致性。

当你使用 Jetpack Navigation 库并配合 Safe Args 插件时,可以生成类型安全的导航方向和参数类,实现目的地之间的安全、明确的数据传递。

第一,在导航图中定义参数:

在你的 nav_graph.xml 文件中:

xml 复制代码
<fragment
    android:id="@+id/secondFragment"
    android:name="com.example.SecondFragment">
    <argument
        android:name="username"
        app:argType="string" />
</fragment>

第二,从源 Fragment 传递数据:

Safe Args 插件会在编译时自动生成目标对象和构建器类,让你能够安全、显式地传递参数,如下所示:

Kotlin 复制代码
val action = FirstFragmentDirections
    .actionFirstFragmentToSecondFragment(username = "rocky")
findNavController().navigate(action)

第三,在目标 Fragment 中获取数据:

最后,你可以通过以下方式从传入的参数中提取数据:

Kotlin 复制代码
val username = arguments?.let {
    SecondFragmentArgs.fromBundle(it).username
}

使用 Safe Args 可以定义并获取强类型的参数,减少运行时错误,同时提升代码可读性和跨 Fragment 的维护性。

共享 ViewModel

当同一个 Activity 内的多个 Fragment 需要互相通信时,推荐使用 共享 ViewModel

所谓共享 ViewModel,是指在同一个 Activity 中被多个 Fragment 共享的 ViewModel 实例。这通过 Jetpack 提供的 androidx.fragment:fragment-ktx 包中的 activityViewModels() 方法实现。

该方法将 ViewModel 的作用域限定在当前 Activity,使得所有 Fragment 都能访问并共享同一个 ViewModel 实例。

这种方式避免了 Fragment 之间的紧耦合,同时支持生命周期感知的数据共享,是现代 Android 架构中推荐的做法。

Kotlin 复制代码
// 共享 ViewModel
class SharedViewModel : ViewModel() {
    private val _userData = MutableStateFlow<User?>(null)
    val userData: StateFlow<User?> = _userData

    fun setUserData(user: User) {
        _userData.value = user
    }
}

// Fragment A(发送数据)
class FirstFragment : Fragment() {

    // activityViewModels !
    private val sharedViewModel: SharedViewModel by activityViewModels()

    fun updateUser(user: User) {
        sharedViewModel.setUserData(user)
    }
}

// Fragment B(在另一个 Fragment 中接收数据)
class SecondFragment : Fragment() {

    // activityViewModels !
    private val sharedViewModel: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
                sharedViewModel.userData.collectLatest { user ->
                    // 处理用户数据
                }
            }
        }
    }
}

// Activity(在 Activity 中接收数据)
class MainActivity : ComponentActivity() {
    private val sharedViewModel: SharedViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        lifecycleScope.launch {
            lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
                sharedViewModel.userData.collectLatest { user ->
                    // 处理用户数据
                }
            }
        }
    }
}

在这个例子中,FirstFragment 更新 SharedViewModel 中的数据,SecondFragmentMainActivity 都能实时响应这些变化,实现了跨组件的高效、安全的数据同步。

总结

  1. Intent 用于在 Activity 之间传递数据,通过 putExtra() 添加数据,接收方用 getIntent() 获取。
  2. Bundle 常用于 Fragment 之间通过 arguments 属性传递数据。
  3. 使用 Jetpack Navigation 配合 Safe Args 插件,可以通过自动生成的方向和参数类实现 Fragment 之间的类型安全传参。
  4. 当同一个 Activity 内的多个 Fragment 需要共享数据时,使用共享 ViewModel 是一种生命周期感知、解耦的解决方案。每种方式都有其适用场景,具体选择取决于应用的实际需求。

进阶:Fragment Result API

在某些场景下,Fragment 需要向另一个 Fragment 或其宿主 Activity 传递一个一次性值 。比如,一个扫描二维码的 Fragment 可能需要把扫描结果返回给前一个 Fragment

Fragment 版本 1.3.0 开始,每个 FragmentManager 都实现了 FragmentResultOwner 接口,允许 Fragment 通过结果监听器进行通信,而无需相互持有直接引用。这种方式简化了数据传递,同时保持了组件之间的松耦合。

要从 Fragment B(发送方)向 Fragment A(接收方)传递数据,只需按以下步骤操作:

  1. Fragment A 中设置一个结果监听器(接收方)。
  2. Fragment B 发送结果,并使用相同的 requestKey

第一,在 Fragment A 中设置结果监听器:

Fragment A 应使用 setFragmentResultListener() 注册监听器,确保在进入 STARTED 状态时能接收到结果。

Kotlin 复制代码
class FragmentA : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 注册监听器以接收结果
        setFragmentResultListener("requestKey") { _, bundle ->
            val result = bundle.getString("bundleKey")
            // 处理接收到的结果
        }
    }
}

setFragmentResultListener("requestKey") 会为指定的请求键注册一个监听器。当 Fragment 进入 STARTED 状态时,回调就会触发。

第二,从 Fragment B 发送结果:

Fragment B 使用 setFragmentResult() 发送结果,确保当 Fragment A 激活时能够获取到数据。

Kotlin 复制代码
class FragmentB : Fragment() {
    private lateinit var button: Button

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        button = view.findViewById(R.id.button)
        button.setOnClickListener {
            val result = "result"
            // 将结果发送给 FragmentA
            setFragmentResult("requestKey", bundleOf("bundleKey" to result))
        }
    }
}

setFragmentResult("requestKey", bundleOf("bundleKey" to result)) 会使用指定的键将结果存储在 FragmentManager 中。如果 Fragment A 当前未激活,该结果会被保存,直到 Fragment A 恢复并注册了监听器为止。

Fragment Result 的行为

  • 每个键对应一个监听器和一个结果 :每个 requestKey 在任意时刻只能有一个监听器和一个结果。
  • 待处理结果会被覆盖 :如果在监听器激活前设置了多个结果,只有最新的那个会被保留。
  • 结果一旦被消费就会清除 :当 Fragment 接收到结果并处理后,该结果会从 FragmentManager 中移除。
  • 后台栈中的 Fragment 无法接收结果 :只有当 Fragment 从返回栈中弹出并进入 STARTED 状态时,才能接收到结果。
  • 处于 STARTED 状态的监听器会立即触发 :如果 Fragment A 已经处于活跃状态,当 Fragment B 发送结果时,监听器会立刻执行

总之:

Fragment Result API 简化了 Fragment 之间传递一次性数据的过程,无需直接引用对方。它通过 FragmentManager 安全地存储结果,直到接收方激活,从而实现了一种解耦且生命周期感知的通信机制。

这种设计在多种场景下非常有用,比如二维码扫描、用户输入对话框、表单提交等,让基于 Fragment 的导航更高效、更易维护。


配置变更

当 Android 设备发生配置变更(例如屏幕旋转、主题切换、字体大小调整或语言更新)时,系统可能会销毁并重新创建当前的 Activity,以应用新的配置。这种行为确保了应用资源能根据新配置重新加载。

面试问题

开发者如何防止因配置变更导致的 Activity 重建过程中的数据丢失?

处理临时状态和持久状态有哪些方法?

默认行为

  1. Activity 的销毁与重建

    当配置变更发生时,Activity 会被销毁并重新创建,过程如下:

    • 系统依次调用当前 ActivityonPause()onStop()onDestroy() 方法。
    • 然后通过调用 onCreate() 方法,使用新的配置重新创建 Activity
  2. 资源重新加载

    系统会根据新的配置重新加载资源(如布局、图片、字符串等),使应用能够适配屏幕方向、主题或语言的变化。

  3. 防止数据丢失

    为了避免重建过程中数据丢失,开发者可以通过以下方式保存和恢复状态:

    • 使用 onSaveInstanceState()onRestoreInstanceState() 方法手动保存 UI 状态。
    • 或者借助 ViewModel 来管理生命周期感知的数据。
Kotlin 复制代码
override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putString("user_input", editText.text.toString())
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val restoredInput = savedInstanceState?.getString("user_input")
    editText.setText(restoredInput)
}

这段代码展示了如何在 onSaveInstanceState() 中保存用户输入,并在 onCreate() 中从 savedInstanceState 恢复,从而避免因配置变更导致的数据丢失。

常见的配置变更

  1. 屏幕旋转:在竖屏和横屏之间切换,导致布局需要重新加载以适配新的屏幕尺寸。
  2. 深色/浅色主题切换:当用户在深色模式和浅色模式之间切换时,应用会重新加载与主题相关的资源(如颜色、样式等)。
  3. 字体大小调整:设备字体大小设置的更改会触发文本资源的重新加载,以匹配新的缩放比例。
  4. 语言变更:系统语言的更新会触发本地化资源的加载(例如,切换为另一种语言的字符串资源)。

避免 Activity 重建

为了避免配置变更时重新创建 Activity,可以在 AndroidManifest.xml 中使用 android:configChanges 属性。这种方式将处理配置变更的责任交给开发者,通过代码手动响应变化。

xml 复制代码
<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|keyboardHidden" />

在这种情况下,系统不会销毁并重新创建 Activity,而是调用 onConfigurationChanged() 方法,允许开发者手动处理配置变更。

Kotlin 复制代码
override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        // 处理横屏相关逻辑
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        // 处理竖屏相关逻辑
    }
}

总结

当发生配置变更时,默认行为是销毁并重建 Activity,以重新加载资源适配新配置。开发者可以通过 onSaveInstanceState() 保存临时 UI 状态,或使用 ViewModel 管理非 UI 状态。若想避免重建,可在清单文件中使用 android:configChanges 属性,将处理逻辑交给开发者自行控制。


什么是 ActivityManager

ActivityManager 是 Android 系统中的一项系统服务,负责提供并管理设备上运行的 Activity、任务和进程的信息。它是 Android 框架的一部分,允许开发者与应用生命周期、内存使用和任务管理等方面进行交互和控制。

面试问题

如何使用 ActivityManager.getMemoryInfo() 来优化应用性能?

当系统进入低内存状态时,开发者应采取哪些措施?

主要功能:

  1. 任务与 Activity 信息ActivityManager 可以获取正在运行的任务、Activity 以及它们在栈中的状态详情。这有助于开发者监控应用行为和系统资源的使用情况。
  2. 内存管理:它提供了关于系统内存使用的相关信息,包括每个应用的内存消耗和全局内存状态。开发者可以利用这些信息来优化应用性能,并应对低内存场景。
  3. 应用进程管理ActivityManager 允许查询正在运行的应用进程和服务的详细信息。开发者可以借此检测应用状态,或响应进程级别的变化。
  4. 调试与诊断 :它提供了调试工具,例如生成 heap dump 或对应用进行性能分析,帮助识别性能瓶颈或内存泄漏问题。

常用方法

  • getRunningAppProcesses() :返回设备上当前正在运行的进程列表。
  • getMemoryInfo(ActivityManager.MemoryInfo memoryInfo) :获取系统详细的内存信息,例如可用内存、阈值内存以及设备是否处于低内存状态。该方法有助于在内存紧张时优化应用行为。
  • killBackgroundProcesses(String packageName) :终止指定应用的后台进程,以释放系统资源。适用于测试或管理资源密集型应用的场景。
  • isLowRamDevice() :检查设备是否被归类为低内存设备,帮助应用针对低内存设备优化资源使用。
  • appNotResponding(String message) :模拟"应用无响应"(ANR )事件,用于测试目的。调试时可用于观察应用在 ANR 情况下的表现和响应。
  • clearApplicationUserData() :清除与应用相关的所有用户数据,包括文件、数据库和共享偏好设置。常用于工厂重置或将应用恢复到初始状态的场景。

示例用法

以下代码展示了如何使用 ActivityManager 获取内存信息:

Kotlin 复制代码
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)

Log.d(TAG, "Low memory state: ${memoryInfo.lowMemory}")
Log.d(TAG, "Threshold memory: ${memoryInfo.threshold / (1024 * 1024)} MB")

val processes = activityManager.runningAppProcesses
Log.d(TAG, "Process name: ${processes.first().processName}")

// 方法用于通知系统应用卡住,希望触发 ANR
activityManager.appNotResponding("Pokedex is not responding")

// 允许应用清除自身存储在磁盘上的数据
activityManager.clearApplicationUserData()

在 LeakCanary 中的应用

LeakCanary 是一个由 Square 维护的开源 Android 内存泄漏检测库。

它在开发过程中自动监控并检测应用中的内存泄漏,提供详细的分析和可操作的建议,帮助开发者高效修复问题。

它利用了 ActivityManager 来追踪内存状态和系统信息。

总结

ActivityManager 用于系统级管理、性能调优和监控应用行为。尽管其部分功能已被现代 Android 中更专业的 API 所取代,但它仍然是管理与优化 Android 应用资源使用的重要工具。开发者应合理使用它,避免对系统性能造成意外影响。

相关推荐
TheNextByte13 小时前
如何轻松地将音乐从Mac传输到Android ?
android·stm32·macos
似霰3 小时前
HIDL Hal 开发笔记1----Android 8 HAL 变迁
android·framework·hal
SinFeeLoo_星凤楼3 小时前
Android Studio 中gradle.properties 中的中文注释显示乱码,如何解决?
android·ide·gradle·android studio·.properties
-suiyuan-3 小时前
sqli-labs靶场7笔记
android·笔记
zhlx28353 小时前
免费开源跨平台听歌自由!自定义音乐源 + 桌面歌词!LX Music 落雪音乐开源版
android·windows·macos
程序员码歌12 小时前
短思考第263天,每天复盘10分钟,胜过盲目努力一整年
android·前端·后端
安卓兼职framework应用工程师12 小时前
Android 10.0 按键智能机按键连续响两次的异常处理
android·audio·audioservice·按键音·按键声音
studyForMokey12 小时前
【Android 项目】个人学习demo随笔
android
吃喝不愁霸王餐APP开发者12 小时前
利用责任链模式解耦多平台(美团/饿了么)霸王餐接口的适配逻辑
android·责任链模式