如何避免 context.getSystemService(Context.CAMERA_SERVICE) 中的内存泄漏?
How to avoid memory leak in context.getSystemService(Context.CAMERA_SERVICE)?
我在 Android 5.x Camera2 API 中发现内存泄漏,我也 reported。问题是当您使用 Android Lollipop 设备时,该设备在 LEGACY
模式下实现了 Camera2 API。在这样的设备上,调用 context.getSystemService(Context.CAMERA_SERVICE)
会导致 context
被保留,这样它就不会被垃圾回收。
如果这个 context
是你的 activity 并且多次启动,你最终可能会挂起对你的 activity 的数十个实例的挂起引用,这些实例永远不会被垃圾收集。
此问题似乎只发生在 LEGACY
模式下实施了 Camera2 API 的 Lollipop 设备上(例如 HTC One M8、Samsung Galaxy S4),而在 Samsung Galaxy S6 上不会发生在 FULL
模式下实现 Camera2 API。
为了演示这个问题,我创建了一个 small demo app. The app contains two activities: first that contains a button which calls the second activity. The second activity obtains the CameraManager
and queries the level of Camera2 API support for first back-facing camera and returns the result to first activity. If you run the app on device that implements Camera2 API in LEGACY
mode, after tapping the button 98 times, causing GC and then dumping HPROF you will see exactly 98 live instances of Main2Activity
, like this http://www.pohrani.com/f/1H/gs/4EFlHKoj/sgs4.png
如果您在 FULL
模式下实现 Camera2 API 的设备上执行相同操作,您将看到 Main2Activity
的 0 个活动实例,例如 http://www.pohrani.com/f/2q/bV/4srUZIJL/sgs6.png
有没有办法解决这个漏洞?
有人可能会问我为什么要这样做?在我们公司,我们正在开发barcode and OCR scanning solutions and also a famous PhotoMath app。所以我们有一个 scan activity 来控制相机和扫描过程。启动时,activity 检查设备是否在 FULL
或 LIMITED
模式下支持 Camera2 API 并尝试使用它以获得更好的性能,而如果 Camera2 API处于 LEGACY
模式,那么我们更喜欢使用旧相机 API 使用相机管理,就像我们在 Lollipop 之前的设备上所做的那样。
由于提到的内存泄漏,每当将我们的SDK集成到其应用程序中的客户端开始扫描activity,执行扫描并获得结果时,扫描activity的一个实例将由于错误而被泄露。如果客户端扫描很多,这会占用超过 20 MB 的内存 - 一个严重的问题!
因此,如果有人知道如何解决此问题,我将不胜感激!
Is there a way to workaround this leak?
您可以在 Application
单例上调用 getSystemService()
。所以,而不是:
getSystemService(CAMERA_SERVICE)
你会使用:
getApplicationContext().getSystemService(CAMERA_SERVICE)
如果您的评估是正确的,那么这将导致那些额外的引用指向现有的 Application
单例,它始终存在于您的进程中。它实际上是 "pre-leaked",您不能通过对它的更多引用来进一步泄漏它。
这是一个 Android 错误,已在 L MR1 中修复。
基本上,CameraManager 保留了对其创建时使用的上下文的引用,然后连接到相机服务。此连接使相机管理器实例无限期地保持活动状态,因此也使上下文无限期地保持活动状态。
这已在 L MR1 中修复,以便在不再引用时正确地回收 CameraManager 对象。
我在 Android 5.x Camera2 API 中发现内存泄漏,我也 reported。问题是当您使用 Android Lollipop 设备时,该设备在 LEGACY
模式下实现了 Camera2 API。在这样的设备上,调用 context.getSystemService(Context.CAMERA_SERVICE)
会导致 context
被保留,这样它就不会被垃圾回收。
如果这个 context
是你的 activity 并且多次启动,你最终可能会挂起对你的 activity 的数十个实例的挂起引用,这些实例永远不会被垃圾收集。
此问题似乎只发生在 LEGACY
模式下实施了 Camera2 API 的 Lollipop 设备上(例如 HTC One M8、Samsung Galaxy S4),而在 Samsung Galaxy S6 上不会发生在 FULL
模式下实现 Camera2 API。
为了演示这个问题,我创建了一个 small demo app. The app contains two activities: first that contains a button which calls the second activity. The second activity obtains the CameraManager
and queries the level of Camera2 API support for first back-facing camera and returns the result to first activity. If you run the app on device that implements Camera2 API in LEGACY
mode, after tapping the button 98 times, causing GC and then dumping HPROF you will see exactly 98 live instances of Main2Activity
, like this http://www.pohrani.com/f/1H/gs/4EFlHKoj/sgs4.png
如果您在 FULL
模式下实现 Camera2 API 的设备上执行相同操作,您将看到 Main2Activity
的 0 个活动实例,例如 http://www.pohrani.com/f/2q/bV/4srUZIJL/sgs6.png
有没有办法解决这个漏洞?
有人可能会问我为什么要这样做?在我们公司,我们正在开发barcode and OCR scanning solutions and also a famous PhotoMath app。所以我们有一个 scan activity 来控制相机和扫描过程。启动时,activity 检查设备是否在 FULL
或 LIMITED
模式下支持 Camera2 API 并尝试使用它以获得更好的性能,而如果 Camera2 API处于 LEGACY
模式,那么我们更喜欢使用旧相机 API 使用相机管理,就像我们在 Lollipop 之前的设备上所做的那样。
由于提到的内存泄漏,每当将我们的SDK集成到其应用程序中的客户端开始扫描activity,执行扫描并获得结果时,扫描activity的一个实例将由于错误而被泄露。如果客户端扫描很多,这会占用超过 20 MB 的内存 - 一个严重的问题!
因此,如果有人知道如何解决此问题,我将不胜感激!
Is there a way to workaround this leak?
您可以在 Application
单例上调用 getSystemService()
。所以,而不是:
getSystemService(CAMERA_SERVICE)
你会使用:
getApplicationContext().getSystemService(CAMERA_SERVICE)
如果您的评估是正确的,那么这将导致那些额外的引用指向现有的 Application
单例,它始终存在于您的进程中。它实际上是 "pre-leaked",您不能通过对它的更多引用来进一步泄漏它。
这是一个 Android 错误,已在 L MR1 中修复。
基本上,CameraManager 保留了对其创建时使用的上下文的引用,然后连接到相机服务。此连接使相机管理器实例无限期地保持活动状态,因此也使上下文无限期地保持活动状态。
这已在 L MR1 中修复,以便在不再引用时正确地回收 CameraManager 对象。