# Py ⚔

The latest articles on DEV Community by Py ⚔ (@pyricau).

This is one page of public article previews, not the complete archive. Follow Next page to continue. Summaries are not the original full articles.

## Leak investigation: Rx disposal race in SQLDelight

DevFeed: [Leak investigation: Rx disposal race in SQLDelight](<https://devfeed.tech/articles/leak-investigation-rx-disposal-race-in-sqldelight-25862.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/leak-investigation-rx-disposal-race-in-sqldelight-3n06>)

Author: Py ⚔

Published: 2021-05-17T22:10:41Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [RxJava](<https://devfeed.tech/topics/rxjava.md>), [Code](<https://devfeed.tech/topics/code.md>), [implementation](<https://devfeed.tech/topics/implementation.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code](<https://devfeed.tech/tags/code.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [implementation](<https://devfeed.tech/tags/implementation.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [leak](<https://devfeed.tech/tags/leak.md>), [rxjava](<https://devfeed.tech/tags/rxjava.md>), [software](<https://devfeed.tech/tags/software.md>), [sqldelight](<https://devfeed.tech/tags/sqldelight.md>), [thread](<https://devfeed.tech/tags/thread.md>)

### AI overview

This article investigates a memory leak caused by a disposal race in SQLDelight's RxJava integration. The implementation sets the disposable before adding the query listener, so a subscription that is already disposed can add a listener that is never removed. The article recommends checking listener and disposable ordering and avoiding unnecessary scheduler calls for observables originating from Observable.create().

### Source excerpt

Header image: The In-Between by Romain Guy. In this blog we'll look into how an easy mistake when using Observable.create() can lead to subtle leaks. I recently investigated the following leak, which I couldn't reproduce systematically: ┬─── ... ├─ com.example.hockey.PlayerQueries$selectAllQuery instance │ ↓ Query.listeners │ ~~~~~~~~~ ├─ java.util.concurrent.CopyOnWriteArrayList instance │ ↓ CopyOnWriteArrayList.array │ ~~~~~ ├─ java.lang.Object[] array │ ↓ Object[].[0] │ ~~~ ├─ sqldelight.runtime.rx.QueryListenerAndDisposable instance │ Retaining 4.3 kB in 56 objects │ ↓ QueryListenerAndDisposable.emitter │ ~~~~~~~ ... RxJava observer chain ├─ com.example.hockey.PlayersView$onAttachedToWindow$1 instance │ Anonymous class implementing io.reactivex.functions.Function │ ↓ PlayersView$onAttachedToWindow$1.this$0 │ ~~~~~~ ╰-> com.example.hockey.view.PlayersView instance Leaking: YES (View.mContext references a destroyed activity) In the above leaktrace, PlayerQueries$selectAllQuery is a generated SQLDelight query. Our PlayersView is listening for updates to that query while the view is attached by leveraging Query.asObservable(). Once the view is detached, the observable chain is disposed and the query is expected to let go of the corresponding listener. I inspected the heap dump and found that the view was indeed detached, the observable chain was correctly disposed, and yet the QueryListenerAndDisposable listener had not been removed from the query. Let's look at the Query.asObservable() implementation: fun <T : Any> Query<T>.asObservable(): Observable<Query<T>> { return Observable.create(QueryOnSubscribe(this)) } private class QueryOnSubscribe<T : Any>( private val query: Query<T> ) : ObservableOnSubscribe<Query<T>> { override fun subscribe(emitter: ObservableEmitter<Query<T>>) { val listener = QueryListenerAndDisposable(emitter, query) emitter.setDisposable(listener) query.addListener(listener) emitter.onNext(query) } } private class QueryListenerAndDisposable<T : A

## Tap Response Time: Jetpack Navigation 🗺

DevFeed: [Tap Response Time: Jetpack Navigation 🗺](<https://devfeed.tech/articles/tap-response-time-jetpack-navigation-25863.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/tap-response-time-jetpack-navigation-4738>)

Author: Py ⚔

Published: 2021-04-24T19:27:34Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Jetpack](<https://devfeed.tech/topics/jetpack.md>), [navigation](<https://devfeed.tech/topics/navigation.md>), [Android](<https://devfeed.tech/topics/android.md>), [implementation](<https://devfeed.tech/topics/implementation.md>), [GPU](<https://devfeed.tech/topics/gpu.md>), [OpenGL](<https://devfeed.tech/topics/opengl.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [gpu](<https://devfeed.tech/tags/gpu.md>), [implementation](<https://devfeed.tech/tags/implementation.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [jetpack](<https://devfeed.tech/tags/jetpack.md>), [jetpack-navigation](<https://devfeed.tech/tags/jetpack-navigation.md>), [main-thread](<https://devfeed.tech/tags/main-thread.md>), [navigation](<https://devfeed.tech/tags/navigation.md>), [opengl](<https://devfeed.tech/tags/opengl.md>), [performance](<https://devfeed.tech/tags/performance.md>), [process](<https://devfeed.tech/tags/process.md>), [render](<https://devfeed.tech/tags/render.md>), [software](<https://devfeed.tech/tags/software.md>), [tracing](<https://devfeed.tech/tags/tracing.md>)

### AI overview

A tutorial on measuring tap response time in Android applications using Jetpack Navigation. It traces the path from a touch event through navigation, view traversal, rendering, GPU execution, and display composition to define the total user-perceived response duration.

### Source excerpt

Header image: Surf by Romain Guy. In Android Vitals - Tap Response Time 👉 we established that the naive approach to measuring Tap Response Time isn't accurate and doesn't scale. Today we'll build a better implementation step by step, on top Jetpack Navigation. 🗺 Navigation library We'll focus on Jetpack Navigation here, however most of the content applies for any navigation library or tap action. In fact, I first implemented this at Square on top of Flow and Workflow. Advanced Navigation Sample We'll implement the Tap Response Time measurement inside the Advanced Navigation Sample and focus on the navigation from the Title screen to the About screen. aboutButton.setOnClickListener { findNavController().navigate(R.id.action_title_to_about) } From Tap to Render What happens exactly when we click on the about button? Main thread tracing To figure that out, we enable Java method tracing while clicking on the button : The MotionEvent.ACTION_UP event is dispatched and a click is posted to the main thread. The posted click runs, the click listener calls NavController.navigate() and a fragment transaction is posted to the main thread. The fragment transaction runs, the view hierarchy is updated, and a view traversal is scheduled for the next frame on the main thread. The view traversal runs, the view hierarchy is measured, laid out and drawn. What happens after step 4? Systrace We get a better high level view with systrace: In step 4, the view traversal draw pass generates a list of drawing commands (known as display lists) and sends that list of drawing commands to the render thread. Step 5: the render thread optimizes the display lists, adds effects such as ripples, then leverages the GPU to run the drawing commands and draw into a buffer (an OpenGL surface). Once done, the render thread tells the surface flinger (which lives in a separate process) to swap the buffer and put it on the display. Step 6 (not visible in the systrace screenshot): the surfaces for all visible w

## Android Vitals - Tap Response Time 👉

DevFeed: [Android Vitals - Tap Response Time 👉](<https://devfeed.tech/articles/android-vitals-tap-response-time-25858.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/android-vitals-tap-response-time-19mj>)

Author: Py ⚔

Published: 2021-04-15T14:10:20Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [User experience (UX)](<https://devfeed.tech/topics/ux.md>), [navigation](<https://devfeed.tech/topics/navigation.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code](<https://devfeed.tech/tags/code.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [monitoring](<https://devfeed.tech/tags/monitoring.md>), [navigation](<https://devfeed.tech/tags/navigation.md>), [performance](<https://devfeed.tech/tags/performance.md>), [software](<https://devfeed.tech/tags/software.md>), [ux](<https://devfeed.tech/tags/ux.md>), [vitals](<https://devfeed.tech/tags/vitals.md>)

### AI overview

This article defines Tap Response Time on Android as the interval between a finger leaving the touchscreen and the display rendering a frame that visibly reacts to the tap. It examines a naive measureTimeMillis() approach and explains why it can produce negative or incomplete measurements and does not scale across a codebase.

### Source excerpt

Header image: Alone Together by Romain Guy. Android users expect apps to respond to their actions within a short time window. 💡 Did you know? UX research teaches us that a response time shorter than 100ms feels immediate, and a response time beyond 1s makes users lose focus. When the response time gets closer to 10 seconds, users simply abandon their task (source). 👉📱 Measuring user action response times is critical to ensure a good user experience. Taps are the most common action apps must respond to. Can we measure Tap Response Time? 🎓 Tap Response Time The Tap Response Time is the time from when the user is done pressing a button to when the app has visibly reacted to the tap. More precisely, it's the time from when the finger leaves the touch screen to when the display has rendered a frame with a visible reaction to that tap (e.g. the start of a navigation animation). The Tap Response Time does not include any animation time. Naive Tap Response Time I opened the Navigation Advanced Sample project and added a call to measureTimeMillis() to measure the Tap Response Time when tapping on the about button. aboutButton.setOnClickListener { val tapResponseTimeMs = measureTimeMillis { findNavController().navigate(R.id.action_title_to_about) } PerfAnalytics.logTapResponseTime(tapResponseTimeMs) } Simple enough! However this approach presents several drawbacks: ⌛ It can return a negative time. 📈 It doesn't scale with the codebase size. 👉 It doesn't account for the time from when the finger leaves the touch screen to when the click listener is called. 📱 It doesn't account for the time from when we're done calling NavController.navigate() to when the display has rendered a frame with the new screen visible. ⌛ Negative time measureTimeMillis() calls System.currentTimeMillis() whihch can be set by the user or the phone network, so the time may jump backwards or forwards unpredictably. Elapsed time measurements should not use System.currentTimeMillis() (learn more: Android V

## Waldo's Android support handles mobile UI changes in recorded tests

DevFeed: [Waldo's Android support handles mobile UI changes in recorded tests](<https://devfeed.tech/articles/waldo-where-s-my-ui-25865.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/waldo-where-s-my-ui-4p53>)

Author: Py ⚔

Published: 2021-01-27T19:41:44Z

Content type: opinion

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Testing](<https://devfeed.tech/topics/testing.md>), [Mobile](<https://devfeed.tech/topics/mobile.md>), [LineageOS](<https://devfeed.tech/topics/lineageos.md>), [ui](<https://devfeed.tech/topics/ui.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [automated](<https://devfeed.tech/tags/automated.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [mobile](<https://devfeed.tech/tags/mobile.md>), [software](<https://devfeed.tech/tags/software.md>), [testing](<https://devfeed.tech/tags/testing.md>), [ui](<https://devfeed.tech/tags/ui.md>), [waldo](<https://devfeed.tech/tags/waldo.md>)

### AI overview

The article examines Waldo.io's Android support for no-code automated mobile tests. In an example project, recorded tests continued to select the intended tabs after navigation order, labels, IDs, and icons changed; when a tab was removed, updating the test adapted the scenario.

### Source excerpt

Today, Waldo.io, a no-code platform for automated mobile tests, announced general availability of their Android support. // Detect dark theme var iframe = document.getElementById('tweet-1354481799611736075-839'); if (document.body.className.includes('dark-theme')) { iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1354481799611736075&theme=dark" } I was recently talking with the CEO of Waldo, Amine Bellakrid. I told him that the llama logo was super cute and the UI gorgeous... but I had doubts a no-code platform was a good fit for automated mobile tests. The problem with no-code tests No-code platforms typically provide a recorder tool that lets you navigate through the app, recording everywhere you tapped. Then later on you can replay the test automatically, and get notified when it fails. Automated tests are expected to fail when a change breaks the correct behavior of the app. Unfortunately, no-code platforms tend to generate brittle tests that also fail on small changes that didn't introduce incorrect behavior. This creates a lot of noise, so teams tend to stop updating and running these tests and they just rot in a corner. Note: similar problems can happen with code based tests, but we've introduced patterns to work around them (e.g. Testing Robots) Apparently Waldo is different When I shared my doubts, Amine smiled and said I should try Waldo. So I created a new example project with 3 tabs: I recorded a simple test on Waldo.io, tapping on the second tab (Dashboard) and then the 3rd tab (Notifications). Messing with Waldo Tab swap I started by swapping out the second and the third navigation tab: The result is interesting: Waldo clicked on the correct tab (Dashboard) even though it changed place, and the test passed. Changing strings Next, I renamed the Dashboard tab to Summary . Still ✅ . Changing everything else Ok, time to make this really hard. I also changed the tab menu id and its icon. No way this can still work. ... the test still passes a

## Android Vitals - How adb measures App Startup 🔎

DevFeed: [Android Vitals - How adb measures App Startup 🔎](<https://devfeed.tech/articles/android-vitals-how-adb-measures-app-startup-25855.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/android-vitals-how-adb-measures-app-startup-5n7>)

Author: Py ⚔

Published: 2020-12-01T17:05:55Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [API](<https://devfeed.tech/topics/api.md>), [Code](<https://devfeed.tech/topics/code.md>), [Android Studio](<https://devfeed.tech/topics/android-studio.md>)

Tags: [adb](<https://devfeed.tech/tags/adb.md>), [android](<https://devfeed.tech/tags/android.md>), [android-studio](<https://devfeed.tech/tags/android-studio.md>), [api](<https://devfeed.tech/tags/api.md>), [app-startup](<https://devfeed.tech/tags/app-startup.md>), [code](<https://devfeed.tech/tags/code.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [performance](<https://devfeed.tech/tags/performance.md>), [software](<https://devfeed.tech/tags/software.md>), [startup](<https://devfeed.tech/tags/startup.md>)

### AI overview

This deep dive explains how Android's ActivityTaskManager measures app startup time. The measurement runs from the system receiving an activity-start Intent until the activity window finishes drawing, using uptime before API 30 and realtime from API 30 onward. The article traces the implementation through AOSP and shows how to inspect it with Android Studio debugging.

### Source excerpt

Last week, Chet Haase published a great blog post: Testing App Startup Performance. It leverages the output of ActivityTaskManager to obtain the app startup duration. Whenever an activity starts, you'll see something like this in the logcat output: ActivityTaskManager: Displayed com.android.samples.mytest/.MainActivity: +1s380ms This duration (1,380ms in this example) represents the time that it took from launching the app to the time when the system consider it "launched," which includes drawing the first frame (hence "Displayed"). This article is a deep dive to explore the question: What does ActivityTaskManager measure exactly? I know you're impatient, let's jump to the conclusion: ActivityTaskManager measures the time (uptime on API < 30, realtime on API 30+) from when system_process receives an Intent to start an activity to when the window of that activity is done drawing. Key takeways: This measure includes a few hundred milliseconds prior to app code and resources loading, i.e. time that an app developer cannot affect. You can measure this without the extra time from within the app, I'll share how at the end. And now, let's dive into AOSP code! ActivityTaskManager log ActivityTaskManager: Displayed com.android.samples.mytest/.MainActivity: +1s380ms We know what the log looks like so we can search for it on cs.android.com: This leads us to ActivityTaskManager.logAppDisplayed(): private void logAppDisplayed(TransitionInfoSnapshot info) { StringBuilder sb = mStringBuilder; sb.setLength(0); sb.append("Displayed "); sb.append(info.launchedActivityShortComponentName); sb.append(": "); TimeUtils.formatDuration(info.windowsDrawnDelayMs, sb); Log.i(TAG, sb.toString()); } The startup duration is TransitionInfoSnapshot.windowsDrawnDelayMs. It's calculated in TransitionInfoSnapshot.notifyWindowsDrawn(): TransitionInfoSnapshot notifyWindowsDrawn( ActivityRecord r, long timestampNs ) { TransitionInfo info = getActiveTransitionInfo(r); info.mWindowsDrawnDelayMs = info.calc

## Android Vitals - Profiling App Startup 🔬

DevFeed: [Android Vitals - Profiling App Startup 🔬](<https://devfeed.tech/articles/android-vitals-profiling-app-startup-25857.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/android-vitals-profiling-app-startup-32ek>)

Author: Py ⚔

Published: 2020-11-23T23:03:19Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Android Studio](<https://devfeed.tech/topics/android-studio.md>), [configuration](<https://devfeed.tech/topics/configuration.md>), [Processes](<https://devfeed.tech/topics/processes.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-studio](<https://devfeed.tech/tags/android-studio.md>), [app-startup](<https://devfeed.tech/tags/app-startup.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [performance](<https://devfeed.tech/tags/performance.md>), [profile](<https://devfeed.tech/tags/profile.md>), [profiling](<https://devfeed.tech/tags/profiling.md>), [software](<https://devfeed.tech/tags/software.md>), [trace](<https://devfeed.tech/tags/trace.md>), [traces](<https://devfeed.tech/tags/traces.md>)

### AI overview

This tutorial explains how to profile Android app startup with Android Studio. It compares profiler recording configurations, recommends Sample Java Methods for startup profiling, shows how to start recording when the app launches, and discusses why release builds are preferable to debug builds when investigating customer-facing performance issues.

### Source excerpt

Header image: The In-Between by Romain Guy. My previous articles focused on measuring Android app start in production. Once we've established a metric and scenarios that trigger a slow app start, the next step is to improve performance. To understand what makes the app start slow, we need to profile it. Android Studio provides several types of profiler recording configurations: Trace System Calls (aka systrace, perfetto): Low impact on runtime, great for understanding how the app interacts with the system and CPUs, but not the Java method calls that happen inside the app VM. Sample C/C++ Functions (aka Simpleperf): Not interesting to me, the apps I deal with run much more bytecode than native code. On Q+ this is supposed to now also sample Java stacks in a low overhead way, but I haven't managed to get that working. Trace Java Methods: This captures all VM method calls which introduces so much overhead that the results don't mean much. Sample Java Methods: Less overhead than tracing but shows the Java method calls that happen inside the VM. This is my preferred option when profiling app startup. Start recording on app startup The Android Studio profiler has UI to start a trace by connecting to an already running process, but no obvious way to start recording on app startup. The option exist but is hidden away in the run configuration for your app: check Start this recording on startup in the profiling tab. Then deploy the app via Run > Profile app. Profiling release builds Android developers typically use a debug build type for their everyday work, and debug builds often include a debug drawer, extra libraries such as LeakCanary, etc. Developers should profile release builds rather than debug builds to make sure they're fixing the actual issues that their customers are facing. Unfortunately, release builds are non debuggable so the Android profiler can't record traces on release builds. Here are a few options to work around that issue. 1. Create a debuggable release

## Leak detection: Android Studio vs LeakCanary ⚔

DevFeed: [Leak detection: Android Studio vs LeakCanary ⚔](<https://devfeed.tech/articles/leak-detection-android-studio-vs-leakcanary-25861.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/leak-detection-android-studio-vs-leakcanary-35j5>)

Author: Py ⚔

Published: 2020-10-30T00:13:40Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Android Studio](<https://devfeed.tech/topics/android-studio.md>), [Memory Leaks](<https://devfeed.tech/topics/memory-leaks.md>), [Android](<https://devfeed.tech/topics/android.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-studio](<https://devfeed.tech/tags/android-studio.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [false-positive](<https://devfeed.tech/tags/false-positive.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [leak](<https://devfeed.tech/tags/leak.md>), [leakcanary](<https://devfeed.tech/tags/leakcanary.md>), [memory](<https://devfeed.tech/tags/memory.md>), [memory-leaks](<https://devfeed.tech/tags/memory-leaks.md>), [software](<https://devfeed.tech/tags/software.md>)

### AI overview

The article compares Android Studio's Activity/Fragment leak filtering with LeakCanary's lifecycle-based detection. It explains that Android Studio may flag newly created or cached Fragments as potential leaks, while LeakCanary watches destroyed objects and checks whether they remain retained after garbage collection.

### Source excerpt

I recently came across this comment in a post: The thing really annoying about LeakCanary or Android Studio, most of the time leaks identified by LeakCanary do not appear in Profiler/Memory/memory leaks, I wonder if LeakCanary is showing false positives or Android Studio is missing positives. That's a good question, let's dig into code and figure this out! False positive leaks in Android Studio Before answering the question, we need to talk about where the idea of false positive leaks comes from: Android Studio. That warning was originally a longer description: Activity and Fragment instances that might be causing memory leaks. For Activities, these are instances that have been destroyed but are still being referenced. For Fragments, these are instances that do not have a valid FragmentManager but are still being referenced. Note, these instance might include Fragments that were created but are not yet being utilized. The documentation provides more insights on false positive leaks: In certain situations, such as the following, the filter might yield false positives: A Fragment is created but has not yet been used. A Fragment is being cached but not as part of a FragmentTransaction. The phrasing is vague but it looks like false positive leaks only applies to Fragments. Android Studio leak filtering Android Studio dumps and analyzes the heap when you press the Dump Heap icon. Leaking instances are displayed by enabling the "Activity/Fragment Leaks" filter, which updates the bottom panel to only show leaking instances. The filtering is performed by ActivityFragmentLeakInstanceFilter: const val FRAGFMENT_MANAGER_FIELD_NAME = "mFragmentManager" /** * A Fragment instance is determined to be potentially leaked if * its mFragmentManager field is null. This indicates that the * instance is in its initial state. Note that this can mean that * the instance has been destroyed, or just starting to be * initialized but before being attached to an activity. The * latter gives us

## The real size of Android objects 📏

DevFeed: [The real size of Android objects 📏](<https://devfeed.tech/articles/the-real-size-of-android-objects-25864.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/the-real-size-of-android-objects-1i2e>)

Author: Py ⚔

Published: 2020-09-22T22:31:08Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Java](<https://devfeed.tech/topics/java.md>), [Android Studio](<https://devfeed.tech/topics/android-studio.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [deep-dive](<https://devfeed.tech/tags/deep-dive.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [java](<https://devfeed.tech/tags/java.md>), [leakcanary](<https://devfeed.tech/tags/leakcanary.md>), [memory](<https://devfeed.tech/tags/memory.md>), [performance](<https://devfeed.tech/tags/performance.md>), [software](<https://devfeed.tech/tags/software.md>)

### AI overview

The article investigates why Android and other heap-dump tools report different shallow object sizes. It explains that Java virtual machines use different memory layouts and that HPROF instance sizes are independent of VM layout and padding. Because JOL does not run on Dalvik or ART, the author explores using ART TI and its GetObjectSize API for LeakCanary.

### Source excerpt

Header image: Deep Dive by Romain Guy. I'm currently reimplementing how LeakCanary computes the retained heap size of objects. As a quick reminder: Shallow heap size of an object: The object size in the memory. Retained heap size of an object: The shallow size of that object plus the shallow size of all the objects that are transitively held in memory by only that object. In other words, it's the amount of memory that will be freed when that object is garbage collected. One cannot trust a shallow size As part of that work, I compared the shallow size of objects as reported in LeakCanary versus other heap dump tools such as YourKit, Eclipse Memory Analyzer Tool (MAT) and Android Studio Memory Analyzer. That's when I realized something was wrong: every tool provides a different answer. I asked Jesse Wilson about it and he pointed me to this article by Aleksey Shipilёv: What Heap Dumps Are Lying To You About. Some take aways: Every Java VM lays out its memory in a slightly different way and performs various optimizations, such as changing field order, aligning bits, etc. The heap dump format (.hprof) is a standard. A class dump record contains the list of fields and their types as well as the instance size of the class. Aleksey Shipilёv asked about having the instance size be the actual size of an instance in memory but the answer was nope: the sizes in the HPROF dump are VM and padding independent, to avoid breaking expectations from consuming tools. There's a tool called JOL that instruments the JVM runtime to report the actual size of an object. Aleksey used that to compare the size reported in hprof based tools with the actual size and found that they were all wrong in a different way. In Exploring Java's Hidden Costs, Jake Wharton showed how to use JOL. Unfortunately, JOL only runs on JVMs, and not the Dalvik or ART runtimes. To quote Jake: In this case, because the classes are exactly the same and the JVM 64 bit, and Android's now 64 bit, the number should be tra

## Android Vitals - First draw time 👩🎨

DevFeed: [Android Vitals - First draw time 👩🎨](<https://devfeed.tech/articles/android-vitals-first-draw-time-25854.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/android-vitals-first-draw-time-m1d>)

Author: Py ⚔

Published: 2020-08-29T11:51:28Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Monitoring](<https://devfeed.tech/topics/monitoring.md>), [App](<https://devfeed.tech/topics/app.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [bug](<https://devfeed.tech/tags/bug.md>), [callback](<https://devfeed.tech/tags/callback.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [extension-function](<https://devfeed.tech/tags/extension-function.md>), [function](<https://devfeed.tech/tags/function.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [monitoring](<https://devfeed.tech/tags/monitoring.md>), [performance](<https://devfeed.tech/tags/performance.md>), [performance-monitoring](<https://devfeed.tech/tags/performance-monitoring.md>), [software](<https://devfeed.tech/tags/software.md>), [vitals](<https://devfeed.tech/tags/vitals.md>)

### AI overview

This article explains how Android app startup time is tied to the first completely loaded frame and focuses on measuring when cold start ends. It compares Choreographer.postFrameCallback() with ViewTreeObserver.addOnDrawListener(), describing timing behavior, an API 25 issue, and an API 26 listener-merging bug workaround.

### Source excerpt

Header image: Light Field by Romain Guy. This blog series is focused on stability and performance monitoring of Android apps in production. Last week, I wrote about how to best determine the app start time. Today, we focus on determining the time at which cold start ends. According to the Play Console documentation: Startup times are tracked when the app's first frame completely loads. We learn a bit more from the App startup cold time documentation: Once the app process has completed the first draw, the system process swaps out the currently displayed background window, replacing it with the main activity. At this point, the user can start using the app. In Android Vitals - Rising to the first drawn surface 🤽♂, we learnt that: ActivityThread.handleResumeActivity() schedules the first frame. On the first frame Choreographer.doFrame() calls ViewRootImpl.doTraversal() which performs a measure pass, a layout pass, and finally the first draw pass on the view hierarchy. First frame Since API level 16, Android provides a simple API to schedule a callback when the next frame happens: Choreographer.postFrameCallback(). class MyApp : Application() { var firstFrameDoneMs: Long = 0 override fun onCreate() { super.onCreate() Choreographer.getInstance().postFrameCallback { firstFrameDoneMs = SystemClock.uptimeMillis() } } } Unfortunately, calling Choreographer.postFrameCallback() has the side effect of scheduling a frame that runs before the first traversal is scheduled. So the time reported here is before the time of the frame that runs the first draw. I was able to reproduce this on API 25 but also noticed it doesn't happen in API 30, so this bug was probably fixed. First draw ViewTreeObserver On Android, each view hierarchy has a ViewTreeObserver which can hold callbacks for global events such as layout or draw. ViewTreeObserver.addOnDrawListener() We can call ViewTreeObserver.addOnDrawListener() to register a draw listener: view.viewTreeObserver.addOnDrawListener { // repo

## Android Vitals - When did my app start? ⏱

DevFeed: [Android Vitals - When did my app start? ⏱](<https://devfeed.tech/articles/android-vitals-when-did-my-app-start-25859.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/android-vitals-when-did-my-app-start-24p4>)

Author: Py ⚔

Published: 2020-08-21T21:01:05Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Monitoring](<https://devfeed.tech/topics/monitoring.md>), [Processes](<https://devfeed.tech/topics/processes.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [monitoring](<https://devfeed.tech/tags/monitoring.md>), [performance](<https://devfeed.tech/tags/performance.md>), [performance-monitoring](<https://devfeed.tech/tags/performance-monitoring.md>), [process](<https://devfeed.tech/tags/process.md>), [software](<https://devfeed.tech/tags/software.md>), [vitals](<https://devfeed.tech/tags/vitals.md>)

### AI overview

This Android Vitals article evaluates several ways to determine when an Android app starts, including Application.onCreate(), ContentProvider.onCreate(), class loading, and Linux process start time. It explains why process fork time may precede the meaningful beginning of app cold-start monitoring.

### Source excerpt

Header image: Ressence Type 5 Tilt by Romain Guy. This blog series is focused on stability and performance monitoring of Android apps in production. Last week, I wrote about using process importance to determine why an app was started. To track cold start time, we need to know when the app started. There are many ways to do that and this blog evaluates different approaches. As a reminder, I already established in Android Vitals - What time is it? that I would use SystemClock.uptimeMillis() to measure time intervals. Application.onCreate() The simplest approach is to capture the time at which Application.onCreate() is called. class MyApp : Application() { var applicationOnCreateMs: Long = 0 override fun onCreate() { super.onCreate() applicationOnCreateMs = SystemClock.uptimeMillis() } } ContentProvider.onCreate() In How does Firebase initialize on Android? we learn that a safe early initialization hook for library developers is ContentProvider.onCreate(): class StartTimeProvider : ContentProvider() { var providerOnCreateMs: Long = 0 override fun onCreate(): Boolean { providerOnCreateMs = SystemClock.uptimeMillis() return false } } ContentProvider.onCreate() also works for app developers and it's called earlier in the app lifecycle than Application.onCreate(). Class load time Before any class can be used, it has to be loaded. We can rely on static initializers to store the time at which specific classes are loaded. We could track the time at which the Application class is loaded: class MyApp : Application() { companion object { val applicationClassLoadMs = SystemClock.uptimeMillis() } } In Android Vitals - Diving into cold start waters 🥶, we learnt that on Android P+ the first class loaded is the AppComponentFactory: @RequiresApi(Build.VERSION_CODES.P) class StartTimeFactory : androidx.core.app.AppComponentFactory() { companion object { val factoryClassLoadMs = SystemClock.uptimeMillis() } } <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schema

## Android Vitals - Why did my process start? 🌄

DevFeed: [Android Vitals - Why did my process start? 🌄](<https://devfeed.tech/articles/android-vitals-why-did-my-process-start-25860.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/android-vitals-why-did-my-process-start-4d0e>)

Author: Py ⚔

Published: 2020-08-15T14:06:06Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Processes](<https://devfeed.tech/topics/processes.md>), [Process](<https://devfeed.tech/topics/process.md>), [Monitoring](<https://devfeed.tech/topics/monitoring.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [lifecycle](<https://devfeed.tech/tags/lifecycle.md>), [memory](<https://devfeed.tech/tags/memory.md>), [monitoring](<https://devfeed.tech/tags/monitoring.md>), [performance](<https://devfeed.tech/tags/performance.md>), [performance-monitoring](<https://devfeed.tech/tags/performance-monitoring.md>), [process](<https://devfeed.tech/tags/process.md>), [processes](<https://devfeed.tech/tags/processes.md>), [software](<https://devfeed.tech/tags/software.md>), [vitals](<https://devfeed.tech/tags/vitals.md>)

### AI overview

This article explains how Android process importance can help identify cold starts when the reason a process started is unavailable through an Android API. It describes checking RunningAppProcessInfo.importance via ActivityManager.getMyMemoryState() at process startup and reports anonymized production results comparing startup importance with whether an activity was created before the first posted message.

### Source excerpt

Header image: Windmill Sunrise by Romain Guy. This blog series is focused on stability and performance monitoring of Android apps in production. Last week, I wrote about how to determine if an app start is a cold start: If we post a message and no activity was created when that message runs, then we know this isn't a cold start, even if an activity is eventually launched 20 seconds later. class MyApp : Application() { override fun onCreate() { super.onCreate() var firstActivityCreated = false registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { if (firstActivityCreated) { return } firstActivityCreated = true } }) Handler().post { if (firstActivityCreated) { // TODO Report cold start } } } } With this approach, we must wait for an activity to be launched or that message to run before we know if an app start is a cold start. Sometimes it would be useful to know that from within Application.onCreate(). For example, we might want to preload resources asynchronously to optimize cold start: class MyApp : Application() { override fun onCreate() { super.onCreate() if (isColdStart()) { preloadDataForUiAsync() } } } Process importance While there is no Android API to know why a process was started, there is one to know why a process is still running: RunningAppProcessInfo.importance, which we can read from ActivityManager.getMyMemoryState(). According to the Processes and Application Lifecycle documentation: To determine which processes should be killed when low on memory, Android places each process into an "importance hierarchy" based on the components running in them and the state of those components. [...] When deciding how to classify a process, the system will base its decision on the most important level found among all the components currently active in the process. Right when the process starts, we could check its importance. If the importance is IMPORTANCE_FOREGRO

## Android Vitals - Is this a cold start? 🦋

DevFeed: [Android Vitals - Is this a cold start? 🦋](<https://devfeed.tech/articles/android-vitals-is-this-a-cold-start-25856.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/android-vitals-is-this-a-cold-start-3m44>)

Author: Py ⚔

Published: 2020-08-05T22:00:07Z

Content type: article

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Monitoring](<https://devfeed.tech/topics/monitoring.md>), [Processes](<https://devfeed.tech/topics/processes.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code](<https://devfeed.tech/tags/code.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [monitoring](<https://devfeed.tech/tags/monitoring.md>), [performance](<https://devfeed.tech/tags/performance.md>), [process](<https://devfeed.tech/tags/process.md>), [software](<https://devfeed.tech/tags/software.md>), [vitals](<https://devfeed.tech/tags/vitals.md>)

### AI overview

This article explains cold starts in Android apps and why Android does not provide a direct API to identify them. It discusses monitoring cold-start times in production and begins developing an alternative detection approach based on app and activity lifecycle behavior.

### Source excerpt

Header image: Follow the Light by Romain Guy. This blog series is focused on stability and performance monitoring of Android apps in production. In the last 2 posts, I wrote about what happens from when the user taps a launcher icon to when the first activity is drawn. A cold start is an activity launch where the app process starts from scratch in response to an intent to start an activity. According to the App startup time documentation: This type of start presents the greatest challenge in terms of minimizing startup time, because the system and app have more work to do than in the other launch states. We recommend that you always optimize based on an assumption of a cold start. Doing so can improve the performance of warm and hot starts, as well. To optimize cold start, we need to measure it, which means we need to monitor cold start times in production. Unfortunately, there is no Activity.isThisAColdStart() API on Android. This is by design: the Activity lifecycle APIs indicate when to save and restore state and abstract away the death and rebirth of processes. The engineers who designed the Android APIs didn't want us to write overly complex code with special cases for all the various ways an activity can be started. So there's no API. How are we supposed to monitor cold start if we can't tell a cold start from any other process start? This post leverages what we learnt from our previous deep dives on cold start to start building out our own version of the missing Activity.isThisAColdStart() API. Traditional approach Most apps and libraries report a cold start if the first activity was created within a minute of the app start. It looks something like this: class MyApp : Application() { override fun onCreate() { super.onCreate() val appCreateMs = SystemClock.uptimeMillis() var firstActivityCreated = false registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { if