更新日志¶
请感谢我们的贡献者 🙏 🙏 🙏。
版本 3.0 Alpha 8 (2024-06-04)¶
- 增加了对堆增长检测失败时 hprof 的正确处理支持。同时内联了一些公共协作类以实现此目的。我们现在有一个单一的类,它稍大一些,但更显而易见。
findGrowingObjects
不再接受CloseableHeapGraph
(只接受HeapGraph
),也不关闭该 graph。
版本 3.0 Alpha 7 (2024-05-30)¶
重新审视了堆增长相关的许多 API 选择,以简化并支持更高级的行为,例如在测试失败时保留堆转储,或压缩堆转储以便上传到 CI。
HeapGrowthTraversal
现在是HeapDiff
ObjectGrowthDetector.forAndroidHeap().repeatingAndroidInProcessScenario()
现在是 HeapDiff.repeatingAndroidInProcessScenario(),它实际上只是一个带有 Android UI 测试特定配置的 HeapDiff.repeatingDumpingTestScenario()
包装器。maxHeapDumps
和scenarioLoopsPerDump
已从工厂参数移至每个场景的参数。- 💥 #2683 修复了当 java.lang.Object 在 JVM 堆转储中包含多个类加载记录时崩溃的问题
- 🔨 #2682 添加对卸载类标签和记录的支持
堆增长:Espresso 测试示例¶
dependencies {
androidTestImplementation 'com.squareup.leakcanary:leakcanary-android-test:3.0-alpha-7'
}
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Performing the heap growth analysis in process requires more heap. -->
<application
android:largeHeap="true"/>
</manifest>
class MyEspressoTest {
val detector = HeapDiff.repeatingAndroidInProcessScenario()
@Test
fun greeter_says_hello_does_not_grow_heap() {
// Runs repeatedly until the heap stops growing or we reach max heap dumps.
val heapDiff = detector.findRepeatedlyGrowingObjects {
onView(withId(R.id.name_field)).perform(typeText("Steve"))
onView(withId(R.id.greet_button)).perform(click())
onView(withText("Hello Steve!")).check(matches(isDisplayed()))
}
assertThat(heapDiff.growingObjects).isEmpty()
}
}
堆增长:JVM Junit 测试示例。¶
dependencies {
androidTestImplementation 'com.squareup.leakcanary:leakcanary-jvm-test:3.0-alpha-7'
}
class MyUnitTest {
val detector = HeapDiff.repeatingJvmInProcessScenario()
val growingList = mutableListOf<String>()
@Test
fun failing_test() {
// Runs repeatedly until the heap stops growing or we reach max heap dumps.
val heapDiff = detector.findRepeatedlyGrowingObjects {
growingList += "Hi at ${System.currentTimeMillis()}"
}
// This should fail.
assertThat(heapDiff.growingObjects).isEmpty()
}
}
堆增长:UI Automator 测试示例。¶
dependencies {
androidTestImplementation 'com.squareup.leakcanary:leakcanary-android-uiautomator:3.0-alpha-7'
}
class MyUiAutomatorTest {
val detector = HeapDiff.repeatingUiAutomatorScenario()
@Test
fun clicking_welcome_does_not_grow_heap() {
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// Runs repeatedly until the heap stops growing or we reach max heap dumps.
val heapDiff = detector.findRepeatedlyGrowingObjects {
device.findObject(By.text("Welcome!")).click()
}
assertThat(heapDiff.growingObjects).isEmpty()
}
}
堆增长:Shark CLI¶
下载 Shark CLI Zip(alpha 版本无法发布到 brew)并解压。
运行 heap-growth
命令
$ ~/Downloads/shark-cli-3.0-alpha-7/bin/shark-cli -p com.example.app.debug heap-growth
查看完整差异。
版本 3.0 Alpha 6 (2024-05-21)¶
- 🐛 #2670 对
Toast.makeText
使用RequestPermissionActivity
上下文。
堆增长¶
- 添加预热以减少由于类加载与分析相关而被误判为堆增长信号的变化。
- 添加对 AndroidX Collections 的依赖,以替代 JDK 数据结构(内存占用更低)和 HHPC 复制的数据结构(难以维护)。
- 通过扁平化许多对象来改进内存占用
- 修复了分析在第二次堆转储时停止时保留大小未计算的问题。
- 移除
InitialState.heapGraphCount
- 在 JVM 上忽略所有类中的静态
<resolved_references>
查看完整差异。
版本 3.0 Alpha 4 (2024-05-10)¶
- 删除了
shark-heap-growth
artifact,代码已合并到shark*
和leakcanary*
模块中。 - 新增
leakcanary-android-test
和leakcanary-android-uiautomator
artifacts。 - 撤销了在 alpha 1 中引入的重大 API 变更。目标是使升级无缝。如果您发现从 2.x 版本升级时存在任何 API 重大变更,请提交问题。
- 优化:对于已知的数据结构,如果它们除了我们已知的引用之外不再引用 graph 的其他部分,我们会立即在本地探索它们,并停止将其内部结构排队,这减少了内存占用和 IO 读取。
- 改进了堆增长检测 API,增加了对 UI Automator 和 Shark CLI 的支持。
(注意:我跳过了 alpha 1 到 alpha 4,因为我在几个版本中搞砸了。抱歉!)
堆增长:Espresso 测试示例¶
添加依赖项
dependencies {
androidTestImplementation 'com.squareup.leakcanary:leakcanary-android-test:3.0-alpha-2'
}
通过更新 src/androidTest/AndroidManifest.xml
确保您的 UI 测试拥有足够的堆内存
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Performing the heap growth analysis in process requires more heap. -->
<application
android:largeHeap="true"/>
</manifest>
class MyEspressoTest {
val detector = ObjectGrowthDetector
.forAndroidHeap()
.repeatingAndroidInProcessScenario()
@Test
fun greeter_says_hello_does_not_leak() {
// Runs repeatedly until the heap stops growing or we reach max heap dumps.
val heapGrowth = detector.findRepeatedlyGrowingObjects {
onView(withId(R.id.name_field)).perform(typeText("Steve"))
onView(withId(R.id.greet_button)).perform(click())
onView(withText("Hello Steve!")).check(matches(isDisplayed()))
}
assertThat(heapGrowth.growingObjects).isEmpty()
}
}
堆增长:UI Automator 测试示例。¶
添加依赖项
dependencies {
androidTestImplementation 'com.squareup.leakcanary:leakcanary-android-uiautomator:3.0-alpha-4'
}
class MyUiAutomatorTest {
val detector = ObjectGrowthDetector
.forAndroidHeap()
.repeatingUiAutomatorScenario()
@Test
fun clicking_welcome_does_not_leak() {
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// Runs repeatedly until the heap stops growing or we reach max heap dumps.
val heapGrowth = detector.findRepeatedlyGrowingObjects {
device.findObject(By.text("Welcome!")).click()
}
assertThat(heapGrowth.growingObjects).isEmpty()
}
}
堆增长:Shark CLI¶
下载 Shark CLI Zip(alpha 版本无法发布到 brew)并解压。
运行 heap-growth
命令
$ ~/Downloads/shark-cli-3.0-alpha-2/bin/shark-cli -p com.example.app.debug heap-growth
版本 2.14 (2024-04-17)¶
- 🐛 #2650 移除了对
SettableFuture
的意外使用,它是WorkManager
的一个内部类,将在 WorkManager 的未来版本中移除。更新 WorkManager 到该未来版本后,所有从 2.8 到 2.13 的 LeakCanary 版本将在泄漏分析时崩溃。为避免将来出现问题,请更新到 LeakCanary 2.14。 - 🔨 #2660 为 LeakCanary 发布版添加 proguard 映射支持。
- 🐛 #2531 修复了导航时堆转储和泄漏列表不保留列表位置的问题。
- 🐤 #2615 自动修复 AOSP PermissionControllerManager 泄漏 (issuetracker.google.com/issues/318415056)。
- 🐤 #2559 忽略
UiModeManager
AOSP 泄漏。 - 💥 #2643 修复了 RenderHeapDumpScreen 上罕见的崩溃问题。
版本 3.0 Alpha 1 (2024-01-09)¶
此 alpha 版本标志着 LeakCanary 3 开发工作的开始。它尚不稳定!虽然我打算重构一些 API,但也希望最大程度地减少迁移工作。确保平稳迁移的最佳方法是尝试升级到 3.0 alpha 版本,并告知我是否遇到任何编译或运行时错误。
堆增长¶
新增 API,尚不稳定:shark-heap-growth
artifact 包含用于编写检测重复堆增长的测试场景的 API。
以下是如何与 Espresso 测试一起使用
class MyEspressoTest {
@Test
fun greeter_says_hello_does_not_leak() {
// Runs in a loop until the heap stops growing or we reach max heap dumps.
val heapTraversal = HeapGrowthDetector.detectRepeatedHeapGrowth {
// Runs repeatedly until the heap stops growing or we reach maxHeapDumps.
onView(withId(R.id.name_field)).perform(typeText("Steve"))
onView(withId(R.id.greet_button)).perform(click())
onView(withText("Hello Steve!")).check(matches(isDisplayed()))
}
assertThat(heapTraversal.growingNodes).isEmpty()
}
}
以下是设置示例,目前所有这些都非常手动。
添加新依赖项
dependencies {
androidTestImplementation 'com.squareup.leakcanary:shark-heap-growth:3.0-alpha-1'
androidTestImplementation 'com.squareup.leakcanary:leakcanary-android-core:3.0-alpha-1'
}
为 Espresso 进程内 UI 测试创建实现设置
import leakcanary.AndroidDebugHeapDumper
import shark.AndroidReferenceMatchers
import shark.AndroidReferenceReaderFactory
import shark.CloseableHeapGraph
import shark.DiffingHeapGrowthDetector
import shark.HeapGraphProvider
import shark.HeapTraversal
import shark.HprofHeapGraph.Companion.openHeapGraph
import shark.IgnoredReferenceMatcher
import shark.LiveHeapGrowthDetector
import shark.LoopingHeapGrowthDetector
import shark.MatchingGcRootProvider
import shark.ReferencePattern.InstanceFieldPattern
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
/**
* Heap growth detector for in process Espresso UI tests.
*
* Call [LiveHeapGrowthDetector.detectRepeatedHeapGrowth] with a scenario to repeat,
* then assert that the resulting [shark.HeapTraversalWithDiff.growingNodes] is empty.
*/
val HeapGrowthDetector by lazy {
val referenceMatchers = AndroidReferenceMatchers.appDefaults +
HeapTraversal.ignoredReferences +
// https://cs.android.com/android/_/android/platform/frameworks/base/+/6985fb39f07294fb979b14ba0ebabfd2fea06d34
IgnoredReferenceMatcher(InstanceFieldPattern("android.os.StrictMode", "sLastVmViolationTime"))
val dateFormat = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS'-heap-growth.hprof'", Locale.US)
val uploadedTracesDirectory = File("/sdcard/traces/")
uploadedTracesDirectory.mkdirs()
check(uploadedTracesDirectory.exists()) {
"Expected heap dump folder to exist: ${uploadedTracesDirectory.absolutePath}"
}
val heapGraphProvider = HeapGraphProvider {
val fileName = dateFormat.format(Date())
val heapDumpFile = File(uploadedTracesDirectory, fileName)
AndroidDebugHeapDumper.dumpHeap(heapDumpFile)
check(heapDumpFile.exists()) {
"Expected file to exist after heap dump: ${heapDumpFile.absolutePath}"
}
val realGraph = heapDumpFile.openHeapGraph()
object : CloseableHeapGraph by realGraph {
override fun close() {
realGraph.close()
heapDumpFile.delete()
}
}
}
LiveHeapGrowthDetector(
maxHeapDumps = 5,
heapGraphProvider = heapGraphProvider,
scenarioLoopsPerDump = 5,
detector = LoopingHeapGrowthDetector(
DiffingHeapGrowthDetector(
referenceReaderFactory = AndroidReferenceReaderFactory(referenceMatchers),
gcRootProvider = MatchingGcRootProvider(referenceMatchers)
)
)
)
}
通过更新 src/androidTest/AndroidManifest.xml
确保您的 UI 测试拥有足够的堆内存
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Performing the heap growth analysis in process requires more heap. -->
<application
android:largeHeap="true"/>
</manifest>
引用读取器¶
新增 API,尚不稳定:ReferenceReader
实现(又称 expanders)现在是公共 API。名称可能会更改。这些类定义了 LeakCanary 如何遍历 graph,并允许创建虚拟引用,如此处介绍的。这些新 API 使添加对更多自定义数据结构的支持成为可能,并且在直接使用 shark
API 时也很有用(例如,这些 API 是构建上述堆增长检测工具所必需的)。
ObjectWatcher API 重构¶
#2612 是重构 reachability API 的首次尝试。我已发现向后兼容性问题 (#2617),将在下一个 alpha 版本中修复,并且可能还会更改 API 形状。
此次重构的总体目标是摆脱当前实现的静态单例和高耦合性,使 reachability API 在各种上下文中更具实用性。
其他 Bug 修复及改进 🐛🔨¶
- 将 Kotlin 升级到 1.8.21
- 放弃了对检测 support fragment 泄漏的支持:现在应用都应该已迁移到 Android X,如果还没有,它们可以轻松地自行添加实现。
此列表仅反映了部分变更。有关更多详细信息,请查看完整差异。
版本 2.13 (2024-01-03)¶
- 🐛 #2565 修复了 AndroidX Fragments 在 detached 但未 destroyed 时被错误地标记为泄漏的问题。
- 💥 #2568 修复了在 API 34+ 上调用 registerReceiver() 时缺少 RECEIVER_EXPORTED flag 的问题。
- 🔨 #2555 Binder stubs 现在在泄漏跟踪中被指出。
- 🐤 #2601 添加了一些已知的制造商和框架泄漏。
版本 2.12 (2023-06-29)¶
- 💥 #2527 androidx.lifecycle:lifecycle-runtime 中的 LifecycleRegistry 已迁移到 kotlin,其 mState 字段名更改为 state,这打破了 LeakCanary 的预期。
- 🐤 #2545 添加了一些已知的制造商和框架泄漏。
版本 2.11 (2023-05-17)¶
- 🐛 #1764 忽略已卸载后又重新加载的 phantom classes(长期存在的 LeakCanary bug)。
- 🐛 #2471 修复了 LeakCanary 在 Google CI 基础设施中引入的奇怪泄漏。
- 🐛 #2496 修复了损坏的 ViewModel 泄漏检测功能。
版本 2.10 (2022-11-10)¶
实验性 Neo4j 堆转储探索¶
shark-cli
有一个新的实验性 neo4j
命令,可以将堆转储转换为嵌入式 Neo4j 数据库,然后打开 Neo4j Browser 以探索堆转储。
brew install leakcanary-shark
shark-cli --process com.example.app.debug neo4j
其他 Bug 修复及改进 🐛🔨¶
- 🐤 #2440 添加 Android 13
POST_NOTICICATIONS
权限,以及一个新的LeakCanary.Config.showNotifications
配置项,用于完全禁用通知。 - 🐤 #2416 添加 Android 13 单色图标。
- 💥 #2371 修复了导航到堆转储屏幕时 db 崩溃的问题。
- 🐛 #2393 允许将 LeakCanary 定义为 AndroidX Startup 的依赖项。
- 💥 #2430 修复了 Android TV 上的 ShortcutManager 崩溃问题。
- 💥 #2382 修复了堆转储关闭崩溃问题。
此列表仅反映了部分变更。有关更多详细信息,请参阅 2.10 Milestone 和完整差异。
版本 2.9.1 (2022-04-20)¶
前言¶
在未来的 LeakCanary 3 版本中,您希望看到哪些功能?请在Twitter上告诉我!
我正在思考的一些想法
- 将堆分析泄漏可视化功能迁移到一个独立的单一应用(使用 Compose 编写!),并在 PlayStore 上提供。
- 将 Okio 升级到 3.0
- 多平台堆分析?在浏览器中分析 JVM 堆转储?!
- 将堆 dominators / 保留大小可视化为树状图。
- LeakCanary 的后端?
总之,这些都还是未来的计划,现在让我们聊聊 2.9.1 版本有什么!
堆分析元数据中的新指标¶
我构建 LeakCanary 的目的是帮助修复泄漏,但在此过程中,我不经意间写了一个相当灵活的堆转储解析器。既然我们无论如何都在解析堆以查找泄漏,不妨也报告一些其他有趣的指标。以下是您现在将在堆转储元数据中看到的内容
- 类计数:已加载类的数量
- 实例计数
- 基本类型数组计数
- 对象数组计数
- 线程计数
- 堆总字节数
- Bitmap 计数
- Bitmap 总字节数
- 大 Bitmap 计数(像素数超过屏幕像素数 1.1 倍的 Bitmap)
- 大 Bitmap 总字节数
- 内存中的 SQLiteDatabase(开放或关闭,及其文件路径)
这只是第一步,欢迎提供反馈和想法!
性能改进¶
堆分析现在使用 RandomAccessFile
遍历堆转储,而不是 FileChannel.transferTo()
,在 API 23 上速度提升 40%,在较新的 API 上提升 20%。此外,sticky class GC roots 现在已去重,这极大地减少了 LeakCanary 在 API 23 上的内存占用 (#2324)。您可以在 py.hashnode.dev 上阅读相关调查。
重大变更:FailTestOnLeakRunListener 已删除¶
FailTestOnLeakRunListener
、FailTestOnLeak
和 FailAnnotatedTestOnLeakRunListener
在 LeakCanary 2.8 中已弃用,因为它们依赖于 hack Android Test 库内部实现,而这些内部实现已经改变,现已被 LeakAssertions.assertNoLeak()
和 DetectLeaksAfterTestSuccess
test rule 所取代。我最初计划保留这些类,但随着我在 LeakCanary 中尝试增加 API level 支持范围,我需要将 Android Test 库升级到更新的版本,而这些 hack 现在导致了编译错误。所以它们被移除了:#2282。如果您暂时无法使用这些 test rule,欢迎您将 listener 实现复制到您自己的代码库中。
其他 Bug 修复及改进 🐛🔨¶
- 💥 #2367 修复了
AndroidLeakFixes.FLUSH_HANDLER_THREADS
(HandlerThread
可以有 nullLooper
)。 - 💥 #2286 更新 Curtains 以包含 Proguard 规则并防止 WindowCallbackWrapper 崩溃。
- 💥 #2294 修复了
WindowDelegateCallback.onMenuOpened()
崩溃的问题。 - 🐤 #2328 修复了 ToastEventListener 泄漏。抱歉 😬!
- 💥 #2310 修复了使用 WorkManager < 2.1.0 时崩溃的问题。
- 💥 #2342 修复了
HashSet.map
为 null 时崩溃的问题(这不应该发生,好吧,Android 🤷♂️)。 - 🐛 #2117 修复了 StrictMode 磁盘读取违规问题。
- 💥 #2351 修复了导致启动崩溃的竞态条件问题。
- 💥 #2315 修复了使用 Okio 1.14 时崩溃的问题。
- 🐛 #2182 修复了 BackgroundListener$checkAppInBackground 的多次重新调度问题。
- 💥 #2360 修复了 SQLiteOpenHelper 并发创建崩溃问题。
此列表仅反映了部分变更。有关更多详细信息,请参阅 2.9 Milestone 和完整差异。
版本 2.8.1 (2022-01-06)¶
这是一个 bug 修复版本,是对 2.8 版本(包含一些主要问题 😅)的快速跟进。如果您还没有,强烈建议您阅读 2.8 版本的更新日志。
致谢¶
请感谢 @dicosta, @Goooler, @plnice, @preetha1326 的贡献、bug 报告和功能请求 🙏 🙏 🙏。
崩溃修复 💥💥💥¶
这个补丁版本修复了不止 1 个、不止 2 个,而是 3 个崩溃问题!
- 💥 #2268 WorkManager expedited request 在 API 31 之前崩溃。
- 💥 #2270 当 AppWatcher 未安装时,更新 LeakCanary.config 会崩溃。
- 💥 #2271 在 API 25 上分析失败,因为 HashMap$Entry 在最终更改为 HashMap$Node 之前变成了 HashMap$HashMapEntry(在 API 25 上)。
有关更多详细信息,请参阅 2.8.1 Milestone 和完整差异。
版本 2.8 (2022-01-04)¶
注意:请更新到 2.8.1 版本。
前言¶
上一个版本是 9 个月前发布的。发生了什么?!嗯,在发布 LeakCanary 2.7 后不久,我有了第二个宝宝,一个可爱的女儿 😍。有两个年幼的孩子让我投入开源工作的时间大大减少……但这一切都值得!
― P.Y.
致谢¶
请感谢 @aaronweihe, @alhah, @Andre-max, @AoraMD, @BraisGabin, @breezenan, @Goooler, @iliaskomp @Jeff11, @jmnwong, @IdioticMadman, @keyur1sst, @lchen8, @leinardi, @Maragues, @mars885, @mateuszkwiecinski, @matiash, @maxxx, @preetha1326, @SimonMarquis, @slavonnet, @Sonphil, @summerlyr, @SUPERCILEX, @utwyko, @ZacSweers, @ziranshang, @zoltish 的贡献、bug 报告和功能请求 🙏 🙏 🙏。
改进了对数据结构内部的支持¶
🤓 受 Android Studio 启发,LeakCanary 在 heap graph 遍历期间的节点发现现在已被抽象出来。这允许在常见数据结构内部叠加逻辑结构。
😅 什么?!
👉 这意味着我们可以让已知的数据结构看起来更像它们的 API,而不是它们的内部结构。例如,开发者更倾向于将设置 HashMap 条目视为 map["key"] = value
,而不是 map.table[hash("key")].next.next.next = Node(value)
,这是 LeakCanary 以前在其泄漏跟踪中显示的方式。
让我们看一个 HashMap 示例
class CheckoutController {
val tabs = HashMap<String, Tab>()
fun addItemsTab(tab: Tab) {
tabs["ItemsTab"] = tab
}
}
如果 Tab 实例持有视图的引用,我们可能会看到如下所示的泄漏跟踪
│ ...
├─ com.example.CheckoutController instance
│ ↓ CheckoutController.tabs
├─ java.util.HashMap instance
│ ↓ HashMap.table
├─ java.util.HashMap$Node[] array
│ ↓ HashMap$Node[42]
├─ java.util.HashMap$Node instance
│ ↓ HashMap$Node.next
├─ java.util.HashMap$Node instance
│ ↓ HashMap$Node.value
├─ com.example.Tab instance
│ ...
借助改进的数据结构支持,泄漏跟踪更加清晰(另请注意 ItemsTab 字符串键如何现在被显示出来)
│ ...
├─ com.example.CheckoutController instance
│ ↓ CheckoutController.tabs
├─ java.util.HashMap instance
│ ↓ HashMap[ItemsTab]
├─ com.example.Tab instance
│ ...
这项变更的另一个好处是,泄漏签名对运行时的依赖性降低,因此更加一致。对于依赖链表(HashMap, LinkedList, MessageQueue 等)的任何数据结构尤其如此。目前 LeakCanary 支持 Apache Harmony, Open JDK 和 Android SDK 中的一套有限的常见数据结构。如果您还需要其他支持,请告诉我!
ObjectAnimator 泄漏¶
LeakCanary 现在将检测忘记取消 ObjectAnimator 时触发的泄漏。此新功能得益于上述节点发现的变更!
假设您不小心启动了一个无限循环的 ObjectAnimator 并且从未取消它,例如这样
class ExampleActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
findViewById<Button>(R.id.button).setOnClickListener { view ->
ObjectAnimator.ofFloat(view, View.ALPHA, 0.1f, 0.2f).apply {
duration = 100
repeatMode = ValueAnimator.REVERSE
repeatCount = ValueAnimator.INFINITE
start()
}
}
}
}
在以前的版本中,LeakCanary 会检测到动画视图正在泄漏,但它无法找到泄漏路径,而是将其报告为不可达对象:一个不可达对象仍然在内存中,但 LeakCanary 找不到从 GC roots 到它的强引用路径。
LeakCanary 现在会报告泄漏并添加动画器状态信息,帮助检测和修复任何无限循环的 ObjectAnimator。
┬───
│ GC Root: Thread object
│
├─ java.lang.Thread instance
│ Leaking: NO (the main thread always runs)
│ Thread name: 'main'
│ ↓ Thread.threadLocals
│ ~~~~~~~~~~~~
...
├─ android.animation.ObjectAnimator instance
│ Leaking: UNKNOWN
│ mListeners = null
│ mPropertyName = null
│ mProperty.mName = alpha
│ mProperty.mType = java.lang.Float
│ mInitialized = true
│ mStarted = true
│ mRunning = true
│ mAnimationEndRequested = false
│ mDuration = 100
│ mStartDelay = 0
│ mRepeatCount = INFINITE (-1)
│ mRepeatMode = REVERSE (2)
│ ↓ ObjectAnimator.mTarget
│ ~~~~~~~
╰→ android.widget.Button instance
Leaking: YES (View.mContext references a destroyed activity)
要了解更多信息,请参阅此 AOSP 问题:ObjectAnimator.mTarget weak ref Creates memory leaks on infinite animators。
在测试中检测泄漏¶
leakcanary-android-instrumentation
的先前版本引入了 FailTestOnLeakRunListener
,它可以在每次 UI 测试后运行泄漏检测。不幸的是,FailTestOnLeakRunListener
依赖于 hack androidx.test 的内部实现来报告失败。androidx.test 的内部实现随着每个版本不断变化,导致 FailTestOnLeakRunListener
损坏 😭。
FailTestOnLeakRunListener
现在已被弃用 (👋),并由 DetectLeaksAfterTestSuccess
test rule 取代,您可以像添加任何普通 test rule 一样将其添加到您的测试中。
此外,您可以在 instrumentation tests 中的任何地方调用 LeakAssertions.assertNoLeak()
。您还可以使用 @SkipLeakDetection
注解测试(为此,您还需要设置 TestDescriptionHolder
test rule)。
class CartTest {
@get:Rule
val rules = RuleChain.outerRule(TestDescriptionHolder)
.around(DetectLeaksAfterTestSuccess())
.around(ActivityScenarioRule(CartActivity::class.java))
@Test
fun addItemToCart() {
// ...
}
@SkipLeakDetection("See #1234")
@Test
fun removeItemFromCart() {
// ...
}
}
Android 12¶
希望这次我们修复了 Android 12 导致的所有问题:缺失的 exported:true
标签、缺失的 pending intent flag 和 ForegroundServiceStartNotAllowedException
崩溃。如果还有问题,请告诉我们!迫不及待地想看 Android 13 再次破坏一切 🤬。
WorkManager¶
在不崩溃的情况下运行 Android Service (ForegroundServiceStartNotAllowedException
...) 随着 Android 每个版本的发布变得越来越困难,所以我移除了 LeakCanary heap analyzer service!取而代之的是,如果您已经依赖 WorkManager,LeakCanary 会利用它。如果您不使用 WorkManager,那么 LeakCanary 将回退到使用一个简单的线程。
注意:我建议至少使用 WorkManager 2.7.0 版本,因为它添加了 WorkRequest.Builder.setExpedited()
API,LeakCanary 如果可用就会利用该 API。
多进程¶
切换到 WorkManager 也影响了 LeakCanary 的多进程方法,现在它利用 WorkManager remote jobs。有关我如何实现这一点的博客文章:WorkManager multi-process for libraries。
多进程更难正确实现,因此只有当 LeakCanary 在执行堆分析时频繁出现内存不足情况时才应使用此方法。以下是更新后的设置步骤
1) 添加 leakcanary-android-process
依赖项并保留 leakcanary-android
依赖项。
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8'
debugImplementation 'com.squareup.leakcanary:leakcanary-android-process:2.8'
}
2) 跳过 Application 类中的初始化代码
class ExampleApplication : Application() {
override fun onCreate() {
if (LeakCanaryProcess.isInAnalyzerProcess(this)) {
return
}
super.onCreate()
// normal init goes here, skipped in :leakcanary process.
}
}
就是这样!请注意,与分析相关的事件监听器(见下文)将在 remote process 中触发。
AndroidX App Startup¶
LeakCanary 现在可选支持 AndroidX App Startup 库。您只需将 leakcanary-android
依赖项替换为 leakcanary-android-startup
。
dependencies {
// Remove the normal leakcanary-android dependency
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8'
debugImplementation 'com.squareup.leakcanary:leakcanary-android-startup:2.8'
}
注意 1:leakcanary-android
会将自动安装代码添加到 leakcanary-android-core
中。如果您调用 AppWatcher.manualInstall()
,您可以直接依赖 leakcanary-android-core
而不是 leakcanary-android
,这样您就不需要禁用任何自动安装了。
注意 2:同样的原理适用于 leakcanary-object-watcher-android
:它依赖于 leakcanary-object-watcher-android-core
并添加自动安装,而 leakcanary-object-watcher-android-startup
则利用 App Startup 库。plumber-android
, plumber-android-core
和 plumber-android-startup
也是如此。
事件监听器¶
LeakCanary.Config
有一个新的 eventListeners
字段,允许您对 LeakCanary 的生命周期做出反应。如果您想自定义它,最有可能应该保留默认的监听器列表,并在此基础上添加或移除。
例如,如果您想禁用 LeakCanary toast
LeakCanary.config = LeakCanary.config.run {
copy(
eventListeners = eventListeners.filter {
it !is ToastEventListener
}
)
}
如果您想上传堆分析结果
LeakCanary.config = LeakCanary.config.run {
copy(
eventListeners = eventListeners + EventListener { event ->
if (event is HeapAnalysisSucceeded) {
// Upload event.heapAnalysis
}
}
)
}
注意:Leakcanary.Config.onHeapAnalyzedListener
仍然可用,但现已弃用。
欢迎对这个新 API 提供反馈!
其他 Bug 修复及改进 🐛🔨¶
此列表仅反映了部分变更。有关更多详细信息,请参阅 2.8 Milestone 和完整差异。
版本 2.7 (2021-03-26)¶
请感谢 @chao2zhang, @ihrupin, @jzbrooks, @msfjarvis, @reneargento, @Unpublished 的贡献、bug 报告和功能请求 🙏 🙏 🙏。
更细粒度的根视图监听¶
在版本 2.6 中,LeakCanary 增加了检测 View.onDetachedFromWindow()
后保留的根视图的功能。这有助于找到更多泄漏,但不幸的是,一些 Android 控件会保留一个 detached 的根视图,以便稍后重新附加它(例如 spinner)。应用开发者有时也会对 dialogs 进行同样的操作,保留一个实例,并根据需要调用 show()
和 hide()
。结果是,LeakCanary 会报告实际上不是泄漏的问题。
在版本 2.7 中,默认行为发生了变化:LeakCanary 将继续检测 toasts 的泄漏,但会忽略由 PopupWindow 创建的根视图(这是 Android 控件使用的)。默认情况下,它还会忽略由 dialog 创建的根视图,您可以通过将 leak_canary_watcher_watch_dismissed_dialogs
资源布尔值设置为 true 来重新启用此功能。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="leak_canary_watcher_watch_dismissed_dialogs">true</bool>
</resources>
这是使用新的 Square 库 Curtains 实现的。
面向 Android 12¶
我们为想要面向 Android 12 的应用修复了两个问题
- #2074 使用 intent filters 的 Activity 必须声明
android:exported
属性。 - #2079 PendingIntent 需要
FLAG_IMMUTABLE
标志。
Bug 修复和改进 🐛🔨¶
- #2075 修复了分享堆转储文件时崩溃的问题。
- #2067 修复了从旧版本(2.6 之前)LeakCanary 打开泄露时崩溃的问题。
- #2049 修复了由于 R8 混淆 AndroidLeakFixes 导致 Plumber 崩溃的问题。
- #2084 修复了 Shark 在多线程中使用时崩溃的问题。
- #2054 🙈🙉🙊 阻止了 Monkey 删除泄露。
- #2069 在根泄露 Activity 中添加了 X 按钮(用于没有返回键的自定义设备)
- #2091 如果 LoadedApk 出现在泄露跟踪中,则添加接收器详细信息。
- #2083 在泄露跟踪中添加了服务状态详细信息(是否已创建)。
- #2099 如果分析失败,添加重试按钮。
- #2066 在 UI 测试中跳过堆分析并返回 NoAnalysis 时,NoAnalysis 现在包含一个原因,以帮助调试为何未运行。
- #2000 LeakCanary CI 现在利用 GitHub Actions 而不是 Travis。
更多详细信息,请参阅 2.7 Milestone 和 完整差异。
版本 2.6 - 圣诞节发布 🎄 (2020-12-24)¶
请感谢 @chao2zhang, @ChaosLeung, @LitterSun, @mickverm, @opatry, @Thomas-Vos, @tricknology, @rahul-a, @samoylenkodmitry, @sing0055, @ubiratansoares 的贡献、bug 报告和功能请求 🙏 🙏 🙏。
这个圣诞节发布版本包含了多项外部贡献和许多很酷的新功能!🎁🎁
检测在 View.onDetachedFromWindow()
后被持有的根视图¶
在 Android 上,每个显示的视图层级都连接到一个窗口,无论是 activity、对话框、toast 还是 聊天气泡的视图层级。视图层级与其窗口分离后,应该被垃圾回收。
LeakCanary 已经可以检测 activity 视图层级的泄露,因为被持有的分离视图引用了它们的 activity context,并且 LeakCanary 会检测在 Activity.onDestroy()
后被持有的 activity。在这个新版本中,LeakCanary 现在将在对话框被关闭后立即检测对话框视图层级的泄露,或任何传递给 WindowManager.removeView() 的其他视图的泄露。
检测在 Service.onDestroy()
后被持有的服务¶
Android 服务销毁后,应该被垃圾回收。不幸的是,Android SDK 没有提供任何通用 API 来观察服务的生命周期。我们通过对灰名单 API 使用反射来解决了这个问题(详情见 #2014)。希望这能激励 Android 团队构建开发者所需的 API。
配置被持有对象的检测¶
随着新增 2 种被持有对象的检测类型,我们还添加了 API 来配置应该安装哪些 watcher,并增加了过滤功能。
首先,禁用自动安装
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="leak_canary_watcher_auto_install">false</bool>
</resources>
然后你可以手动安装 LeakCanary。LeakCanary 2.6 默认安装了 4 个 watcher:ActivityWatcher
、FragmentAndViewModelWatcher
、RootViewWatcher
、ServiceWatcher
。这是一个获取除 ServiceWatcher
之外所有默认 watcher 的示例
class DebugExampleApplication : ExampleApplication() {
override fun onCreate() {
super.onCreate()
val watchersToInstall = AppWatcher.appDefaultWatchers(application)
.filter { it !is ServiceWatcher }
AppWatcher.manualInstall(
application = application,
watchersToInstall = watchersToInstall
)
}
}
LeakCanary 引入了一个新的由 ObjectWatcher
实现的函数式(SAM)接口:ReachabilityWatcher
,其中包含一个 ReachabilityWatcher.expectWeaklyReachable()
方法,取代了现在已弃用的 ObjectWatcher.watch()
方法。你可以创建一个自定义的 ReachabilityWatcher
来创建默认 watcher 实例,该 watcher 会委托给 AppWatcher.objectWatcher
但会过滤掉特定实例(例如 BadSdkLeakingFragment
)
class DebugExampleApplication : ExampleApplication() {
override fun onCreate() {
super.onCreate()
val delegate = ReachabilityWatcher { watchedObject, description ->
if (watchedObject !is BadSdkLeakingFragment) {
AppWatcher.objectWatcher.expectWeaklyReachable(watchedObject, description)
}
}
val watchersToInstall = AppWatcher.appDefaultWatchers(application, delegate)
AppWatcher.manualInstall(
application = application,
watchersToInstall = watchersToInstall
)
}
}
有了这些新的配置选项,AppWatcher.config
现在已弃用且不再生效。
熄屏时进行堆转储¶
默认的堆转储阈值是当应用**可见**时有 **5 个被持有对象**,当应用**不可见**时有 **1 个被持有对象**。到目前为止,可见意味着“应用至少有一个 activity 处于 **started** 状态”。在 LeakCanary 2.6 中,如果设备屏幕**关闭**,应用现在将被视为**不可见**,从而降低了在熄屏时触发堆转储的阈值。
LeakCanary 用于发布版本¶
LeakCanary 2.6 引入了一个新的 artifact:leakcanary-android-release
。这个 artifact 提供了在发布版本中运行堆分析的 API。
危险
这完全是实验性的。在生产环境中运行堆分析并不常见,我们仍在学习和试验这一点。此外,artifact 名称和 API 都可能发生变化。
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
// NEW: LeakCanary for releases!
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-release:2.6'
// Optional: detect retained objects. This helps but is not required.
releaseImplementation 'com.squareup.leakcanary:leakcanary-object-watcher-android:2.6'
}
这是一个代码示例,它在屏幕关闭或应用进入后台时运行堆分析,首先检查 Firebase Remote Config 标志是否开启,然后将结果上传到 Bugsnag
class ReleaseExampleApplication : ExampleApplication() {
// Cancels heap analysis if "heap_analysis_flag" is false.
private val flagInterceptor by lazy {
object : HeapAnalysisInterceptor {
val remoteConfig: FirebaseRemoteConfig = TODO()
override fun intercept(chain: Chain): HeapAnalysisJob.Result {
if (remoteConfig.getBoolean("heap_analysis_flag")) {
chain.job.cancel("heap_analysis_flag false")
}
return chain.proceed()
}
}
}
private val analysisClient by lazy {
HeapAnalysisClient(
// Use private app storage. cacheDir is never backed up which is important.
heapDumpDirectoryProvider = { cacheDir },
// stripHeapDump: remove all user data from hprof before analysis.
config = HeapAnalysisConfig(stripHeapDump = true),
// Default interceptors may cancel analysis for several other reasons.
interceptors = listOf(flagInterceptor) + HeapAnalysisClient.defaultInterceptors(this)
)
}
private val analysisExecutor by lazy {
Executors.newSingleThreadExecutor {
thread(start = false, name = "Heap analysis executor") {
android.os.Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND)
it.run()
}
}
}
private val analysisCallback: (Result) -> Unit by lazy {
val uploader = BugsnagHeapAnalysisUploader(this@ReleaseExampleApplication)
{ result ->
if (result is Done) {
uploader.upload(result.analysis)
}
}
}
override fun onCreate() {
super.onCreate()
// Delete any remaining heap dump (if we crashed)
analysisExecutor.execute {
analysisClient.deleteHeapDumpFiles()
}
// Starts heap analysis on background importance
BackgroundTrigger(
application = this,
analysisClient = analysisClient,
analysisExecutor = analysisExecutor,
analysisCallback = analysisCallback
).start()
// Starts heap analysis when screen off
ScreenOffTrigger(
application = this,
analysisClient = analysisClient,
analysisExecutor = analysisExecutor,
analysisCallback = analysisCallback
).start()
}
/**
* Call this to trigger heap analysis manually, e.g. from
* a help button.
*
* This method returns a `HeapAnalysisJob` on which you can
* call `HeapAnalysisJob.cancel()` at any time.
*/
fun triggerHeapAnalysisNow(): HeapAnalysisJob {
val job = analysisClient.newJob()
analysisExecutor.execute {
val result = job.execute()
analysisCallback(result)
}
return job
}
}
Bugsnag 上传器
class BugsnagHeapAnalysisUploader(applicationContext: Application) {
private val bugsnagClient: Client
init {
bugsnagClient = Client(
applicationContext,
BUGSNAG_API_KEY,
DO_NOT_ENABLE_EXCEPTION_HANDLER
)
bugsnagClient.setSendThreads(false)
}
fun upload(heapAnalysis: HeapAnalysis) {
when (heapAnalysis) {
is HeapAnalysisSuccess -> {
val exception = HeapAnalysisReport()
bugsnagClient.notify(exception) { report ->
val metaData = report.error.metaData
metaData.addToTab("Heap Analysis", "result", heapAnalysis.toString())
}
}
is HeapAnalysisFailure -> {
// Please file any reported failure to
// https://github.com/square/leakcanary/issues
bugsnagClient.notify(heapAnalysis.exception)
}
}
}
// Exception with fake unique stacktrace to send all reports to the same error entry.
class HeapAnalysisReport : Exception("Check the HEAP ANALYSIS tab") {
override fun fillInStackTrace(): Throwable {
stackTrace = arrayOf(
StackTraceElement(
"HeapAnalysisReport",
"analyzeHeap",
"HeapAnalysisReport.kt",
1
)
)
return this
}
}
companion object {
private const val BUGSNAG_API_KEY = YOUR_BUGSNAG_API_KEY
private const val DO_NOT_ENABLE_EXCEPTION_HANDLER = false
}
}
Plumber 中更多的泄露修复¶
我们在 plumber-android
中为已知的 AOSP 泄露添加了 3 个新的自动修复(详情:#1993)。提醒一下,当你添加 leakcanary-android
时,plumber-android
会自动包含进来,你也可以手动添加它,用于不包含 LeakCanary 的构建类型
dependencies {
// leakcanary-android adds plumber-android to debug builds
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
// This adds plumber-android to all build types
implementation 'com.squareup.leakcanary:plumber-android:2.6'
}
Bug 修复和改进 🐛🔨¶
- #1948 Leakcanary 现在针对 Kotlin 1.4 编译(同时保持与 1.3 兼容),以支持函数式(SAM)接口。
- #1956 被持有对象的大小以人类可读的格式显示(KB, MB, ...)。
- #1976 改进了
View
和Context
的默认对象检查器和泄露查找器。 - #1972 在泄露跟踪中,字段会与其所在的父类名称一起打印。
- #1981 修复了 StrictMode 策略违规问题(主线程从磁盘读取)。
- #1977 报告非强可达的对象。
- #2018 和 #2019 修复了 LeakCanary UI 中的崩溃问题(通过 Monkey 测试发现)。
- #2015 修复了 Android < 16 上的崩溃问题。
- #2023 修复了插件项目中的崩溃问题。
更多详细信息,请参阅 2.6 Milestone 和 完整差异。
版本 2.5 (2020-10-01)¶
请感谢 @Amokrane, @Armaxis, @askont, @chao2zhang, @daniil-shevtsov, @eygraber, @msfjarvis, @mzgreen, @lchen8, @rpattabi, @sahil2441, @SylvainGirod, @vhow 的贡献、bug 报告和功能请求 🙏 🙏 🙏。
堆分析速度提高一倍 🐤💨¶
没有人要求,所以我们做到了!我们重写了 Shark(LeakCanary 的堆分析器)中的几个核心组件,以显著减少 IO 读取和分配,同时保持内存恒定。更多详情请关注 Twitter:@ArtemChubaryan 的推文 和 @Piwai 的推文。
在泄露跟踪中计算被持有大小¶
以前,LeakCanary 计算的是泄露对象(泄露跟踪中的最后一个对象)的被持有大小。然而,导致对象泄露的错误引用通常在泄露跟踪中更靠上的位置,并且它持有的所有内容实际上都在泄露。因此,LeakCanary 现在会计算泄露跟踪中所有状态为 LEAKING 或 UNKNOWN 的对象的被持有大小。
┬───
│ GC Root: System class
│
├─ com.example.MySingleton class
│ Leaking: NO (a class is never leaking)
│ ↓ static MySingleton.leakedView
│ ~~~~~~~~~~
├─ android.widget.TextView instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ Retaining 46326 bytes in 942 objects
│ ↓ TextView.mContext
╰→ com.example.MainActivity instance
Leaking: YES (Activity#mDestroyed is true)
Retaining 1432 bytes in 36 objects
从 UI 中禁用 LeakCanary¶
新增一个开关来禁用堆转储,这对于 QA 或进行产品演示很有用。当对象被持有时,LeakCanary 仍会显示通知。
去混淆 hprof 文件¶
brew install leakcanary-shark
shark-cli --hprof heapdump.hprof -m mapping.txt deobfuscate-hprof
Bug 修复和改进 🐛🔨¶
- 现在从 LeakCanary activity 分享堆分析文本结果或打印到 logcat 时,结果会限制最大宽度并自动换行。这使得长文本行末尾的细节更不容易被遗漏。
leak_canary_watcher_auto_install
、leak_canary_allow_in_non_debuggable_build
和leak_canary_plumber_auto_install
这几个资源布尔值本应是 public 的。- 我们增加了一些 `@JvmStatic` 来帮助 Java 用户。
- 修复了未安装浏览器时崩溃的问题。
- 为 LeakCanary 通知使用独立的通知分组。
- 堆分析结果现在包含堆转储持续时间,因为 Android 11 的堆转储有时非常慢。我们还添加了更多与性能相关的指标。
- 当 AppWatcher 在发布版本中运行时禁用日志输出。
- 直接在泄露跟踪中高亮显示库泄露模式。
- 改进了对 Context, View 和 ContextImpl 的检查。
更多详细信息,请参阅 2.5 Milestone 和 完整差异。
版本 2.4 (2020-06-10)¶
请感谢 @0x109, @andersu, @antoniomerlin, @bishiboosh, @ckesc, @jrodbx, @LouisCAD, @marcardar, @OlivierGenez, @pyricau, @runningcode, @seljad, @worldsnas 的贡献、bug 报告和功能请求。
plumber-android
是一个修复已知 Android 泄露的新 artifact 🚽🔧¶
LeakCanary 会报告所有泄露,包括由第三方代码中您无法控制的已知 bug 引起的泄露(报告为库泄露)。这可能很烦人!LeakCanary 现在附带了一个新的依赖 plumber-android
,它在运行时执行 hack 来修复其中一些已知泄露。这个版本包含了对 **11 个已知泄露**的修复,但这只是一个开始。欢迎贡献!🙏
注意,由于 leakcanary-android
依赖通常作为 debugImplementation
依赖添加,plumber-android
只在调试构建中传递添加,因此它不会修复发布构建中的泄露。你可以将该依赖直接添加为 implementation
,以便在发布构建中也获得这些修复。
dependencies {
implementation 'com.squareup.leakcanary:plumber-android:2.4'
}
警告
虽然其中一些修复已在 Square 应用的发布版本中发布,但这仍是 plumber-android
的第一个正式版本,因此您应将其视为**实验性**。
从 CLI 分析泄露比以往任何时候都更容易 🍺¶
brew install leakcanary-shark
然后你可以在任何连接的设备上的应用中查找泄露,例如
$ shark-cli --device emulator-5554 --process com.example.app.debug analyze
支持 Android Test Orchestrator 🎼¶
如果你设置了 LeakCanary 在检测到 instrumentation 测试中的泄露时报告测试失败,它现在也支持 Android Test Orchestrator 了。无需更改,LeakCanary 会自动检测 Android Test Orchestrator 是否正在运行并与其集成。
不再使用 master
分支¶
分支名称 master
来自于 主/从 (master / slave) 术语。我们将默认分支重命名为 main
,这是朝着使 LeakCanary 社区成为更安全空间迈出的一小步。这是一篇关于这个主题的很好的讨论串。
Bug 修复和改进 🐛🔨¶
- 库泄露描述中的 URL 现在可以点击了
- 修复了导致手动设置配置不正确的排序问题。相关的更改是
AppWatcher.Config.enabled
现在已弃用。 - 修复了计算被持有大小时可能发生的OutOfMemoryError 失败:我们之前会将堆转储中的大数组加载到内存中只是为了获取它们的大小。
更多详细信息,请参阅 2.4 Milestone 和 完整差异。
版本 2.3 (2020-04-08)¶
这在功能方面是一个小版本,但在文档方面是一个大版本!
非常感谢 @adamfit, @Amokrane, @Armaxis, @artnc, @burakeregar, @ClaasJG, @clementcontet, @ckesc, @cketti, @fbenbassat, @Guneetgstar, @Igorxp5, @JLLeitschuh, @KidAndroid, @ligi, @mzgreen, @pyricau, @sprintuu, @tevjef, @thrlr123 的贡献、bug 报告和功能请求。
我们寻求帮助后立即看到了更多的贡献。感谢大家!请查看如何提供帮助页面。
LeakCanary 现在将在发布版本中崩溃¶
尽管文档强调使用 debugImplementation
,我们仍然看到有些应用在**发布版本**中包含了 LeakCanary。错误难免发生,所以我们通过让 LeakCanary 在**发布版本中包含时崩溃**来使这个错误更难被忽视。
了解更多:发布版本中的 LeakCanary。
文档网站 🛀💥¶
文档网站的内容变化很大!我们采纳了 Google 技术写作指南的建议。如果您之前对 库泄露 感到困惑,请查看新的泄露分类部分。浏览一下,告诉我们您的想法。
Bug 修复和改进 😉¶
- 新的去混淆 Gradle 插件之前与 Gradle 3.6 不兼容,现在已修复。此外,也移除了插件必须在 AGP 后应用的要求。
- 修复了 LeakCanary UI 中的 2 个崩溃问题(#1768 和 #1769),这些问题由一个狡猾的 monkey runner 🙈 发现。
- LeakCanary UI 在添加或删除堆分析时会立即更新。
- 修复了从 LeakCanary 2.0 更新时的崩溃问题。
- LeakCanary 的 关于 部分现在会显示堆转储是否当前已启用。
- 修复了 SharkCLI 在 Windows 上进程无法退出的问题。
- 改进了当混淆映射文件似乎丢失时的错误消息。
更多详细信息,请参阅 2.3 Milestone 和 完整差异。
版本 2.2 (2020-02-05)¶
这个十年来的第一个发布版本带来了一些好东西!
非常感谢 @AndroidInternal, @Armaxis, @lic2050, @mzgreen, @orenktaboola, @personshelldon, @Plastix, @pyricau 的贡献、bug 报告和功能请求。
ViewModel 泄露检测¶
Android ViewModel 非常棒!它们的生命周期比 fragment 或 activity 好得多,但有时也会发生错误。LeakCanary 现在将自动检测 ViewModel 泄露,并报告在其 onCleared()
方法被调用后仍被持有的任何 ViewModel 实例。
Android TV¶
LeakCanary 终于登上您身边的大屏幕了!最棒的是——无需额外设置,就像为移动设备启用它一样简单。现在只要有泄露,您就会看到一个带有所有详细信息的 helpful Toast 弹出。请务必查看我们的新Android TV部分,放松一下!
Java 友好型配置 Builder¶
我们注意到,从 Java 代码配置 LeakCanary
和 AppWatcher
是一件痛苦的事情。现在不会了!
现在您可以使用 LeakCanary.Config.Builder
和 AppWatcher.Config.Builder
在更新配置时使用符合 Java 习惯的方式。例如
LeakCanary.Config config = LeakCanary.getConfig().newBuilder()
.retainedVisibleThreshold(3)
.computeRetainedHeapSize(false)
.build();
LeakCanary.setConfig(config);
如果您在使用 Java 代码使用 LeakCanary 时发现任何其他问题,请提交 issue!我们非常重视 Java 互操作性,并乐意改进 LeakCanary 的 API!
更多详细信息,请参阅 2.2 Milestone 和 完整差异。
版本 2.1 (2019-12-31)¶
特别的除夕发布 🥳,下个版本将在另一个十年发布 😎!
非常感谢 @adamfit, @alexander-smityuk, @Armaxis, @BraisGabin, @devism, @ditclear, @jrodbx, @jstefanowski, @Maragues, @mzgreen, @pyricau 的贡献、bug 报告和功能请求。
用于混淆应用的 Gradle 插件¶
团队拥有在发布版本之前进行测试的 QA 构建版本是很常见的。通常该构建版本会进行混淆(通过 Proguard 或 R8),但也添加 LeakCanary 以便在 QA 期间检测泄露。这会导致混淆的泄露跟踪,很难理解 🤯。请查看我们的新Gradle 去混淆插件,高兴吧!
UItwix调整¶
在 2.0 版本中,我们改变了 LeakCanary 的 UI 和 UX,并在此基础上构建了 2.1 的扩展。
- 自 2.0 版本以来,泄露按其独特的签名进行分组。在 2.1 版本中,有一个
New
标签,在您打开泄露之前会一直显示。还有一个Library Leak
标签,用于标记已知由 Android Framework 或 Google 库中的 bug 引起的泄露,并且库泄露的描述现在会显示在 UI 中。 - Java 对象的类型(类、实例、数组)现在会显示在泄露跟踪中,例如上面看到的
FontsContract class
和ExampleApplication instance
。 - GC 根的类型现在会显示在泄露跟踪的根部。这很合理!
- 泄露结果通知现在的重要性已设置为 MAX,以便它会直接显示在您面前。如果您将其关闭,金丝雀会在您的梦中困扰您 🐤👻。为了保持您的理智和设备电池,自动堆转储现在每分钟不会发生超过一次。
View
实例的资源 id 名称现在会显示在泄露跟踪中。您不应该查看实现细节。
├─ android.widget.TextView instance
│ View.mID = R.id.helper_text
文档改进¶
- 基本原理页面被完全重写,拆分为 3 个页面并移到单独的标签页。请阅读并提供反馈!
- 在 Square,我们已经将泄露上传到 Bugsnag 3 年了,这样就不会遗漏任何泄露。请按照这个方法操作!
- 您知道可以在 JVM 中运行 LeakCanary 吗?
API破坏性改进性更改¶
Leak
和LeakTrace
类的 API 发生了显著变化,例如所有具有相同签名的LeakTrace
实例都归入同一个 Leak 对象。尽管存在这些破坏性更改,这个发布版本却是一个次要更新。天哪,语义化版本控制怎么办 😱?去问唐吉诃德吧。- 您现在可以自定义 LeakCanary 在堆转储中查找泄露对象的方式。例如,这是 SharkCli 用于查找甚至没有 LeakCanary 依赖的应用的堆转储中的泄露的配置
LeakCanary.config = LeakCanary.config.copy(
leakingObjectFinder = FilteringLeakingObjectFinder(
AndroidObjectInspectors.appLeakingObjectFilters
)
)
- LeakCanary 通过检测 classpath 中是否存在
org.junit.Test
来自动在测试中禁用自己。不幸的是,有些应用在调试 classpath 中包含了 Junit(例如使用 OkHttp MockWebServer 时)。您现在可以自定义用于检测测试的类。
<resources>
<string name="leak_canary_test_class_name">assertk.Assert</string>
</resources>
交互式 CLI¶
$ shark-cli
Usage: shark-cli [OPTIONS] COMMAND [ARGS]...
^`. .=""=.
^_ \ \ / _ _ \
\ \ { \ | d b |
{ \ / `~~~--__ \ /\ /
{ \___----~~' `~~-_/'-=\/=-'\,
\ /// a `~. \ \
/ /~~~~-, ,__. , /// __,,,,) \ |
\/ \/ `~~~; ,---~~-_`/ \ / \/
/ / '. .'
'._.' _|`~~`|_
/|\ /|\
Options:
-p, --process NAME Full or partial name of a process, e.g.
"example" would match "com.example.app"
-d, --device ID device/emulator id
-m, --obfuscation-mapping PATH path to obfuscation mapping file
--verbose / --no-verbose provide additional details as to what
shark-cli is doing
-h, --hprof FILE path to a .hprof file
--help Show this message and exit
Commands:
interactive Explore a heap dump.
analyze Analyze a heap dump.
dump-process Dump the heap and pull the hprof file.
strip-hprof Replace all primitive arrays from the provided heap dump with
arrays of zeroes and generate a new "-stripped.hprof" file.
有一个新的 interactive
命令,允许从命令行浏览堆转储。
$ shark-cli -h heapdump.hprof interactive
Enter command [help]:
help
Available commands:
analyze Analyze the heap dump.
class NAME@ID Show class with a matching NAME and Object ID.
instance CLASS_NAME@ID Show instance with a matching CLASS_NAME and Object
ID.
array CLASS_NAME@ID Show array instance with a matching CLASS_NAME and
Object ID.
->instance CLASS_NAME@ID Show path from GC Roots to instance.
~>instance CLASS_NAME@ID Show path from GC Roots to instance, highlighting
suspect references.
help Show this message.
exit Exit this interactive prompt.
我们目前正在探索增加对 SQL 查询的支持的想法,欢迎提供反馈!
更多详细信息,请参阅 2.1 Milestone 和 完整差异。
版本 2.0 (2019-11-27)¶
在过去的 7 个月里,LeakCanary 经历了 3 个 alpha 版本和 5 个 beta 版本,汇集了 23 位贡献者,共提交了 493 次 commit,增加了 35826 行代码,删除了 10156 行代码。
我应该升级吗?¶
**是的!** LeakCanary 2 改进巨大,您可能会因为发现新的内存泄露而感到兴奋。请按照升级指南操作,您不会后悔的!
那么,自 1.6.3 版本以来有什么变化?¶
**一切都变了。** LeakCanary 的代码库从大约 **6000** 行 Java 代码增加到大约 **16000** 行 Kotlin 代码(不包括注释和空行)。
Kotlin 不是应该大幅减少样板代码吗?
当然!并且确实减少了。但后来,我们又写了更多的代码。LeakCanary 以前依赖于 HAHA,它是 Android Studio 使用的堆转储解析器 perflib 的重打包版本。不幸的是,perflib 很慢并且使用了过多的内存,因此 LeakCanary 现在包含了它自己的堆转储解析器:Shark。额外增加的代码来自于 Shark,但也来自于增加了大量的自动化测试和改进的 UI 层。
一个主要区别是:当应用在前台时,LeakCanary 2 不会对每一个被持有的实例都触发。相反,它会等到应用进入后台,或在前台达到 5 个被持有实例的阈值。然后分析会一次性找出所有泄露,并在结果 UI 中对相同的泄露进行分组。请阅读基本原理部分了解更多!
小知识¶
- 您可以自定义泄露启动器图标和标签:在此了解更多。
- 如果您长按主 activity 启动器图标,您应该会看到 LeakCanary 动态快捷方式。然后您可以长按它将其拖放到主屏幕上,启动器会显示这是您应用的泄露启动器。
- 开箱即用,LeakCanary 支持所有类型的 fragment:AOSP、Support Library 和 Android X。
- 在泄露详情屏幕内,您可以将泄露分享到 stack overflow。您还可以分享堆转储文件,或从其他设备导入并分析堆转储文件。
- 您可以从计算机命令行运行 LeakCanary,对任何可调试的应用进行操作,即使该应用没有包含 LeakCanary:在此了解更多。
- 新的文档完全可搜索,并包含 API 文档。试试搜索栏 ⤴。
- 一个 160MB 的大型堆转储文件在 Android Studio 中打开时会占用 2GB 内存,但使用 Shark 只占用 40MB。
自 2.0 Beta 5 以来的变化¶
- Shark CLI 支持连接多个设备 #1642
- 修复了 Maven Central 中缺少 source 的问题 #1643
- 更新了通知图标,避免与 Twitter DM 通知混淆,并在底部导航栏添加了图标 #1648
- 自动检测支持库 fragment 的泄露 #1611
非常感谢 @AndreasBoehm, @jrodbx, @pyricau 的贡献、bug 报告和功能请求。
更多详细信息,请参阅 2.0 Milestone 和 完整差异。
版本 2.0 Beta 5 (2019-11-25)¶
- 主要 bug 修复:Beta 4 中意外忽略了 native gc root,导致一些泄露未被发现 #1634
- 修复了 Lint 警告(
leak_canary_about_message
字符串触发了 多个替代 警告) #1630
非常感谢 @DanEdgarTarget, @msfjarvis, @PaulWoitaschek, @pyricau, @ZacSweers 的贡献、bug 报告和功能请求。
更多详细信息,请参阅 2.0-beta-5 Milestone 和 完整差异。
版本 2.0 Beta 4 (2019-11-18)¶
- 改进了堆分析结果的字符串渲染
- UX 重新设计 #1445
- 支持 native 引用泄露的模式匹配 #1562
- 在 Shark 中增加了使用 Proguard 映射文件进行去混淆的支持 #1499。这在 LeakCanary 中尚未直接支持。
- 增加了从堆转储中提取元数据的支持(参见方法) #1519
- 改进了在单元测试和 UI 测试中自动禁用 LeakCanary 的功能 #1552
- 解析堆转储时的多项性能改进
- 修复了多个 bug 和崩溃问题
- 增加了新的已知泄露模式
非常感谢 @Armaxis, @BraisGabin, @bric3, @elihart, @fernandospr, @flickator, @gabrysgab, @JorgeDLS, @lannyf77, @msfjarvis, @mzgreen, @ozmium, @PaulWoitaschek, @pyricau, @shelpy, @vRallev, @ZacSweers 的贡献、bug 报告和功能请求。
欲了解更多详情,请参阅 2.0-beta-4 里程碑 和完整差异。
版本 2.0 Beta 3 (2019-08-22)¶
- 大型 hprof 的基线内存使用量减少了三分之二,并消除了内存峰值 #1543
- 修复了从另一个进程初始化 LeakCanary 时崩溃的问题 #1529
- Java 本地引用被降权,以寻找更长的替代路径 #1525
- 修复了
JavaLocalPattern
在 Lollipop 上不匹配的问题 #1524
非常感谢 @Armaxis、@elihart、@emartynov、@hmcgreevy-instil、@pyricau 的贡献、错误报告和功能请求。
欲了解更多详情,请参阅 2.0-beta-3 里程碑 和完整差异。
版本 2.0 Beta 2 (2019-08-02)¶
非常感谢 @kolphi、@pyricau、@ZacSweers 的贡献、错误报告和功能请求。
欲了解更多详情,请参阅 2.0-beta-2 里程碑 和完整差异。
版本 2.0 Beta 1 (2019-07-30)¶
- 新的独立库!Shark 是支持 LeakCanary 2 的堆分析器,它可以在任何 Java VM 中运行。它附带了一个 CLI:您现在可以在计算机上运行
shark-cli analyze-process com.example.myapp
。 - 新的设备内嵌堆浏览器!在 LeakCanary 中打开堆分析,点击选项菜单并选择“Heap Explorer”。这仍然是实验性的,用户体验不太友好,欢迎贡献!
- 大规模 API 重写以提高可用性。如果您使用了带有自定义配置的 alpha 版本,则存在重大更改。值得注意的是:LeakSentry 更名为 AppWatcher,RefWatcher 更名为 ObjectWatcher,AndroidExcludedRefs 更名为 AndroidReferenceMatchers,AnalysisResultListener 更名为 OnHeapAnalyzedListener,AndroidLeakTraceInspectors 更名为 AndroidObjectInspectors。
- 整个 API 表面现在都有文档记录,文档可在本网站上找到:请参阅顶部的LeakCanary API选项卡。
- 移除了对 Android X 的依赖。不再有配置问题了!#1462
- 添加了 LeakCanary 和 ObjectWatcher 的 Proguard 规则。 #1500
- 在“关于”屏幕中显示 LeakCanary 版本。 #1448
- 错误修复,新的引用匹配器和对象检查器
非常感谢 @arctouch-carlosottoboni、@jemaystermind、@kushagrakumar27、@pyricau、@snkashis 的贡献、错误报告和功能请求。
欲了解更多详情,请参阅 2.0-beta-1 里程碑 和完整差异。
版本 2.0 Alpha 3 (2019-07-04)¶
- #1401 LeakCanary 现在可以导入所有由早期 LeakCanary 版本创建的 hprof 文件。
- #1414 新 API:
RefWatcher.retainedInstances
,它返回当前被认为是保留的实例。 - #1419 新 API:
LeakCanary.Config.maxStoredHeapDumps
(默认 7) 和LeakCanary.Config.requestWriteExternalStoragePermission
(默认 false)。LeakCanary 默认不再请求外部存储权限。 - #1338 API 更改:
LeakCanary.Config.exclusionsFactory
被LeakCanary.Config.knownReferences
(使用更简单) 替换,LeakCanary.Config.leakInspectors
和LeakCanary.Config.labelers
合并到LeakCanary.Config.leakTraceInspectors
中,后者提供对整个内存泄漏追踪的访问,以及一个新的图导向 API,该 API 替换了低级别的 hprof 解析器 API。 - #1382 LeakCanary 现在在 AndroidX UI 测试中运行时禁用自动堆转储。
- #1424 API 重命名:
RefWatcher.hasRetainedReferences
=>RefWatcher.hasRetainedInstances
,RefWatcher.retainedReferenceCount
=>RefWatcher.retainedInstanceCount
,RefWatcher.hasWatchedReferences
=>RefWatcher.hasWatchedInstances
,RefWatcher.removeKeysRetainedBeforeHeapDump
=>RefWatcher.removeInstancesRetainedBeforeHeapDump
,RefWatcher.clearWatchedReferences
=>RefWatcher.clearWatchedInstances
。 - #1432 #1438 #1440 新的“不会修复”泄漏和内存泄漏追踪检查器
- #1374 #1364 #1366 #1417 #1399 #1416 #1407 #1427 #1385 错误和崩溃修复
非常感谢 @1step2hell、@afollestad、@ansman、@bjdodson、@BraisGabin、@EBfVince、@jaredsburrows、@pforhan、@pyricau、@tellypresence、@wiyarmir 的贡献、错误报告和功能请求。
欲了解更多详情,请参阅 2.0-alpha-3 里程碑 和完整差异。
版本 2.0 Alpha 2 (2019-05-21)¶
- #1040 导入并分析来自其他设备的 hprof 文件
- #1344 计算保留大小
- #1325 新通知,显示当前保留实例数量
- #1079 “Excluded” 泄漏已重命名为“Won’t fix” 泄漏,以澄清含义。
- #1328 在 UI 中突出显示新的泄漏。
- #1327 LeakSentry 可以启用/禁用,并在非调试版本中自动禁用。
- #1173 实验性:现在报告仅通过弱引用发生的泄漏(以前报告为“无路径到实例”)
- #1339 重新添加了基于线程名称的排除支持
- #1312 修复了导致 LeakCanary 在应用被终止后停止检测泄漏的错误。
- #1310 #1313 #1314 #1340 #1337 许多 API 更改
- #1296 #1293 #1306 #1336 修复了多处崩溃问题。
非常感谢 @forrestbice、@Foso、@Goddchen、@marcosholgado、@orionlee、@pyricau、@satoshun、@ZacSweers 的贡献!
欲了解更多详情,请参阅 2.0-alpha-2 里程碑 和完整差异。
版本 2.0 Alpha 1 (2019-04-23)¶
- 新图标,感谢 @flickator!
- 完全重写为 100% Kotlin
- 一次分析中检测到多个泄漏
- 当应用进入后台或在前台达到至少 5 个泄漏时,会进行堆转储。
- 泄漏分组
- 具有相似原因的泄漏在 UI 中分组显示。
- 新的屏幕,用于查看组列表和每个组。
- 改进了内存泄漏追踪字符串,以突出显示泄漏原因。
- 泄漏可以分享到 Stack Overflow
- 新库:LeakSentry。
- 检测对象何时发生泄漏并触发 LeakCanary
- 可以在生产环境中独立使用,例如在发生 OutOfMemoryError 崩溃时报告泄漏实例的数量。
- 新的堆解析器
- 比之前的堆解析器**内存使用量减少 90%,速度快 6 倍**。
- 在与应用相同的进程中以低优先级线程运行。
- 不再依赖 Perflib 和 TroveJ。新增依赖 Okio。
- 旧解析器仍然可用,名为
leakcanary-android-perflib
,但在 alpha 版本后将移除。
- Labelers 可以向泄漏元素添加任意字符串内容
- 0 代码设置,只需添加一个 debug 依赖项。
- 更简单的配置选项
- 从支持库更新到 Android X
非常感谢 @BraisGabin、@colinmarsch、@jrodbx、@flickator、@JakeWharton、@pyricau、@WhatsEmo 的贡献!
欲了解更多详情,请参阅 2.0-alpha-1 里程碑 和完整差异。
版本 1.6.3 (2019-01-10)¶
- #1163 修复了由于遗漏 GC Roots 导致泄漏被错误分类为“无泄漏”的问题。
- #1153
LeakCanary.isInAnalyzerProcess
现在在分析器进程中,在任何首次泄漏发生之前(可能由启动泄漏结果 activity 触发)正确返回 true。 - #1158 在不使用 DisplayLeakService 时停止启用 DisplayLeakActivity。
- #1135 修复了大小为 1 的内存泄漏追踪的 IndexOutOfBoundsException 问题。
- #1163 保留“无泄漏”堆转储文件。
非常感谢 @KMaragh、@pyricau、@SebRut 的代码贡献!
版本 1.6.2 (2018-10-16)¶
- #1067 修复了 TransactionTooLargeException 崩溃(内存泄漏分析永远不会完成)。
- #1061 在 Fragment#onDestroyView() 后检测 Fragment 视图泄漏。
- #1076 为 Android P 添加了 FOREGROUND_SERVICE 权限。
- #1062 LeakCanary toast 现在总是正确显示。如果前台没有 activity,它将不显示。
- #1115 在全新安装时重新启用了 DisplayLeakActivity 图标。
- #1100 添加了可空性注解以改进 Kotlin 支持。
- 排除的泄漏更新(提交记录)。
- 可达性检查器更新(提交记录)。
非常感谢 @fractalwrench、@ZacSweers、@Goddchen、@igokoro、@IlyaGulya、@JakeWharton、@javmarina、@jokermonn、@jrodbx、@Parseus、@pyricau、@scottkennedy 的代码贡献!
公共 API 变更¶
AbstractAnalysisResultService
的子类现在应该覆盖onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap)
,而不是onHeapAnalyzed(@NonNull HeapDump heapDump, @NonNull AnalysisResult result)
版本 1.6.1 (2018-06-21)¶
- #727 改进了内存泄漏分析:LeakCanary 现在可以识别并突出显示潜在的泄漏原因。
- #1011 我们注意到计算保留堆大小可能需要很长时间,因此现在它是可选的,并且默认关闭。
- #633 支持在 instrumentation 测试中检测泄漏(请参阅 wiki)。
- #985 能够将内存泄漏追踪转换为堆栈追踪,以便于远程报告(请参阅 wiki)。
- #983 支持监视已销毁的 Fragment。
- #846 LeakCanary 现在使用前台服务,并在分析进行时显示通知。这还修复了在 O+ 版本上在后台分析时崩溃的问题。
- LeakCanary 图标(用于启动 DisplayLeakActivity)现在默认隐藏,只有在找到第一个泄漏后才启用。
- #775 修复了在 O+ 版本上分享堆转储时崩溃的问题,并添加了对 support-core-utils 库的依赖。
- #930 DisplayLeakActivity 具有响应式图标。
- #685 停止在 DisplayLeakActivity 的主线程上进行 IO 操作(修复了 StrictMode 错误)。
- #999 更新 HAHA 到 2.0.4,它使用 Trove4j 作为外部依赖项(来自 jcenter),而不是重新打包。这是为了澄清许可证(Apache v2 对 LGPL 2.1)。
- 一些错误和崩溃修复。
非常感谢 @AdityaAnand1、@alhah、@christxph、@csoon03、@daqi、@JakeWharton、@jankovd、@jrodbx、@kurtisnelson、@NightlyNexus、@pyricau、@SalvatoreT、@shmuelr、@tokou、@xueqiushi 的代码贡献!
注意:我们发布了 1.6 版本,但由于 #1058 问题很快跟进了 1.6.1 版本。
公共 API 变更¶
- 已安装的 ref watcher 单例现在通过
LeakCanary.installedRefWatcher()
可用 AnalysisResult.leakTraceAsFakeException()
返回一个异常,可用于将内存泄漏追踪报告和分组到 Bugsnag 或 Crashlytics 等工具。- 用于检测 instrumentation 测试中泄漏的新 API:
InstrumentationLeakDetector
和FailTestOnLeakRunListener
。 - 新的
Reachability.Inspector
和RefWatcherBuilder.stethoscopeClasses()
API,用于建立可达性并帮助识别泄漏原因。 - 可以通过
AndroidRefWatcherBuilder.watchActivities(false)
禁用监视 activity,通过AndroidRefWatcherBuilder.watchFragments(false)
禁用监视 fragment LeakCanary.setDisplayLeakActivityDirectoryProvider()
已弃用,替换为LeakCanary.setLeakDirectoryProvider()
- 新的
RefWatcherBuilder.computeRetainedHeapSize()
API,用于启用保留堆大小计算(默认关闭)。
版本 1.5.4 (2017-09-22)¶
- 在 leakcanary-watcher 中恢复 Java 7 兼容性
版本 1.5.3 (2017-09-17)¶
- 修复损坏的 1.5.2 构建
- 将 leakcanary-watcher 从 Android 库转换为 Java 库
- 在 RequestStoragePermissionActivity 中禁用完成动画
- 更正了 Robolectric 测试的 README 示例
欲了解更多详情,请参阅完整差异。
版本 1.5.2 (2017-08-09)¶
- 新的排除泄漏
- 将 Leakcanary UI 移至内存泄漏分析器进程
- 忽略计算 O+ 版本上位图的保留大小
- 为 O+ 版本上的持久消息添加通知渠道
- 从最近的应用菜单中排除权限 activity
- 更新了用于处理 Robolectric 测试的 README 和示例
欲了解更多详情,请参阅完整差异。
版本 1.5.1 (2017-04-25)¶
- 新的排除泄漏
- 修复 DisplayLeakService 中的 java.util.MissingFormatArgumentException 问题
- 为不同的应用分离任务 affinity
- 将 minSdk 提高到 14
- 修复 O Preview 的 HahaHelper 问题
欲了解更多详情,请参阅完整差异。
版本 1.5 (2016-09-28)¶
- 新的排除泄漏
- 将
LeakCanary.isInAnalyzerProcess()
添加到 no-op jar - 修复了多个文件访问问题
- 启动时不再清理,我们在每次新的堆转储时轮换堆转储文件。
- LeakCanary 现在回退到应用目录,直到它可以写入外部存储。
- 泄漏通知现在每个都使用一个独立的通知,而不是相互覆盖。
- 如果 LeakCanary 因任何原因(例如分析进行中、调试器已连接)无法执行堆转储,它会稍后通过指数退避重试。
- 添加了用户删除所有泄漏时的确认对话框。
- 将两个 LeakCanary 配置方法替换为提供更大灵活性的 builder,请参阅
LeakCanary.refWatcher()
。
欲了解更多详情,请参阅完整差异。
公共 API 变更¶
- 新的
HeapAnalyzer.findTrackedReferences()
方法,用于在您不清楚是什么泄漏的情况下进行无头分析。 - 将
LeakCanary.isInAnalyzerProcess()
添加到 no-op jar - 添加了
LeakCanary.refWatcher()
,它返回一个AndroidRefWatcherBuilder
,该 builder 扩展了RefWatcherBuilder
,让您可以完全自定义RefWatcher
实例。 - 移除了
LeakCanary.install(Application, Class)
和LeakCanary.androidWatcher(Context, HeapDump.Listener, ExcludedRefs)
。 - 移除了
R.integer.leak_canary_max_stored_leaks
和R.integer.leak_canary_watch_delay_millis
,这些现在可以通过LeakCanary.refWatcher()
设置。 - 更新了
LeakDirectoryProvider
API,以集中所有文件相关的职责。 RefWatcher
现在使用WatchExecutor
(执行Retryable
)构建,而不是使用执行Runnable
的Executor
构建。HeapDumper.NO_DUMP
已重命名为HeapDumper.RETRY_LATER
版本 1.4 (2016-09-11)¶
- 修复 GC root 类型为 android.os.Binder 时发生的假阴性问题 #482
- 更新 HAHA 到 2.0.3;清除编译器警告 #563
- 更正了德语翻译中的一些错误 #516
- 存储权限被拒绝时不要循环 #422
- 移除对以 "__" 开头的旧资源引用 #477
- 修复在 M 版本上 DisplayLeakActivity 的权限崩溃问题 #382
- 修复堆转储中找不到线程名称时的 NPE 问题 #417
- 向堆栈追踪添加版本信息 #473
版本 1.4-beta2 (2016-03-23)¶
- 向分析结果添加忽略原因 #365。
- 降低在 M 版本上解析堆转储时的内存使用量 #223。
- 修复 LeakCanaryInternals.isInServiceProcess() 中的 NPE 问题 #449。
- 新的忽略的 Android SDK 泄漏 #297,#322。
- 在测试构建中使用 leakcanary-android-no-op #143。
- 修复以允许 LeakCanary 与 ProGuard 一起工作 #398。
- 优化 png 资源 #406。
- 修复错误视图上的删除按钮不起作用问题 #408。
- 添加德语翻译 #437。
版本 1.4-beta1 (2016-01-08)¶
- 切换到 HAHA 2.0.2,它在底层使用 Perflib 而不是 MAT #219。这修复了崩溃并大大提高了速度。
- 我们现在可以解析 Android M 的堆转储 #267,尽管仍然存在内存问题(请参阅 #223)。
- 现在也报告排除的泄漏,并在显示泄漏 activity 中可用。
- 为 #132 添加了 ProGuard 配置。
- 许多新的忽略的 Android SDK 泄漏。
- 将排除的泄漏添加到文本报告中 #119。
- 将 LeakCanary SHA 添加到文本报告中 #120。
- 添加了 CanaryLog API 以替换日志记录器:#201。
- 将所有资源重命名为以
leak_canary_
开头,而不是__leak_canary
#161 - 堆转储失败时不会崩溃 #226。
- 将保留大小添加到泄漏报告中 #162。
公共 API 变更¶
AnalysisResult.failure
现在是Throwable
而不是Exception
。主要目标是在解析时捕获并正确报告 OOM。- 为通过数组条目的引用向
LeakTraceElement.Type
添加了ARRAY_ENTRY
。 - 重命名
ExcludedRefs
字段。 - 现在可以完全忽略每个
ExcludedRef
条目,或者“仅在没有其他路径时保留”。 - 添加了对忽略给定类的所有字段(静态和非静态)的支持。
版本 1.3.1 (2015-05-16)¶
- 堆转储和分析结果现在保存到 SD 卡:#21。
ExcludedRef
和AndroidExcludedRefs
可以自定义:#12 #73。- 7 个新的忽略的 Android SDK 泄漏:#1 #4 #32 #89 #82 #97。
- 修复了 LeakCanary 中的 3 个崩溃问题:#37 #46 #66。
- 修复了 StrictMode 线程策略违规问题:#15。
- 将
minSdkVersion
从9
更新到8
:#57。 - 将 LeakCanary 版本名称添加到
LeakCanary.leakInfo()
中:#49。 leakcanary-android-no-op
更轻量,不再依赖leakcanary-watcher
,现在只有 2 个类:#74。- 向文本泄漏追踪添加字段状态详情。
- 堆转储进行时会显示一个 Toast,警告 UI 将冻结:#20。您可以通过提供自己的名为
__leak_canary_heap_dump_toast.xml
的布局来自定义 toast(例如,您可以将其设为空布局)。 - 如果分析失败,将保留结果和堆转储文件,以便可以将其报告给 LeakCanary:#102。
- 更新到 HAHA 1.3 以修复 2 个崩溃问题 #3 #46
公共 API 变更¶
- 从 1.3 升级到 1.3.1 时,之前保存的堆转储文件将不再可读,但它们不会从应用目录中移除。您最好卸载您的应用。
- 将
android.permission.WRITE_EXTERNAL_STORAGE
添加到leakcanary-android
artifact 中。 LeakCanary.androidWatcher()
参数类型已更改(+ExcludedRefs)。LeakCanary.leakInfo()
参数类型已更改(+boolean)ExcludedRef
现在可序列化且不可变,可以使用ExcludedRef.Builder
创建实例。ExcludedRef
在HeapDump
中可用AndroidExcludedRefs
是一个枚举,您现在可以通过创建EnumSet
并调用AndroidExcludedRefs.createBuilder()
来选择要在AndroidExcludedRefs
中忽略的泄漏。AndroidExcludedRefs.createAppDefaults()
和AndroidExcludedRefs.createAndroidDefaults()
返回一个ExcludedRef.Builder
。ExcludedRef
从leakcanary-analyzer
移至leakcanary-watcher
版本 1.3 (2015-05-08)¶
初始发布。