<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[mohamedebrahim96]]></title><description><![CDATA[I'm a software engineer who is passionate about building android applications with Kotlin and Java.
Creating technology to elevate people, and building communit]]></description><link>https://mohamedebrahim96.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1664052499997/sDBntLGuD.png</url><title>mohamedebrahim96</title><link>https://mohamedebrahim96.com</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 12:06:13 GMT</lastBuildDate><atom:link href="https://mohamedebrahim96.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A safer way to collect flows from Android UIs]]></title><description><![CDATA[In an Android app, Kotlin flows are typically collected from the UI layer to display data updates on the screen. However, you want to collect these flows making sure you’re not doing more work than necessary, wasting resources (both CPU and memory) o...]]></description><link>https://mohamedebrahim96.com/a-safer-way-to-collect-flows-from-android-uis</link><guid isPermaLink="true">https://mohamedebrahim96.com/a-safer-way-to-collect-flows-from-android-uis</guid><dc:creator><![CDATA[Mohamed Ebrahim]]></dc:creator><pubDate>Sat, 24 Sep 2022 22:07:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1664057086852/JoMiBJCtl.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In an Android app, Kotlin flows are typically collected from the UI layer to display data updates on the screen. However, you want to collect these flows making sure you’re not doing more work than necessary, wasting resources (both CPU and memory) or leaking data when the view goes to the background.</p>
<p>In this article, you’ll learn how the Lifecycle.repeatOnLifecycle, and Flow.flowWithLifecycle APIs protect you from wasting resources and why they’re a good default to use for flow collection in the UI layer.</p>
<p>Wasting resources
It’s recommended to expose the Flow API from lower layers of your app hierarchy regardless of the flow producer implementation details. However, you should also collect them safely.</p>
<p>A cold flow backed by a channel or using operators with buffers such as buffer, conflate, flowOn, or shareIn is not safe to collect with some of the existing APIs such as CoroutineScope.launch, Flow.launchIn, or LifecycleCoroutineScope.launchWhenX, unless you manually cancel the Job that started the coroutine when the activity goes to the background. These APIs will keep the underlying flow producer active while emitting items into the buffer in the background, and thus wasting resources.</p>
<h3 id="heading-note-a-cold-flow-is-a-type-of-flow-that-executes-the-producer-block-of-code-on-demand-when-a-new-subscriber-collects">Note: A cold flow is a type of flow that executes the producer block of code on-demand when a new subscriber collects.</h3>
<pre><code><span class="hljs-comment">// Implementation of a cold flow backed by a Channel that sends Location updates</span>
fun FusedLocationProviderClient.locationFlow() = callbackFlow&lt;Location&gt; {
    val callback = object : LocationCallback() {
        override fun onLocationResult(result: LocationResult?) {
            result ?: <span class="hljs-keyword">return</span>
            <span class="hljs-keyword">try</span> { offer(result.lastLocation) } <span class="hljs-keyword">catch</span>(e: Exception) {}
        }
    }
    requestLocationUpdates(createLocationRequest(), callback, Looper.getMainLooper())
        .addOnFailureListener { e -&gt;
            close(e) <span class="hljs-comment">// in case of exception, close the Flow</span>
        }
    <span class="hljs-comment">// clean up when Flow collection ends</span>
    awaitClose {
        removeLocationUpdates(callback)
    }
}
</code></pre><h1 id="heading-visual-diagram">Visual diagram</h1>
<p>Circling back to the beginning, collecting locationFlow directly from a coroutine started with lifecycleScope.launch was dangerous since the collection keeps happening even when the View is in the background.</p>
<p>repeatOnLifecycle prevents you from wasting resources and app crashes because it stops and restarts the flow collection when the lifecycle moves in and out of the target state.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664057213438/TyhdjuIB4.png" alt="1_fmQRBPMPpnO7NAO2bg0GKw.png" /></p>
<p>Comparison with LiveData
You might’ve noticed that this API behaves similarly to LiveData, and that’s true! LiveData is aware of Lifecycle, and its restarting behavior makes it ideal for observing streams of data from the UI. And that’s also the case for the Lifecycle.repeatOnLifecycle, and Flow.flowWithLifecycle APIs!</p>
<p>Collecting flows using these APIs is a natural replacement for LiveData in Kotlin-only apps. If you use these APIs for flow collection, LiveData doesn’t offer any benefits over coroutines and flow. Even more, flows are more flexible since they can be collected from any Dispatcher and they can be powered with all its operators. As opposed to LiveData, which has limited operators available and whose values are always observed from the UI thread.</p>
<p>StateFlow support in data binding
On a different note, one of the reasons you might be using LiveData is because it’s supported by data binding. Well, so is StateFlow! For more information about StateFlow support in data binding, check out the official documentation.</p>
<p>Use the Lifecycle.repeatOnLifecycle or Flow.flowWithLifecycle APIs to safely collect flows from the UI layer in Android.</p>
]]></content:encoded></item><item><title><![CDATA[Hilt is stable! Easier dependency injection on Android]]></title><description><![CDATA[Hilt, Jetpack’s recommended dependency injection (DI) solution for Android apps, is already stable! That means Hilt is fully ready to be used in production. Hilt is simpler than Dagger, enables you to write less boilerplate code, it’s designed for An...]]></description><link>https://mohamedebrahim96.com/hilt-is-stable-easier-dependency-injection-on-android</link><guid isPermaLink="true">https://mohamedebrahim96.com/hilt-is-stable-easier-dependency-injection-on-android</guid><dc:creator><![CDATA[Mohamed Ebrahim]]></dc:creator><pubDate>Sat, 24 Sep 2022 22:02:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1664056871524/ivSoG1BTb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hilt, Jetpack’s recommended dependency injection (DI) solution for Android apps, is already stable! That means Hilt is fully ready to be used in production. Hilt is simpler than Dagger, enables you to write less boilerplate code, it’s designed for Android and has integration with multiple Jetpack libraries. Several companies have already started taking advantage of Hilt in their apps.</p>
<p>Hilt was first released as alpha in June 2020 with the mission of defining a standard way to do DI in your Android apps and since then, we’ve received tons of feedback from developers. That not only improved the library, but also, it let us know that we’re working on the right problems.</p>
<p>Instead of creating dependency graphs by hand, and manually injecting and passing around types where needed, Hilt automatically generates all that code for you at compile time by means of annotations. Hilt can help you get the most out of DI best practices in your app by doing the hard work and generating all that boilerplate you would’ve needed to write otherwise. Also, as it’s fully integrated with Android, Hilt manages the lifecycle of the dependency graphs associated with the Android framework classes automatically for you.</p>
<p>Let’s see Hilt in action with a quick example! After setting Hilt up, using it in your project from scratch to inject a ViewModel in an Activity is as easy as adding few annotations to your code as follows:</p>
<pre><code>@HiltAndroidApp <span class="hljs-comment">// Setup Hilt in your app</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApplication</span> : <span class="hljs-title">Application</span>() </span>{ ... }

<span class="hljs-comment">// Make Hilt aware of this ViewModel</span>
@HiltViewModel
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoginViewModel</span> @<span class="hljs-title">Inject</span> <span class="hljs-title">constructor</span>(
    <span class="hljs-title">private</span> <span class="hljs-title">val</span> <span class="hljs-title">savedStateHandle</span>: <span class="hljs-title">SavedStateHandle</span>,
    /* ... <span class="hljs-title">Other</span> <span class="hljs-title">dependencies</span> <span class="hljs-title">Hilt</span> <span class="hljs-title">takes</span> <span class="hljs-title">care</span> <span class="hljs-title">of</span> ... */
) : <span class="hljs-title">ViewModel</span>() </span>{ ... }


<span class="hljs-comment">// Make the activity use the right ViewModel factory and</span>
<span class="hljs-comment">// inject other dependencies</span>
@AndroidEntryPoint 
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoginActivity</span> : <span class="hljs-title">AppCompatActivity</span>() </span>{

    private val loginViewModel: LoginViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        <span class="hljs-built_in">super</span>.onCreate(savedInstanceState)
        <span class="hljs-comment">// loginViewModel is ready to be used</span>
    }
}
</code></pre><p>Apart from what’s mentioned above, why should you use Hilt in your Android app?</p>
<p>Simpler than Dagger
Hilt is built on top of the popular DI library Dagger so benefits from the compile time correctness, runtime performance, scalability, and Android Studio support that Dagger provides. Some Dagger annotations, such as @Inject to tell Dagger and Hilt how to provide an instance of a type, are often used in Hilt. But Hilt is simpler than Dagger!</p>
<p>“I highly recommend leveraging Dagger for dependency injection in Android applications. However, pure vanilla Dagger can lead to too much room for creativity. When that gets mixed with the complexity of the various lifecycle-aware components that are part of Android development, there’s plenty of room for pitfalls such as memory leaks: for example, accidentally passing in Activity-scoped dependencies into ViewModels. Hilt being opinionated and designed specifically for Android helps you avoid some of the pitfalls when using vanilla Dagger.” — Marcelo Hernandez, Staff Software Engineer, Tinder</p>
<p>If you’re already using Dagger in your app and want to migrate to Hilt… fear not! Dagger and Hilt can coexist together, apps can be migrated on an as-needed basis.</p>
<p>Less boilerplate
Hilt is opinionated — this means that it makes decisions for you so that you have less code to write. Hilt defines standard components, or dependency graphs, fully integrated with Android framework classes such as Application, activities, fragments, and views. As well as scope annotations to scope instances of types to those components.</p>
<p>“Hilt automatically generates the test application and test component via @HiltAndroidTest. We were able to remove between 20% and 40% of boilerplate wire up test code after migrating to Hilt!” — Jusun Lee, Software Engineer, YouTube</p>
<p>“We are only scratching the surface in terms of migrating to Hilt. However, in one of the modules we migrated to Hilt, we saw +78/-182 in terms of lines changed for this library.” — Marcelo Hernandez, Staff Software Engineer, Tinder</p>
<p>Designed for Android
As opposed to Dagger that is a dependency injection solution for the Java programming language applications, Hilt solely works in Android apps. Some annotations such as</p>
<p>@HiltAndroidApp, @AndroidEntryPoint, or @HiltViewModel are specific to Hilt and define an opinionated way to do DI on Android.</p>
<p>“Hilt finally offers built-in Android lifecycle aware Dagger components. With Hilt, we can solely focus on Dagger @Modules without having to worry about Components, Subcomponents, the Component provider pattern, and so on.” — Marcelo Hernandez, Staff Software Engineer, Tinder</p>
<p>Standardization of components and bindings
For those that know about Dagger, Hilt simplifies the dependency graph by using a monolithic component system to generate less code at compile time.</p>
<p>“With Hilt’s monolithic component system, binding definitions are provided once and shared across all classes that use that component. This is a big win as previously, YouTube used a polylithic component system where modules were manually wired-up into custom components and there were many duplicate binding definitions.” — Jusun Lee, Software Engineer, YouTube</p>
<p>“As our Gradle modules separation allows for feature development in isolation, it became easy to be too creative when using Dagger. We’ve found that migrating those modules over to Hilt has actually exposed flaws in which we were inadvertently violating the separation of concerns.” — Marcelo Hernandez, Staff Software Engineer, Tinder</p>
]]></content:encoded></item><item><title><![CDATA[Migrating Architecture Blueprints to Jetpack Compose]]></title><description><![CDATA[As part of our effort to modernize the app architecture guidance, we want to experiment with different UI patterns to see what works best, find similarities and differences between the alternatives, and ultimately consolidate those learnings as best ...]]></description><link>https://mohamedebrahim96.com/migrating-architecture-blueprints-to-jetpack-compose</link><guid isPermaLink="true">https://mohamedebrahim96.com/migrating-architecture-blueprints-to-jetpack-compose</guid><dc:creator><![CDATA[Mohamed Ebrahim]]></dc:creator><pubDate>Sat, 24 Sep 2022 18:12:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1664042987624/pKQodMCDu.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As part of our effort to modernize the app architecture guidance, we want to experiment with different UI patterns to see what works best, find similarities and differences between the alternatives, and ultimately consolidate those learnings as best practices.</p>
<p>To make our findings as easy to follow as possible, we needed a sample that has a familiar business case and isn’t too complicated. And… who doesn’t know about TODO apps? We chose Architecture Blueprints! Historically, Blueprints served as an experimental playground for architecture choices. What a great fit for this as well!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664043032489/cs-UOCcjW.gif" alt="2022-04-11-blueprints-migration-compose-1.gif" /></p>
<p>The patterns we want to experiment with are obviously affected by the different APIs available nowadays. The new kids in town are Jetpack Compose’s State APIs! As Compose seamlessly works with any Unidirectional Data Flow pattern, we’ll use Compose to render UI to make a fair comparison.</p>
<p>This blog post tells the story of how the team migrated Architecture Blueprints to Jetpack Compose. As LiveData is also considered an alternative in our experiments, we left the sample as it was at the time of the migration. In this refactoring, the ViewModel classes and the data layer were left untouched.</p>
<p>⚠️ The architecture used in this LiveData-based codebase doesn’t fully follow the latest architecture best practices. In particular, LiveData shouldn’t be used in the data or domain layers — Flows and coroutines should be used instead.</p>
<p>Now that the context is clear, let’s dive in on how we approached refactoring Blueprints to Jetpack Compose. You can check out the full code on the main branch.</p>
<p>The goal of a gradual migration is to ease code reviews and maintain a shippable product throughout the migration. The migration plan involved three steps:</p>
<p>Migrate the content of each screen to Compose. Each screen could be individually migrated to Compose, including their UI tests. Fragments then become the container/host of each migrated screen.</p>
<p>Migrate the app to Navigation Compose — which removes all Fragments from the project — and migrate the Activity UI logic to root composables. End-to-end tests are also migrated at this point.</p>
<p>Remove View system dependencies.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664043105056/ByhAxmZSm.png" alt="2022-04-11-blueprints-migration-compose-2.png" /></p>
]]></content:encoded></item></channel></rss>