面试题答案
一键面试常见内存泄漏场景及解决办法
- 非静态内部类持有外部类引用
- 场景:在 Kotlin 中,如果在 Activity 中定义一个非静态内部类,并且该内部类实例的生命周期长于 Activity。例如,使用非静态内部类创建一个线程,线程在后台运行,当 Activity 销毁时,由于内部类持有 Activity 的引用,导致 Activity 无法被回收,从而造成内存泄漏。
- 解决办法:将内部类声明为静态内部类。如果内部类需要访问外部类的成员,可以通过弱引用(
WeakReference
)来持有外部类实例。 - 优化措施:示例代码如下:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 使用静态内部类和弱引用
val staticInnerClass = MyStaticInnerClass(this)
staticInnerClass.start()
}
private class MyStaticInnerClass(private val activityRef: WeakReference<MainActivity>) : Thread() {
override fun run() {
// 访问 Activity 成员
val activity = activityRef.get()
if (activity != null) {
// 执行相关操作
}
}
}
}
- 注册监听器未注销
- 场景:在 Activity 或 Fragment 中注册了一些系统监听器(如
BroadcastReceiver
、SensorEventListener
等),但在组件销毁时没有及时注销监听器。由于监听器通常被系统或其他对象持有,而监听器又持有 Activity 或 Fragment 的引用,导致它们无法被回收,造成内存泄漏。 - 解决办法:在组件的
onDestroy
方法中注销监听器。例如,对于BroadcastReceiver
,在onCreate
方法中注册:
- 场景:在 Activity 或 Fragment 中注册了一些系统监听器(如
val intentFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
registerReceiver(broadcastReceiver, intentFilter)
在 onDestroy
方法中注销:
unregisterReceiver(broadcastReceiver)
- **优化措施**:可以封装注册和注销的逻辑到一个方法中,提高代码的可维护性。同时,可以使用 `LocalBroadcastManager` 代替全局的 `BroadcastReceiver`,减少内存泄漏风险。
3. 资源对象未关闭
- 场景:在使用一些资源对象(如 Cursor
、FileInputStream
、FileOutputStream
等)时,如果没有在使用完毕后及时关闭,这些资源对象会一直持有对 Activity 或相关上下文的引用,导致内存泄漏。
- 解决办法:在使用完资源对象后,调用其 close
方法。例如,对于 Cursor
:
val cursor = contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)
try {
if (cursor != null) {
// 处理 cursor 数据
}
} finally {
cursor?.close()
}
- **优化措施**:使用 Kotlin 的 `use` 扩展函数,它会在代码块执行完毕后自动关闭资源。例如:
contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)?.use { cursor ->
// 处理 cursor 数据
}
- Fragment 重叠
- 场景:在 Fragment 事务管理中,如果重复添加 Fragment 而没有正确处理,可能会导致 Fragment 重叠,新的 Fragment 显示在旧的 Fragment 之上,旧的 Fragment 由于没有被移除,仍然持有视图和上下文的引用,造成内存泄漏。
- 解决办法:在添加 Fragment 之前,先检查该 Fragment 是否已经添加。例如:
val fragmentTransaction = supportFragmentManager.beginTransaction()
val existingFragment = supportFragmentManager.findFragmentByTag("MyFragmentTag")
if (existingFragment == null) {
val myFragment = MyFragment()
fragmentTransaction.add(R.id.fragment_container, myFragment, "MyFragmentTag")
}
fragmentTransaction.commit()
- **优化措施**:使用 `replace` 方法代替 `add` 方法,`replace` 方法会先移除容器中已有的 Fragment,再添加新的 Fragment,避免重叠问题。
5. 集合中对象未清理
- 场景:如果在 Activity 或 Fragment 中创建了一个集合(如 List
、Map
等),并向其中添加了对象,当 Activity 或 Fragment 销毁时,如果没有清理集合中的对象,这些对象仍然被集合持有,而集合又被 Activity 或 Fragment 持有,导致内存泄漏。
- 解决办法:在 Activity 或 Fragment 的 onDestroy
方法中清空集合。例如:
val myList = mutableListOf<Any>()
// 添加元素到列表
myList.add(someObject)
override fun onDestroy() {
super.onDestroy()
myList.clear()
}
- **优化措施**:考虑使用弱引用集合(如 `WeakHashMap`),这样当集合中的对象不再有其他强引用时,它们可以被垃圾回收。