# Stories by Chao Zhang on Medium

Stories by Chao Zhang on Medium

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

## Latency percentiles are not additive

DevFeed: [Latency percentiles are not additive](<https://devfeed.tech/articles/latency-percentiles-are-not-additive-25907.md>)

Original publisher: [Read original article](<https://chao2zhang.medium.com/latency-percentiles-are-not-additive-4f306ced9cd6?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2023-10-07T17:02:37Z

Content type: opinion

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [Latency](<https://devfeed.tech/topics/latency.md>), [Back end](<https://devfeed.tech/topics/backend.md>), [client](<https://devfeed.tech/topics/client.md>), [Caching](<https://devfeed.tech/topics/caching.md>), [Network](<https://devfeed.tech/topics/network.md>)

Tags: [analysis](<https://devfeed.tech/tags/analysis.md>), [android](<https://devfeed.tech/tags/android.md>), [app-development](<https://devfeed.tech/tags/app-development.md>), [backend](<https://devfeed.tech/tags/backend.md>), [caching](<https://devfeed.tech/tags/caching.md>), [ios](<https://devfeed.tech/tags/ios.md>), [latency](<https://devfeed.tech/tags/latency.md>), [mobile](<https://devfeed.tech/tags/mobile.md>), [network](<https://devfeed.tech/tags/network.md>), [performance](<https://devfeed.tech/tags/performance.md>), [request](<https://devfeed.tech/tags/request.md>), [software-engineering](<https://devfeed.tech/tags/software-engineering.md>), [ui](<https://devfeed.tech/tags/ui.md>)

### AI overview

The article explains why adding the p90 latencies of sequential backend requests can overestimate the combined perceived latency. It uses a simplified example to show how client processing, network latency, and the relationship between request latency distributions affect the result.

### Source excerpt

Generated from https://mage.space/ When we are estimating the latency spanning multiple requests on the backend, we tend to use napkin math to add 90th percentile (p90) of those requests and use their summation as the estimation. However, the summation is often too pessimistic and follow me to understand why that is the case. In this simplified example, we have a UI feature displayed on the client after request A and request B are completed. Request B depends on request A, so they are sequential. The illustration of perceived latency with Request A and Request B We have backend latency p90(Request A) = 910ms, and p90(Request B) = 910ms, so we conclude the feature latency to be p90(Feature) = p90(Request A) + p90(Request B) = 1820ms. The above math is common in the latency analysis of our engineering requirement document. What did we miss here? A few points: Client logic: Client code does have non-trivial complexity. Regardless of iOS or Android, any caching layer, data transformation layer and view layer could add latency at the magnitude of 100ms or more. Network latency: The device needs to be connected to Home Wifi or 5G network, and then hit the nodes through ISP, CDN, Data Center and eventually reached our application server. These are perceived by the end user but very difficult to measure. Latency percentiles are not additive: p90(Request A and B) ≠ p90(Request A) + p90(Request B) Let's assume our feature has 10 end users from Alice to Jessica. And let's ignore client logic and estimate the network latency to be 128ms (Average of 2 RTT from US West to US East). In the most pessimistic case (napkin math pitfall!), Alice has the lowest backend latency for both requests and Jessica has the highest latency for both requests. p90(Request A and B) is 1948ms = p90(Request A) + p90(Request B) + avg(network latency) = 910ms + 910ms + 128ms. In the most optimistic though, Alice has the lowest backend latency for request A but the highest backend latency for request B,

## Sharing Thread Pools Across Libraries in Android Applications

DevFeed: [Sharing Thread Pools Across Libraries in Android Applications](<https://devfeed.tech/articles/reduce-reuse-recycle-your-thread-pools-25909.md>)

Original publisher: [Read original article](<https://chao2zhang.medium.com/reduce-reuse-recycle-your-thread-pools-%EF%B8%8F-81e2f54d8a1d?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2021-12-20T18:26:36Z

Content type: tutorial

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [Concurrency](<https://devfeed.tech/topics/concurrency.md>), [Concurrent Programming](<https://devfeed.tech/topics/concurrent-programming.md>), [Android](<https://devfeed.tech/topics/android.md>), [Library](<https://devfeed.tech/topics/library.md>), [App](<https://devfeed.tech/topics/app.md>), [Network](<https://devfeed.tech/topics/network.md>), [Jetpack](<https://devfeed.tech/topics/jetpack.md>), [Jetpack Compose](<https://devfeed.tech/topics/jetpack-compose.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [app-development](<https://devfeed.tech/tags/app-development.md>), [app-performance](<https://devfeed.tech/tags/app-performance.md>), [concurrency](<https://devfeed.tech/tags/concurrency.md>), [java](<https://devfeed.tech/tags/java.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [libraries](<https://devfeed.tech/tags/libraries.md>), [memory](<https://devfeed.tech/tags/memory.md>), [okhttp](<https://devfeed.tech/tags/okhttp.md>), [overhead](<https://devfeed.tech/tags/overhead.md>), [performance](<https://devfeed.tech/tags/performance.md>), [thread](<https://devfeed.tech/tags/thread.md>), [threading](<https://devfeed.tech/tags/threading.md>), [threads](<https://devfeed.tech/tags/threads.md>), [work](<https://devfeed.tech/tags/work.md>)

### AI overview

This Android development tutorial explains how creating threads for asynchronous tasks can increase memory pressure, scheduling overhead, and context-switching costs. It discusses using thread pools and proposes configuring libraries such as OkHttp and AndroidX WorkManager to share a common pool, while considering the benefits and costs.

### Source excerpt

Photo: Héctor J. Rivas from Unsplash Real-world applications today are mostly multi-threaded. This means developers should be mindful of managing the concurrency of their applications. Mastering the threading can help boost an app's performance. On the other hand, using concurrency without fully understanding it could lead to problems that negatively impact the app's health. For Android applications, every thread is mapped to a system-level thread at runtime. Each thread costs a minimum of 64k of memory on Android, If we always create a thread for any new asynchronous task, we will create memory pressure on the app. The app performance may suffer because spawning up new threads and context switching among threads are both taking up time and resources. If a thread is referenced even if it is not active, it will be kept in memory and can't be cleaned up by the garbage collector. Thread pools can help us manage concurrency more efficiently. ThreadPoolExecutor creates a pool of worker threads and schedules the tasks for them to execute. It can grow the pool size to meet the demand as new tasks arrive, and it can shrink the pool when threads are idle and no longer need to be kept alive. Thread pool, therefore, improves the app performance by reducing the per-task overhead and controls the resource usage by bounding the resources. Thread pool seems the solution to our concurrency headache, and many libraries have adopted this technique, such as OkHttp and AndroidX WorkManager. Each library maintains its own thread pool by default. When we include these libraries in our application, since each library spins up new threads by itself without the awareness of other thread pools, we come full circle. Hundreds of threads exist in our application because those libraries do not know each other. In this story, we are going to walk through an example of configuring libraries to share a common thread pool. We will also summarize the benefits and costs of managing thread pools, as it

## Reducing Android app size in practice at LinkedIn

DevFeed: [Reducing Android app size in practice at LinkedIn](<https://devfeed.tech/articles/reducing-android-app-size-in-practice-at-linkedin-25911.md>)

Original publisher: [Read original article](<https://medium.com/microsoft-mobile-engineering/reducing-android-app-size-in-practice-at-linkedin-559698e618b9?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2021-11-23T16:53:49Z

Content type: tutorial

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [App](<https://devfeed.tech/topics/app.md>), [Google Play](<https://devfeed.tech/topics/google-play.md>), [APK](<https://devfeed.tech/topics/apk.md>), [modules](<https://devfeed.tech/topics/modules.md>), [Azure](<https://devfeed.tech/topics/azure.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-app-bundle](<https://devfeed.tech/tags/android-app-bundle.md>), [android-app-development](<https://devfeed.tech/tags/android-app-development.md>), [apk](<https://devfeed.tech/tags/apk.md>), [app-size](<https://devfeed.tech/tags/app-size.md>), [azure](<https://devfeed.tech/tags/azure.md>), [google-play](<https://devfeed.tech/tags/google-play.md>), [jetpack-compose](<https://devfeed.tech/tags/jetpack-compose.md>), [modules](<https://devfeed.tech/tags/modules.md>), [optimization](<https://devfeed.tech/tags/optimization.md>), [sdk](<https://devfeed.tech/tags/sdk.md>)

### AI overview

This article explains how LinkedIn reduced Android app download size in practice. It distinguishes download, install, and storage sizes, then discusses Android App Bundle optimizations and Play Feature Delivery, including their use with the Azure Communication Services SDK for Native Video Meeting.

### Source excerpt

Photo: Daniel Romero from Unsplash App size is a key metric impacting user acquisition and retention: If the app is too large to download, users may skip installation or cancel the installation during downloading; if the app takes too much storage space after use, users may choose to uninstall. There are also different measurements of Android app sizes: Download size: The compressed size of the release downloaded by users at install time. Install size: The approximate size of the recent release on the user's device after installation, but before the app is opened for the first time. Space used shown in Storage Settings: Sizes of files under cache directory and other directories under /data/data/com.app.package.name This post mainly focuses on the download size. This is displayed directly in the Google Play console for developers, and also displayed in the about this app section in Play Store. The official documentation on Android Developers already covers many different ways to optimize the app size. However, in this blog post, we would like to emphasize some of the less known yet powerful techniques to reduce app size. These techniques helped LinkedIn to minimize its app size in recent years. Play Feature Deliverya.k.a Dynamic Features, Dynamic Modules, or On-Demand modules Android App Bundle, as a publishing format in Google Play, was recently escalated to the requirement for publishing new applications in August 2021. The app bundle is used to generate multiple optimized APKs. Then the user will only download the specific APK set optimized for their device. The download size savings come from: ABI libraries that do not match the device's processor architecture Resource files that do not match screen density Language resources that do not match the user's device language Application size savings from Android App Bundle (Google I/O 2018) -- https://youtu.be/flU42CTF3MQ Migrating to an Android App Bundle should be a streamlined process. At least this was the case for

## Fairly evaluating the impact of different Android UI libraries on Gradle build

DevFeed: [Fairly evaluating the impact of different Android UI libraries on Gradle build](<https://devfeed.tech/articles/fairly-evaluating-the-impact-of-different-android-ui-libraries-on-gradle-build-25905.md>)

Original publisher: [Read original article](<https://chao2zhang.medium.com/fairly-evaluating-the-impact-of-different-android-ui-libraries-on-gradle-build-6301de5e0e60?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2021-11-09T16:24:37Z

Content type: comparison

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [Compose](<https://devfeed.tech/topics/compose.md>), [Android](<https://devfeed.tech/topics/android.md>), [ui](<https://devfeed.tech/topics/ui.md>), [Gradle](<https://devfeed.tech/topics/gradle.md>), [migration](<https://devfeed.tech/topics/migration.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-app-development](<https://devfeed.tech/tags/android-app-development.md>), [android-gradle-plugin](<https://devfeed.tech/tags/android-gradle-plugin.md>), [build](<https://devfeed.tech/tags/build.md>), [comparison](<https://devfeed.tech/tags/comparison.md>), [compose](<https://devfeed.tech/tags/compose.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [incremental](<https://devfeed.tech/tags/incremental.md>), [jetpack-compose](<https://devfeed.tech/tags/jetpack-compose.md>), [migration](<https://devfeed.tech/tags/migration.md>), [ui](<https://devfeed.tech/tags/ui.md>)

### AI overview

This article describes a controlled approach for evaluating how Jetpack Compose, Data Binding, View Binding, and plain View affect Android Gradle build performance. It defines independent, dependent, and control variables, and proposes using synthetic Android projects generated with Android Studio to make comparisons more consistent.

### Source excerpt

Photo: Tsvetoslav Hristov from Unsplash For an Android application maintained by a large group of developers, build time is one of the most critical developer ergonomic metrics. This is particularly true when we evaluate to adopt new technology. Jetpack Compose is the new exciting modern UI library to build Android applications. Its official documentation already covers developer ergonomics on two of their sample applications. Although the comparison narrated in Jetpack Compose: Before and after were valuable, we would like to apply scientific control to our experiments, in order to fairly evaluate Compose against the existing UI layer technologies, such as Data Binding and View Binding. Fairness In a scientific experiment study, we use variables to test our hypothesis. There are basically three groups of variables: Independent variable as the input of our evaluation. Since we are evaluating the different Android UI libraries in this study, the particular type of UI library becomes our independent variable. Namely, Jetpack Compose, Data Binding, View Binding, and plain View. Dependent variable as the output of our evaluation. We would like to see how the choice of UI library impacts Gradle build, so the dependent variable could be any metric from our Gradle build. For instance, clean build time, incremental build time, and the timeline of the task execution. Control variable as the constants in our evaluation. We want to ensure the comparison to be performed with the same context. This means we need to ensure the same machine spec, same Gradle version, same Android Gradle Plugin version, same module dependencies, and same UI appearance. However, for large-scale applications, we are very unlikely to complete the full UI layer migration with a single atomic commit. Oftentimes, we are doing migrations over multiple commits spread over a time span, mixed with other commits to upgrading dependencies and altering module structures. With multiple commits, we become less co

## Safely collecting hot flows from Android native UI

DevFeed: [Safely collecting hot flows from Android native UI](<https://devfeed.tech/articles/safely-collecting-hot-flows-from-android-native-ui-25910.md>)

Original publisher: [Read original article](<https://chao2zhang.medium.com/safely-collecting-hot-flows-from-android-native-ui-f22f645edb44?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2021-09-02T23:38:42Z

Content type: tutorial

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [ui](<https://devfeed.tech/topics/ui.md>), [XML](<https://devfeed.tech/topics/xml.md>), [data](<https://devfeed.tech/topics/data.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [bug](<https://devfeed.tech/tags/bug.md>), [coroutine](<https://devfeed.tech/tags/coroutine.md>), [flow](<https://devfeed.tech/tags/flow.md>), [issue](<https://devfeed.tech/tags/issue.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [livedata](<https://devfeed.tech/tags/livedata.md>), [native](<https://devfeed.tech/tags/native.md>), [stateflow](<https://devfeed.tech/tags/stateflow.md>), [ui](<https://devfeed.tech/tags/ui.md>), [viewmodel](<https://devfeed.tech/tags/viewmodel.md>), [xml](<https://devfeed.tech/tags/xml.md>)

### AI overview

This tutorial explains a common Android UI pitfall when collecting multiple hot flows in one coroutine. Because StateFlow never completes, a later collection may never execute; the article presents separate coroutines and flowWithLifecycle() as fixes.

### Source excerpt

Photo: Merlene Goulet from Unsplash Along with the coroutine adoption, StateFlowbecomes an alternative observable data holder for LiveDatasince StateFlowhas almost all the functionalities of LiveDatawithout the main thread confinment. On top of following the best practice of the safer way to collect flows from Android UI, this story covers another common pitfall in detail. Setup I would like to display 2 text views and 1 button: First text view: Display a number X. Second text view: Display X² reactively. Button: Upon click, increments the number X. Apologize for the Jetpack Compose fans, I am still going to define the layout in XML with ViewBinding. https://medium.com/media/b411ff48ddf74bafb4288a430f431a87/href The data structure can be defined in the ViewModel : https://medium.com/media/3d3b345797521dd3063498c538d0715c/href To subscribe to these flows in the activity, I would use lifecycleScope.launchX to collect them, just like collecting LiveData in the old days: https://medium.com/media/4db49b992c028c1be26235e17d232d49/hrefBug When I run the app, I am expecting to see that every time I click the button, both text views will be updated, the first one with the new X and the second one with the new X². Let's run the app and see! What is happening? Why is the second text view not showing X²? Cause Reading the documentation of StateFlow , there is an important piece of information: State flow never completes. From the implementation side, StateFlowImpl has a while(true) loop that does not exit normally. Since we are subscribing a hot flow sourceFlowin the coroutine, the subsequent operations inside the coroutine will not ever be executed. lifecycleScope.launchWhenResumed { viewModel.sourceFlow.collect { binding.sourceFlow.text = it.toString() // Execution stops here } viewModel.transformedFlow.collect { binding.transformedFlow.text = it.toString() } } Note that if the upstream flow does not complete, the downstream flow does not complete either. In our case, sinkFlo

## Converting LiveData to Flow: More lessons learned

DevFeed: [Converting LiveData to Flow: More lessons learned](<https://devfeed.tech/articles/converting-livedata-to-flow-more-lessons-learned-25903.md>)

Original publisher: [Read original article](<https://chao2zhang.medium.com/converting-livedata-to-flow-more-lessons-learned-ba1e068115f5?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2021-06-23T06:52:18Z

Content type: tutorial

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [kotlin-coroutines](<https://devfeed.tech/topics/kotlin-coroutines.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Android](<https://devfeed.tech/topics/android.md>), [test](<https://devfeed.tech/topics/test.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-app-development](<https://devfeed.tech/tags/android-app-development.md>), [androiddev](<https://devfeed.tech/tags/androiddev.md>), [cancellation](<https://devfeed.tech/tags/cancellation.md>), [coroutine](<https://devfeed.tech/tags/coroutine.md>), [coroutines](<https://devfeed.tech/tags/coroutines.md>), [flow](<https://devfeed.tech/tags/flow.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [kotlin-coroutines](<https://devfeed.tech/tags/kotlin-coroutines.md>), [livedata](<https://devfeed.tech/tags/livedata.md>), [sharedflow](<https://devfeed.tech/tags/sharedflow.md>), [stateflow](<https://devfeed.tech/tags/stateflow.md>), [unit-tests](<https://devfeed.tech/tags/unit-tests.md>)

### AI overview

This continuation explains lessons from converting between Kotlin Flow and LiveData in Android. It covers unfinished hot-flow coroutines during unit tests, why asLiveData cancellation occurs after a timeout on Dispatchers.Main, and how values are replayed when LiveData becomes active again.

### Source excerpt

Photo: Buzz Andersen from Unsplash Android KTX provides opinionated methods facilitating using Kotlin Coroutines in Android Architecture Components. For instance, Flow<T>.asLiveData() is quite helpful to migrate your apps using LiveData towards coroutine. Make sure to understand their behaviors underneath before using them! This story is a continuation of the previous one. Lesson III: Unfinished coroutines during unit testshttps://medium.com/media/41979239402051494c1002bb4d9e57dc/href When converting from Flow to LiveData, the source stream might be a hot StateFlow like above. Running the test will actually end up with the following error: kotlinx.coroutines.test.UncompletedCoroutinesError: Unfinished coroutines during teardown. Ensure all coroutines are completed or cancelled by your test. We know a hot Flow like SharedFlow never completes, so this error appears reasonable. A common way to test hot flow is to cancel the coroutine that the hot flow is executed on. In this case, we may want to pass a custom coroutineContext to asLiveData(context = ...)and cancel the job when the assertions are done: https://medium.com/media/129968c37bf50ac019a25755e3c56411/href However, it does not work! Let's take a step back and consult the documentation of Flow<T>.asLiveData : If the LiveData becomes inactive (LiveData.onInactive) while the flow has not completed, the flow collection will be cancelled after timeoutInMs milliseconds unless the LiveData becomes active again before that timeout (to gracefully handle cases like Activity rotation). Since we are converting a hot flow to LiveData, we will always meet the condition. The flow collection will be canceled after timeoutInMs (default is 5 seconds). After digging it a bit more into the source code, we find that the cancellation is implemented as a delay() call on Dispatchers.Main : https://medium.com/media/154954407331c372f002028c8353a843/href The reason why cancel()runs on Dispatchers.Main is because it is invoked byLiveData.onI

## Debugging LiveData changes made easy

DevFeed: [Debugging LiveData changes made easy](<https://devfeed.tech/articles/debugging-livedata-changes-made-easy-25904.md>)

Original publisher: [Read original article](<https://chao2zhang.medium.com/debugging-livedata-changes-made-easy-d3aa16b81b41?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2021-04-08T21:16:44Z

Content type: tutorial

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [debugging](<https://devfeed.tech/topics/debugging.md>), [Logging](<https://devfeed.tech/topics/logging.md>), [Android Studio](<https://devfeed.tech/topics/android-studio.md>), [App](<https://devfeed.tech/topics/app.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-architecture](<https://devfeed.tech/tags/android-architecture.md>), [android-studio](<https://devfeed.tech/tags/android-studio.md>), [architecture-components](<https://devfeed.tech/tags/architecture-components.md>), [bytecode](<https://devfeed.tech/tags/bytecode.md>), [class](<https://devfeed.tech/tags/class.md>), [debugging](<https://devfeed.tech/tags/debugging.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [java](<https://devfeed.tech/tags/java.md>), [jetpack](<https://devfeed.tech/tags/jetpack.md>), [livedata](<https://devfeed.tech/tags/livedata.md>), [logging](<https://devfeed.tech/tags/logging.md>), [refactoring](<https://devfeed.tech/tags/refactoring.md>)

### AI overview

This tutorial examines the difficulties of debugging LiveData changes in larger Android applications. It compares breakpoints and logging, explains why both become less effective as observing chains grow, and identifies minimal code changes and historical state information as key requirements for a better approach.

### Source excerpt

Photo by Markus Spiske from Unsplash Have you ever got frustrated to debug LiveData changes by adding numerous log statements or breakpoints? As the core data structure of Android Architecture Components, LiveData is used widely in many apps to hold observable data. However, its debugging experience could still be a pain point after these many years. Observing LiveData changes at scale There are two traditional yet powerful methods for observing LiveData changes: Debugging and logging. When it comes to debugging, we mostly interact with our IDE and do not need to touch any code. We can simply click "Debug" or "Attach Debugger to Android Process" in Android Studio and expect the process to be paused at the preset debug breakpoints. Debugging in observers In terms of logging, we can add logging statements to our observers to know when they are called. When we rerun the app, we should be able to view those log statements through Logcat while interacting with the app. https://medium.com/media/bd34828e0230fe59c141c0e820c7436e/href Both debugging and logging are effective when we have simple LiveData observing chains. While we are iterating our app with new features and business logics, we may find it necessary to scale up our app into the recommended app architecture illustrated below. As you can tell, LiveData becomes more universally used as the data holder type across different components. In other words, LiveData observing chains merely get longer and more complex. App Architecture using LiveData When debugging our app at scale, logging might be less effective, because we do not want to change code at multiple places to debug. Alternatively, we could also build an abstract class LoggingObserver<T> : Observer<T>that adds log statements in onChanged(data: T) , and require all observers to inherit from LoggingObserver. This plausible approach is cumbersome since it requires large-scale refactoring of our app code. Using debugging may not be effective either: We need to

## Converting LiveData to Flow: Lessons learned

DevFeed: [Converting LiveData to Flow: Lessons learned](<https://devfeed.tech/articles/converting-livedata-to-flow-lessons-learned-25902.md>)

Original publisher: [Read original article](<https://chao2zhang.medium.com/converting-livedata-to-flow-lessons-learned-9362a00611c8?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2021-04-03T01:13:26Z

Content type: tutorial

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [kotlin-coroutines](<https://devfeed.tech/topics/kotlin-coroutines.md>), [Android](<https://devfeed.tech/topics/android.md>), [Coroutines](<https://devfeed.tech/topics/coroutines.md>), [App](<https://devfeed.tech/topics/app.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-app-development](<https://devfeed.tech/tags/android-app-development.md>), [coroutines](<https://devfeed.tech/tags/coroutines.md>), [flow](<https://devfeed.tech/tags/flow.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [kotlin-coroutines](<https://devfeed.tech/tags/kotlin-coroutines.md>), [lifecycle](<https://devfeed.tech/tags/lifecycle.md>), [livedata](<https://devfeed.tech/tags/livedata.md>), [network](<https://devfeed.tech/tags/network.md>), [retrofit](<https://devfeed.tech/tags/retrofit.md>), [rotation](<https://devfeed.tech/tags/rotation.md>), [viewmodel](<https://devfeed.tech/tags/viewmodel.md>)

### AI overview

This tutorial examines lessons from converting Kotlin Flow to LiveData with Flow<T>.asLiveData() in an Android app. It explains why flow collection may be re-executed after screen rotation and identifies creating a new LiveData instance instead of preserving it as a property as the bug causing recollection.

### Source excerpt

Photo: Robert Lukeman from Unsplash Android KTX provides opinionated methods, such asFlow<T>.asLiveData(), facilitating using Kotlin Coroutines in Android Architecture Components. Make sure to understand their behaviors underneath before using them! The official documentation on Kotlin coroutines on Android is quitewell-written, while there are numerous article posts covering the basics. In this story, we will focus on the lessons learned when using these extension methods, usingFlow<T>.asLiveData() as an example. Setup Let's start with a concrete example using the orthodox Android App Architecture: Repository + ViewModel + Fragment https://medium.com/media/a59f3702376fe64d61dd5c3dd72c9c15/href RedditService is a simple Retrofit service. We also added logging statements inside the flow collecting block. https://medium.com/media/a904400508a241bae454d44e0f01a9b9/href We use Flow<T>.asLiveData() to convert the flow into a LiveData. https://medium.com/media/97cb5995c53c5d86606a371ef905d25d/href In the fragment layer, we added logging statements for the fragment lifecycle calls and used Thread.sleep(500)to simulate complex layout inflation. Lesson I: Flow collection is re-executed after rotating the screen When we run the app, it looks fine. However, after rotating the device screen to recreate the fragment, we can observe more logs like flow running... indicating the flow is executed. 2021-04-02 16:41:32.246 I: Fragment onCreate... 2021-04-02 16:41:32.247 I: Fragment onCreateView... 2021-04-02 16:41:32.754 I: Fragment onStart... 2021-04-02 16:41:32.755 I: flow running... 2021-04-02 16:41:32.756 I: Fragment onResume... 2021-04-02 16:41:33.309 I: Observer called... That is weird! The original intention of ViewModel is to persist data that can survive UI recreation. After we rotate the device screen, the data should be retrieved from the storage. Contrarily, what we saw is that the data is fetched again from the service, which may cause unnecessary network calls or UI flic

## Using @JvmSuppressWildcards to resolve Kotlin and Java generic type issues

DevFeed: [Using @JvmSuppressWildcards to resolve Kotlin and Java generic type issues](<https://devfeed.tech/articles/jvmsuppresswildcards-the-secret-sauce-to-your-sandwich-style-generics-25906.md>)

Original publisher: [Read original article](<https://chao2zhang.medium.com/jvmsuppresswildcards-the-secret-sauce-to-your-sandwich-style-generics-b0093aa5979d?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2021-02-23T16:01:15Z

Content type: tutorial

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [generics](<https://devfeed.tech/topics/generics.md>), [Java](<https://devfeed.tech/topics/java.md>), [Android](<https://devfeed.tech/topics/android.md>), [Dagger](<https://devfeed.tech/topics/dagger.md>), [recyclerview](<https://devfeed.tech/topics/recyclerview.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-app-development](<https://devfeed.tech/tags/android-app-development.md>), [compilation-error](<https://devfeed.tech/tags/compilation-error.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [dagger](<https://devfeed.tech/tags/dagger.md>), [generics](<https://devfeed.tech/tags/generics.md>), [java](<https://devfeed.tech/tags/java.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [kotlin-generics](<https://devfeed.tech/tags/kotlin-generics.md>), [recyclerview](<https://devfeed.tech/tags/recyclerview.md>)

### AI overview

This tutorial explains how Kotlin generics interact with Java in mixed Kotlin-Java codebases, including Android applications and Dagger-based dependencies. It shows how the @JvmSuppressWildcards annotation can resolve compilation and type-inference problems when Kotlin generic APIs are consumed by Java code.

### Source excerpt

Photo: amirali mirhashemian from Unsplash If generic types are exposed in Kotlin API, consider @JvmSuppressWildcards so that your Java consumer can compile successfully. Kotlin generics are different from (in my opinion, smarter than) Java generics. Kotlin generics has declaration-site variance and type projections, which are officially documented here. Generics is a gigantic topic itself. My usual practice is to leverage IDE warnings and compiler error messages to fix my code since they usually contain the necessary information to show you where you did wrong. However, as we are Kotlinizing our codebase from Java to Kotlin, it is possible to have a sandwich-style code dependency that mixes Java code and Kotlin code. In such cases, when dealing with generics, your compiler and IDE may not be smart enough to help you fix those issues. What is sandwich-style code dependency?🥪 Sandwich-style Kotlin conversion At a certain time during your Java to Kotlin conversion, there could be some Java code depending on Kotlin code, which in turn depends on Java code. A typical example on Android is that your app code in Java depending on your library in Kotlin, which depends on Android SDK in Java. Even if your app code is written purely in Kotlin, the sandwich might still exist: The Java-based annotation processing Dagger, depends on your app code in Kotlin, which depends on Android SDK in Java. Now let's look at how the secret sauce @JvmSuppressWildcards can help us by making our generic sandwich tasty. Sandwich: List<Object> in RecyclerView methodshttps://medium.com/media/87fd0c63db2b9c82af2b241bf61cef31/href With the code above, the following compilation error is observed: MyChildAdapter.java:13: error: name clash: onBindViewHolder(MyViewHolder,int,List<Object>) in MyChildAdapter overrides a method whose erasure is the same as another method, yet neither overrides the other The error message leads us to think that we need to change our type here: Change the Kotlin generic para

## Presenting data efficiently in emails

DevFeed: [Presenting data efficiently in emails](<https://devfeed.tech/articles/presenting-data-efficiently-in-emails-25908.md>)

Original publisher: [Read original article](<https://chao2zhang.medium.com/presenting-data-efficiently-in-emails-f27cb5f9119e?source=rss-d19045640fe------2>)

Author: Chao Zhang

Published: 2021-01-29T01:25:52Z

Content type: tutorial

Language: en

Sources: [Stories by Chao Zhang on Medium](<https://devfeed.tech/sources/stories-by-chao-zhang-on-medium.md>)

Topics: [data](<https://devfeed.tech/topics/data.md>), [Android Gradle Plugin](<https://devfeed.tech/topics/android-gradle-plugin.md>), [email](<https://devfeed.tech/topics/email.md>), [ci](<https://devfeed.tech/topics/ci.md>), [APK](<https://devfeed.tech/topics/apk.md>), [dashboards](<https://devfeed.tech/topics/dashboards.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-gradle-plugin](<https://devfeed.tech/tags/android-gradle-plugin.md>), [apk](<https://devfeed.tech/tags/apk.md>), [ci](<https://devfeed.tech/tags/ci.md>), [data](<https://devfeed.tech/tags/data.md>), [data-visualization](<https://devfeed.tech/tags/data-visualization.md>), [email](<https://devfeed.tech/tags/email.md>), [numbers](<https://devfeed.tech/tags/numbers.md>)

### AI overview

The article presents a simple technique for making email reports more engaging by displaying key metrics with less text and larger numbers. It illustrates the approach with an Android Gradle Plugin upgrade report covering CI build-time reductions and APK size changes, and connects the idea to public COVID-19 dashboards.

### Source excerpt

A simple trick to write an email report for your readers Image by Hermann Traub from Pixabay Recently my team wrote an email report to the leadership team on the executive summary of Android Gradle Plugin upgrades. Below is the initial version of the reports in the exec summary section: - We have migrated to Android Gradle Plugin from 4.0 to 4.1. -We have observed a reduction in CI build by 50% at 50th percentile and 32% at 90th percentile. - Our APK size is also reduced by -17% The above looks pretty ordinary. But let's compare it to the following: Less text, larger numbers, much easier for readers to get interested in reading them 🎉 Testimony: I have received more than 5 "wow"s from readers across the company hierarchy for the bottom version. The inspiration is actually from the COVID-19 dashboard of where I live. I came to realize how often it is used in public versus how rare it is used in company emails.