<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Rohit Surwase on Medium]]></title>
        <description><![CDATA[Stories by Rohit Surwase on Medium]]></description>
        <link>https://medium.com/@rohitss?source=rss-6d6a65c4e6fa------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*X7NSTBoDzrKaBAloCZsBcQ.jpeg</url>
            <title>Stories by Rohit Surwase on Medium</title>
            <link>https://medium.com/@rohitss?source=rss-6d6a65c4e6fa------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 12 Nov 2026 08:40:45 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@rohitss/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Replacing LocalBroadcastManager in Android: SharedFlow + Sealed Classes = ❤️]]></title>
            <link>https://medium.com/@rohitss/replacing-localbroadcastmanager-in-android-sharedflow-sealed-classes-%EF%B8%8F-33868db8616f?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/33868db8616f</guid>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Thu, 02 Apr 2026 07:23:39 GMT</pubDate>
            <atom:updated>2026-04-02T07:23:39.141Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>A modern, lifecycle-safe, type-driven in-app event bus using Kotlin Coroutines and SharedFlow — demonstrated with a News Reader app.</strong></p><p><strong>TL;DR</strong> — If you are already familiar with Kotlin Coroutines, SharedFlow, and sealed classes, skip the basics and jump straight to the <em>LocalBroadcast + BroadcastEvent</em> section.</p><h3>Preface</h3><p>Every non-trivial Android app eventually runs into the same problem: how do you communicate between two completely unrelated parts of your app — a background sync service and a fragment, a repository and a ViewModel — without coupling them together?</p><p>For years, the go-to answer was LocalBroadcastManager. It was simple. It worked. And then Google deprecated it.</p><blockquote>“LocalBroadcastManager is an application-wide event bus and embraces layer violations in your app; any component may listen to events from any other component… Use other means of communicating between components.”<em><br> — Android Developer Documentation</em></blockquote><p>So, what do we use instead? There are options — EventBus, RxJava&#39;s PublishSubject, or callbacks passed through constructors — but each comes with its own cost. We are in the coroutines era now. There is a cleaner answer.</p><p>Let me walk you through an event bus architecture I’ve been using in production — built on MutableSharedFlow, Kotlin sealed classes, and coroutine extension functions. To keep things concrete, we&#39;ll build it in the context of a <strong>News Reader app</strong> that syncs articles in the background, handles user authentication, and notifies the UI of changes from anywhere in the stack.</p><h3>⭐ Why Not Just Use LiveData or StateFlow?</h3><p>Before we get to the solution, let’s quickly rule out the obvious alternatives.</p><p><strong>LiveData</strong> is lifecycle-aware but it <em>maintains state</em>. If you emit “show a loading spinner” via LiveData and the user rotates the screen, the spinner re-shows on re-subscription. That’s not what you want for fire-and-forget commands.</p><p><strong>StateFlow</strong> has the same problem — it <em>always</em> replays the last value to new collectors. It’s perfect for UI state, not for events.</p><p><strong>SharedFlow</strong> with replay = 0 is exactly what an event bus needs. It does not replay missed emissions. It delivers to all current subscribers simultaneously, and then the event is gone. Think of it like a real broadcast — if you weren&#39;t listening, you missed it.</p><h3>⭐ The Two Core Classes</h3><p>The entire system is built on two files. One defines <em>what</em> can be broadcast. The other defines <em>how</em> to broadcast and observe it.</p><h3>BroadcastEvent — The Contract</h3><p>BroadcastEvent is a <strong>sealed class</strong>. Every possible in-app event is a subclass — either a data class (if it carries a payload) or an object (if it&#39;s just a signal).</p><p>For our News Reader app, these are the events that flow across the app:</p><pre>sealed class BroadcastEvent {<br><br>    // An article&#39;s content was updated remotely<br>    data class ArticleUpdated(val articleId: String? = null) : BroadcastEvent()<br><br>    // Background feed sync is in progress<br>    data class FeedRefreshing(<br>        val currentProgress: Float,<br>        val total: Float,<br>        val remaining: Long<br>    ) : BroadcastEvent()<br><br>    // Background feed sync completed<br>    object FeedRefreshComplete : BroadcastEvent()<br><br>    // App has finished initializing and is ready<br>    object AppReady : BroadcastEvent()<br><br>    // A human-readable sync status message (e.g. &quot;Fetching top stories...&quot;)<br>    data class SyncStatus(val message: String) : BroadcastEvent()<br><br>    // OTP received via SMS for login<br>    data class OtpReceived(val otp: String, val smsBody: String) : BroadcastEvent()<br><br>    // A news category&#39;s articles were updated<br>    data class CategoryUpdated(<br>        val categoryId: String?,<br>        val articlesChanged: Boolean,<br>        val updateTime: Long<br>    ) : BroadcastEvent()<br><br>    // A category update is currently in progress<br>    data class CategoryUpdateInProgress(val categoryId: String?, val updateTime: Long) :<br>        BroadcastEvent()<br><br>    // Bookmark cloud sync started or completed<br>    data class BookmarkSyncUpdate(val started: Boolean, val bookmarkCount: Int) :<br>        BroadcastEvent()<br><br>    // Request to prompt the user for notification permission<br>    object RequestNotificationPermission : BroadcastEvent()<br><br>    // User changed their app lock / security settings<br>    data class SecurityChanged(val lockTypeChanged: Boolean) : BroadcastEvent()<br><br>    // User signed out<br>    object UserSignOut : BroadcastEvent()<br><br>    // Sign-out flow was initiated (before confirmation)<br>    object UserSignOutInitiated : BroadcastEvent()<br><br>    // Toggle read-count visibility in the UI<br>    data class HideReadCount(val hide: Boolean) : BroadcastEvent()<br><br>    // Onboarding was reset (e.g. after account switch)<br>    object OnboardingReset : BroadcastEvent()<br><br>    // Restore from backup is in progress<br>    object RestoreInProgress : BroadcastEvent()<br>}</pre><p>The sealed class is your contract. If you need to add a new event type to the app, this is the <em>one place</em> you go. Your IDE will immediately flag any when expression that doesn&#39;t handle the new subtype — that&#39;s the compiler enforcing your architecture.</p><h3>FilterType — The Subscription Filter</h3><p>Alongside BroadcastEvent, we define a FilterType enum. Each entry corresponds directly to one event type. Observers subscribe to a <em>list of filters</em>, not to all events.</p><pre>enum class FilterType {<br>    ARTICLE_UPDATED,             // BroadcastEvent.ArticleUpdated<br>    FEED_REFRESHING,             // BroadcastEvent.FeedRefreshing<br>    FEED_REFRESH_COMPLETE,       // BroadcastEvent.FeedRefreshComplete<br>    APP_READY,                   // BroadcastEvent.AppReady<br>    SYNC_STATUS,                 // BroadcastEvent.SyncStatus<br>    OTP_RECEIVED,                // BroadcastEvent.OtpReceived<br>    CATEGORY_UPDATED,            // BroadcastEvent.CategoryUpdated<br>    CATEGORY_UPDATE_IN_PROGRESS, // BroadcastEvent.CategoryUpdateInProgress<br>    BOOKMARK_SYNC_UPDATE,        // BroadcastEvent.BookmarkSyncUpdate<br>    REQUEST_NOTIFICATION_PERM,   // BroadcastEvent.RequestNotificationPermission<br>    SECURITY_CHANGED,            // BroadcastEvent.SecurityChanged<br>    USER_SIGN_OUT,               // BroadcastEvent.UserSignOut<br>    USER_SIGN_OUT_INITIATED,     // BroadcastEvent.UserSignOutInitiated<br>    HIDE_READ_COUNT,             // BroadcastEvent.HideReadCount<br>    ONBOARDING_RESET,            // BroadcastEvent.OnboardingReset<br>    RESTORE_IN_PROGRESS,         // BroadcastEvent.RestoreInProgress<br>}</pre><p>This filter pattern lets a subscriber declare exactly what it cares about. The ArticleDetailViewModel only subscribes to ARTICLE_UPDATED and BOOKMARK_SYNC_UPDATE. It never wakes up for OTP_RECEIVED or SECURITY_CHANGED. Clean boundaries.</p><h3>⭐ LocalBroadcast — The Bus</h3><p>Now the bus itself. It’s a Kotlin object — a singleton — holding a single MutableSharedFlow.</p><pre>object LocalBroadcast {<br><br>    private val _events = MutableSharedFlow&lt;Pair&lt;FilterType, BroadcastEvent&gt;&gt;()<br>    private const val TAG = &quot;LocalBroadcast&quot;<br><br>    @JvmStatic<br>    suspend fun postData(type: FilterType, data: BroadcastEvent) {<br>        Log.d(TAG, &quot;Broadcasting: filter=$type event=$data&quot;)<br>        _events.emit(Pair(type, data))<br>    }<br><br>    @JvmStatic<br>    fun observeData(<br>        scope: CoroutineScope,<br>        filters: List&lt;FilterType&gt;,<br>        onReceive: (FilterType, BroadcastEvent) -&gt; Unit<br>    ) {<br>        scope.launch(Dispatchers.Main) {<br>            _events<br>                .filter { filters.contains(it.first) }<br>                .collect { event -&gt;<br>                    onReceive.invoke(event.first, event.second)<br>                }<br>        }<br>    }<br>}</pre><p>Let’s break down what’s happening here:</p><ul><li>_events is a MutableSharedFlow&lt;Pair&lt;FilterType, BroadcastEvent&gt;&gt; — private, not exposed externally.</li><li>postData() is a <strong>suspend function</strong>. Emitting into a SharedFlow is a suspend operation — it must be called from within a coroutine.</li><li>observeData() takes a CoroutineScope. Pass viewModelScope or lifecycleScope, and when that scope is cancelled, the collector automatically stops. No manual cleanup needed.</li></ul><h3>Kotlin-First Call Sites — Extension Functions</h3><p>For Kotlin callers inside a CoroutineScope, we expose two extension functions that make posting a broadcast feel completely natural:</p><pre>fun CoroutineScope.broadcastEvent(type: FilterType, data: BroadcastEvent) {<br>    launch {<br>        LocalBroadcast.postData(type = type, data = data)<br>    }<br>}<br><br>fun CoroutineScope.broadcastEventWithCompletion(<br>    type: FilterType,<br>    data: BroadcastEvent,<br>    onCompleted: () -&gt; Unit<br>) {<br>    launch {<br>        LocalBroadcast.postData(type = type, data = data)<br>    }.invokeOnCompletion {<br>        onCompleted.invoke()<br>    }<br>}</pre><p>The first is your everyday fire-and-forget broadcast. The second gives you a callback once the emission coroutine has finished — useful when you need to trigger navigation or a UI update <em>after</em> the broadcast is guaranteed to have been emitted.</p><h3>⭐ How to Use It</h3><p><strong>Sending a broadcast</strong> from the background sync repository when articles are fetched:</p><pre>class NewsSyncRepository(private val scope: CoroutineScope) {<br><br>    suspend fun syncFeed() {<br>        scope.broadcastEvent(<br>            type = FilterType.FEED_REFRESHING,<br>            data = BroadcastEvent.FeedRefreshing(<br>                currentProgress = 0f,<br>                total = 100f,<br>                remaining = 50<br>            )<br>        )<br>        // ... do actual sync work ...<br>        scope.broadcastEvent(<br>            type = FilterType.FEED_REFRESH_COMPLETE,<br>            data = BroadcastEvent.FeedRefreshComplete<br>        )<br>    }<br>}</pre><p><strong>Observing</strong> in the home screen ViewModel:</p><pre>class HomeViewModel : ViewModel() {<br><br>    init {<br>        LocalBroadcast.observeData(<br>            scope = viewModelScope,<br>            filters = listOf(<br>                FilterType.FEED_REFRESH_COMPLETE,<br>                FilterType.ARTICLE_UPDATED,<br>                FilterType.CATEGORY_UPDATED<br>            )<br>        ) { _, event -&gt;<br>            when (event) {<br>                is BroadcastEvent.FeedRefreshComplete -&gt; reloadFeed()<br>                is BroadcastEvent.ArticleUpdated -&gt; refreshArticle(event.articleId)<br>                is BroadcastEvent.CategoryUpdated -&gt; refreshCategory(event.categoryId)<br>                else -&gt; Unit<br>            }<br>        }<br>    }<br>}</pre><p>No BroadcastReceiver. No IntentFilter. No onResume/onPause registration juggling. The ViewModel subscribes once in init{}, and viewModelScope handles teardown automatically when the ViewModel is cleared.</p><h3>Further Improvements</h3><ol><li><strong>The </strong><strong>FilterType enum is redundant in pure Kotlin projects.</strong><br>Kotlin sealed classes already carry type identity. You can filter the flow using filterIsInstance&lt;T&gt;() directly, removing the need for the enum entirely. The enum was likely introduced for Java interoperability. If your codebase is fully Kotlin, consider simplifying to:</li></ol><pre>// Type-safe, no enum needed<br>_events.filterIsInstance&lt;BroadcastEvent.ArticleUpdated&gt;().collect { event -&gt;<br>    refreshArticle(event.articleId)<br>}</pre><p>2. <strong>replay = 0 means late subscribers miss events.</strong><br>The current MutableSharedFlow() uses default replay = 0. If a subscriber registers <em>after</em> an emission, it will never see that event. For one-time signals like AppReady, consider replay = 1 or a dedicated StateFlow for those specific signals.</p><p><strong>3. </strong><strong>invokeOnCompletion fires on cancellation too.</strong><br>In broadcastEventWithCompletion, the onCompleted callback triggers even if the coroutine is cancelled mid-flight. Always check the cause parameter:</p><pre>}.invokeOnCompletion { cause -&gt;<br>    if (cause == null) onCompleted.invoke() // Only on clean completion<br>}</pre><p><strong>4. Sensitive data on a shared bus.</strong><br>OtpReceived(val otp: String, val smsBody: String) routes raw OTP strings through a global singleton. Any subscriber with access to the right FilterType gets the full payload. Scope sensitive events as tightly as possible — consider a dedicated, narrowly scoped channel for authentication signals rather than the global bus.</p><p><strong>5. Use </strong><strong>Set instead of </strong><strong>List for filters.</strong><br>observeData() accepts a List&lt;FilterType&gt;. A Set is semantically more correct here — duplicate filter entries are meaningless and Set.contains() is O(1) vs O(n) for a List.</p><pre>fun observeData(<br>    scope: CoroutineScope,<br>    filters: Set&lt;FilterType&gt;, // Set, not List<br>    onReceive: (FilterType, BroadcastEvent) -&gt; Unit<br>)</pre><h3>Closing Thoughts</h3><p>The combination of MutableSharedFlow, a sealed class event hierarchy, and coroutine scopes gives you everything LocalBroadcastManager did — and more — with far less ceremony. The sealed class is your single source of truth for all in-app signals. The FilterType enum keeps subscriptions explicit and auditable. The CoroutineScope parameter does all lifecycle management for you.</p><p>This is not groundbreaking architecture — it’s just coroutines doing what they’re already good at. No new dependencies, no new abstractions to learn. Just Kotlin, used well.</p><p><em>Thanks for reading! If this helped you move away from </em><em>LocalBroadcastManager, clap clap clap 👏👏👏 as many times as you can.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=33868db8616f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Scaling Android MVI: Taming Fat ViewModels with DelegateVM ]]></title>
            <link>https://medium.com/@rohitss/scaling-android-mvi-taming-fat-viewmodels-with-delegatevm-5b068d3a36a0?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/5b068d3a36a0</guid>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Tue, 31 Mar 2026 18:05:13 GMT</pubDate>
            <atom:updated>2026-03-31T18:05:13.355Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong>Prerequisites:</strong> This builds on our modernised MVI foundation: <a href="https://medium.com/@rohitss/evolving-android-mvi-kotlin-flows-compose-and-lifecycle-safety-67bd201ad397">Evolving Android MVI: Kotlin Flows, Compose, and Lifecycle Safety</a>.</blockquote><p>MVI keeps screen state predictable, but it creates a new problem: <strong>Fat ViewModels</strong>.</p><p>If your app has a complex “Feature Carousel” component used on the Home Screen, the Search Screen, and the Profile Screen, duplicating the loading, error handling, and state logic across three ViewModels violates DRY. You cannot use inheritance because a ViewModel can only extend one base class, and a carousel is just one piece of a screen.</p><p>We need <strong>Composition</strong>. We need to extract the MVI logic of that component into a reusable unit that any ViewModel can plug in.</p><h3>Introducing AacMviDelegateVM</h3><p>A DelegateVM is a plain class (not an AndroidViewModel) that encapsulates a full MVI loop (ViewState, ViewEffect, ViewEvent) for a specific, reusable UI component.</p><p><strong>It shares code, not state.</strong> Each Host ViewModel instantiates its own unique instance of the Delegate. The Host ViewModel funnels the Delegate’s state and effects into its own MVI stream, and the View remains completely unaware of the delegation.</p><p>Because the Delegate is strictly bound to a single Host ViewModel, it doesn’t need to manage its own lifecycle. It simply borrows the viewModelScope of its Host.</p><h3>1. The AacMviDelegateVM Base Class</h3><pre>open class AacMviDelegateVM&lt;STATE, EFFECT, EVENT&gt; : ViewModelContract&lt;EVENT&gt; {<br><br>    private var _delegateScope: CoroutineScope? = null<br>    protected var delegateScope: CoroutineScope<br>        get() = _delegateScope ?: throw UninitializedPropertyAccessException(&quot;Call initDelegate() first&quot;)<br>        set(value) { _delegateScope = value }<br><br>    // State: SharedFlow with replay=1 ensures the Host gets the current state instantly<br>    private val _viewStates = MutableSharedFlow&lt;STATE&gt;(replay = 1)<br>    val viewStates: SharedFlow&lt;STATE&gt; get() = _viewStates.asSharedFlow()<br><br>    protected var viewState: STATE<br>        get() = _viewState ?: throw UninitializedPropertyAccessException()<br>        set(value) {<br>            _viewState = value<br>            delegateScope.launch { _viewStates.emit(value) }<br>        }<br><br>    // Effects: Channel for one-shot delivery<br>    private val _viewEffects = Channel&lt;EFFECT&gt;(Channel.BUFFERED, BufferOverflow.DROP_OLDEST)<br>    val viewEffects: Flow&lt;EFFECT&gt; get() = _viewEffects.receiveAsFlow()<br><br>    protected var viewEffect: EFFECT<br>        get() = _viewEffect ?: throw UninitializedPropertyAccessException()<br>        set(value) {<br>            _viewEffect = value<br>            delegateScope.launch { _viewEffects.send(value) }<br>        }<br><br>    @CallSuper<br>    override fun process(viewEvent: EVENT) { /* Handle events */ }<br><br>    /**<br>     * Binds the Delegate to the Host ViewModel&#39;s scope and wires up the output.<br>     */<br>    fun initDelegate(<br>        coroutineScope: CoroutineScope,<br>        handleDelegateViewState: (STATE) -&gt; Unit,<br>        handleDelegateViewEffect: (EFFECT) -&gt; Unit,<br>    ) {<br>        _delegateScope = coroutineScope<br>        initViewState()<br>        <br>        delegateScope.launch {<br>            viewStates.collect { handleDelegateViewState(it) }<br>        }<br>        delegateScope.launch {<br>            viewEffects.collect { handleDelegateViewEffect(it) }<br>        }<br>    }<br><br>    @CallSuper<br>    protected open fun initViewState() {}<br>}</pre><h3>2. Building the Delegate</h3><p>Let’s build the reusable FeatureCarouselDelegate:</p><pre>data class CarouselState(val items: List&lt;Item&gt; = emptyList(), val isLoading: Boolean = false)<br>sealed class CarouselEffect { data class ShowError(val msg: String) : CarouselEffect() }<br>sealed class CarouselEvent { object Load : CarouselEvent() }<br><br>class FeatureCarouselDelegate(<br>    private val repo: Repository<br>) : AacMviDelegateVM&lt;CarouselState, CarouselEffect, CarouselEvent&gt;() {<br><br>    override fun initViewState() {<br>        viewState = CarouselState(isLoading = true)<br>        process(CarouselEvent.Load)<br>    }<br><br>    override fun process(viewEvent: CarouselEvent) {<br>        when (viewEvent) {<br>            is CarouselEvent.Load -&gt; loadItems()<br>        }<br>    }<br><br>    private fun loadItems() {<br>        delegateScope.launch {<br>            val data = repo.getFeatures()<br>            viewState = viewState.copy(items = data, isLoading = false)<br>        }<br>    }<br>}</pre><h3>3. Wiring it into the Host ViewModel</h3><p>The Host ViewModel includes the Delegate’s state in its own ViewState and uses initDelegate to funnel the data.</p><pre>data class HomeState(<br>    val carouselState: CarouselState = CarouselState(),<br>    val screenTitle: String = &quot;Home&quot;<br>)<br><br>class HomeViewModel(<br>    application: Application,<br>    private val carouselDelegate: FeatureCarouselDelegate // Unique instance for this VM<br>) : AacMviViewModel&lt;HomeState, HomeEffect, HomeEvent&gt;(application) {<br><br>    init {<br>        viewState = HomeState()<br>        <br>        // Bind the delegate to this ViewModel&#39;s lifecycle<br>        carouselDelegate.initDelegate(<br>            coroutineScope = viewModelScope,<br>            handleDelegateViewState = { delegateState -&gt;<br>                // Merge delegate state into screen state<br>                viewState = viewState.copy(carouselState = delegateState)<br>            },<br>            handleDelegateViewEffect = { delegateEffect -&gt;<br>                // Forward delegate effects<br>                when (delegateEffect) {<br>                    is CarouselEffect.ShowError -&gt; viewEffect = HomeEffect.ShowSnackbar(delegateEffect.msg)<br>                }<br>            }<br>        )<br>    }<br><br>    override fun process(viewEvent: HomeEvent) {<br>        when (viewEvent) {<br>            is HomeEvent.CarouselAction -&gt; carouselDelegate.process(CarouselEvent.Load)<br>            // Handle Home-specific events...<br>        }<br>    }<br>    <br>    // No manual cleanup needed! When HomeViewModel is cleared, <br>    // viewModelScope cancels, which automatically kills all Delegate coroutines.<br>}</pre><h3>The Takeaway</h3><p>By combining Unidirectional Data Flow with Composition, we keep ViewModels focused solely on screen-level orchestration. Complex, reusable UI logic lives isolated, tested, and shared in DelegateVMs, without muddying the inheritance tree or creating lifecycle headaches.</p><p>Thanks for reading!<br><strong>Rohit Surwase</strong></p><p>If you liked this article, clap clap clap 👏👏👏 as many times as you can.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5b068d3a36a0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Evolving Android MVI: Kotlin Flows, Compose, and Lifecycle Safety ]]></title>
            <link>https://medium.com/@rohitss/evolving-android-mvi-kotlin-flows-compose-and-lifecycle-safety-67bd201ad397?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/67bd201ad397</guid>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Tue, 31 Mar 2026 17:56:29 GMT</pubDate>
            <atom:updated>2026-03-31T17:56:29.316Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>Prerequisites: This builds on our foundation: <a href="https://proandroiddev.com/best-architecture-for-android-mvi-livedata-viewmodel-71a3a5ac7ee3">Best Architecture For Android : MVI + LiveData + ViewModel = ❤️</a>. The core concepts of Unidirectional Data Flow, ViewState, ViewEffect, and ViewEvent remain intact.</blockquote><p>The original architecture served us well, but the Android ecosystem has shifted. LiveData is tied to the Android SDK and struggles with one-shot events. SingleLiveEvent was a hack. Jetpack Compose completely changed the UI rendering contract.</p><p>Here is how we evolved our MVI base to use Kotlin Flows, support Jetpack Compose, and guarantee lifecycle safety.</p><h3>1. State: LiveData ➡️ StateFlow</h3><p>LiveData binds lifecycle-awareness into the ViewModel. StateFlow pushes that responsibility to the UI layer, keeping the ViewModel pure Kotlin. It always holds a value and replays it instantly to new collectors, making it the perfect replacement for ViewState.</p><pre>// In AacMviViewModel.kt<br>private val _viewStates: MutableStateFlow&lt;STATE&gt; by lazy { MutableStateFlow(viewState) }<br>override val viewStates: StateFlow&lt;STATE&gt; get() = _viewStates.asStateFlow()<br><br>protected var viewState: STATE<br>    get() = _viewState ?: throw UninitializedPropertyAccessException(&quot;Init viewState first&quot;)<br>    set(value) {<br>        _viewState = value<br>        _viewStates.value = value<br>    }</pre><p><em>Note: We use </em><em>by lazy so the flow is only instantiated after the subclass </em><em>init {} block assigns the initial </em><em>viewState.</em></p><h3>2. Effects: SingleLiveEvent ➡️ Channel</h3><p>SingleLiveEvent was notorious for dropping events or multi-delivery bugs during configuration changes. To handle fire-and-forget ViewEffects (like navigation or Toasts), we use a Channel exposed as a Flow.</p><p>A Channel guarantees exactly-once delivery. If the UI is rotating, the BUFFERED capacity holds the effect until the UI re-attaches.</p><pre>private val _viewEffects: Channel&lt;EFFECT&gt; = Channel(<br>    capacity = Channel.BUFFERED,<br>    onBufferOverflow = BufferOverflow.DROP_OLDEST,<br>    onUndeliveredElement = { Log.w(TAG, &quot;Dropped effect: $it&quot;) } // Hook for analytics<br>)<br>override val viewEffects: Flow&lt;EFFECT&gt; get() = _viewEffects.receiveAsFlow()<br><br>protected var viewEffect: EFFECT<br>    get() = _viewEffect ?: throw UninitializedPropertyAccessException()<br>    set(value) {<br>        _viewEffect = value<br>        viewModelScope.launch { _viewEffects.send(value) }<br>    }<br><br>override fun onCleared() {<br>    super.onCleared()<br>    _viewEffects.close() // Crucial: prevents memory leaks of buffered effects<br>}</pre><h3>3. Safe UI Collection: repeatOnLifecycle</h3><p>Raw Kotlin Flows don’t stop collecting when the Activity goes to the background. To prevent crashes and wasted resources, we enforce repeatOnLifecycle(STARTED) in the Base Activity/Fragment.</p><p>STARTED is critical. If you use RESUMED, your flow pauses when a dialog or permission prompt appears on top of your app. Any effects emitted during that time would be permanently dropped by the Channel.</p><pre>// Inside AacMviActivity.kt onCreate()<br>lifecycleScope.launch {<br>    repeatOnLifecycle(Lifecycle.State.STARTED) {<br>        launch { viewModel.viewStates.collect { renderViewState(it) } }<br>        launch {<br>            // Main.immediate prevents dropping effects during rapid config changes<br>            withContext(Dispatchers.Main.immediate) {<br>                viewModel.viewEffects.collect { renderViewEffect(it) }<br>            }<br>        }<br>    }<br>}</pre><h3>4. Bridging Jetpack Compose</h3><p>To allow incremental migration, we introduce an isForPureCompose flag. When true, XML rendering is bypassed. State collection is handled naturally by Compose&#39;s collectAsStateWithLifecycle().</p><pre>override fun onCreateView(...): View? {<br>    return if (isForPureCompose) {<br>        ComposeView(requireContext()).apply {<br>            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)<br>            setContent {<br>                val viewState = viewModel.viewStates.collectAsStateWithLifecycle()<br>                ComposeViewState(viewState.value)<br>            }<br>        }<br>    } else {<br>        super.onCreateView(inflater, container, savedInstanceState)<br>    }<br>}</pre><p>However, <strong>Effects require explicit collection</strong>. We force developers to call a ComposeViewEffect {} block inside their composable tree. This prevents the silent failures common in open XML methods.</p><pre>@Composable<br>fun ComposeViewEffect(composeViewEffect: (EFFECT) -&gt; Unit) {<br>    val lifecycleOwner = LocalLifecycleOwner.current<br>    LaunchedEffect(viewModel.viewEffects, lifecycleOwner.lifecycle) {<br>        lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {<br>            withContext(Dispatchers.Main.immediate) {<br>                viewModel.viewEffects.collect { composeViewEffect(it) }<br>            }<br>        }<br>    }<br>}</pre><p>Two details worth noting:</p><ol><li><strong>Dispatchers.Main.immediate</strong>: A regular withContext(Dispatchers.Main) posts the effect as a new task on the main thread event loop. If the composable is near a recomposition cycle boundary, the effect could be dispatched after the lifecycle moves below STARTED and get dropped. Main.immediate executes the block in the <em>current</em> event cycle if already on Main, eliminating that race condition.</li><li><strong>Why not automatic?</strong>: Making effect collection opt-in forces the developer to consciously place the ComposeViewEffect{} call in their composable tree. Silent drop (forgetting to call it) is a compile-time-visible omission rather than a silent runtime bug, and it keeps state and effect concerns cleanly separated.</li></ol><h3>Example: A Compose Activity</h3><pre>class MainActivity : AacMviActivity&lt;MainState, MainEffect, MainEvent, MainViewModel&gt;(<br>    isForPureCompose = true<br>) {<br>    override val viewModel: MainViewModel by viewModels()<br><br>    // Required by base contract, but safe to ignore in Compose mode<br>    override fun renderViewEffect(viewEffect: MainEffect) {}<br><br>    @Composable<br>    override fun ComposeViewState(viewState: MainState) {<br>        MainScreen(state = viewState)<br><br>        // Explicitly handle effects<br>        ComposeViewEffect { effect -&gt;<br>            when (effect) {<br>                is MainEffect.ShowToast -&gt; Toast.makeText(this, effect.message, Toast.LENGTH_SHORT).show()<br>            }<br>        }<br>    }<br>}</pre><h3>The Full Base Classes:</h3><pre>private const val TAG = &quot;AacMviActivity&quot;<br><br>abstract class AacMviActivity&lt;STATE, EFFECT, EVENT, VM : AacMviViewModel&lt;STATE, EFFECT, EVENT&gt;&gt;(<br>    val isForPureCompose: Boolean,<br>) : AppCompatActivity() {<br><br>    abstract val viewModel: VM<br><br>    override fun onCreate(savedInstanceState: Bundle?) {<br>        super.onCreate(savedInstanceState)<br>        enableEdgeToEdge()<br>        if (isForPureCompose) {<br>            setContent {<br>                val viewState = viewModel.viewStates.collectAsStateWithLifecycle()<br>                ComposeViewState(viewState.value)<br>            }<br>        } else {<br>            lifecycleScope.launch {<br>                repeatOnLifecycle(Lifecycle.State.STARTED) {<br>                    launch {<br>                        viewModel.viewStates.collect {<br>                            if (logging) Log.d(MAIN_TAG, &quot;$TAG -&gt; observed viewState : $it&quot;)<br>                            renderViewState(it)<br>                        }<br>                    }<br>                    launch {<br>                        withContext(Dispatchers.Main.immediate) {<br>                            viewModel.viewEffects.collect {<br>                                if (logging) Log.d(MAIN_TAG, &quot;$TAG -&gt; observed viewEffect : $it&quot;)<br>                                renderViewEffect(it)<br>                            }<br>                        }<br>                    }<br>                }<br>            }<br>        }<br>    }<br><br>    open fun renderViewState(viewState: STATE) {}<br>    abstract fun renderViewEffect(viewEffect: EFFECT)<br><br>    @CallSuper<br>    @Composable<br>    open fun ComposeViewState(viewState: STATE) {<br>        if (logging) Log.d(MAIN_TAG, &quot;$TAG -&gt; ComposeViewState: $viewState&quot;)<br>    }<br><br>    @Composable<br>    fun ComposeViewEffect(composeViewEffect: (EFFECT) -&gt; Unit) {<br>        val lifecycleOwner = LocalLifecycleOwner.current<br>        LaunchedEffect(viewModel.viewEffects, lifecycleOwner.lifecycle) {<br>            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {<br>                withContext(Dispatchers.Main.immediate) {<br>                    viewModel.viewEffects.collect {<br>                        if (logging) Log.d(MAIN_TAG, &quot;$TAG -&gt; ComposeViewEffect: $it&quot;)<br>                        composeViewEffect(it)<br>                    }<br>                }<br>            }<br>        }<br>    }<br>}</pre><pre>private const val TAG = &quot;AacMviFragment&quot;<br><br>abstract class AacMviFragment&lt;STATE, EFFECT, EVENT, VM : AacMviViewModel&lt;STATE, EFFECT, EVENT&gt;&gt;(<br>    val isForPureCompose: Boolean,<br>) : Fragment() {<br><br>    abstract val viewModel: VM<br><br>    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {<br>        return if (isForPureCompose) {<br>            ComposeView(requireContext()).apply {<br>                setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)<br>                setContent {<br>                    val viewState = viewModel.viewStates.collectAsStateWithLifecycle()<br>                    ComposeViewState(viewState.value)<br>                }<br>            }<br>        } else {<br>            super.onCreateView(inflater, container, savedInstanceState)<br>        }<br>    }<br><br>    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {<br>        super.onViewCreated(view, savedInstanceState)<br>        viewLifecycleOwner.lifecycleScope.launch {<br>            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {<br>                if (isForPureCompose.not()) {<br>                    launch {<br>                        viewModel.viewStates.collect {<br>                            if (logging) Log.d(MAIN_TAG, &quot;$TAG -&gt; observed viewState : $it&quot;)<br>                            renderViewState(it)<br>                        }<br>                    }<br>                    launch {<br>                        withContext(Dispatchers.Main.immediate) {<br>                            viewModel.viewEffects.collect {<br>                                if (logging) Log.d(MAIN_TAG, &quot;$TAG -&gt; observed viewEffect : $it&quot;)<br>                                renderViewEffect(it)<br>                            }<br>                        }<br>                    }<br>                }<br>            }<br>        }<br>    }<br><br>    open fun renderViewState(viewState: STATE) {}<br>    abstract fun renderViewEffect(viewEffect: EFFECT)<br><br>    @CallSuper<br>    @Composable<br>    open fun ComposeViewState(viewState: STATE) {<br>        if (logging) Log.d(MAIN_TAG, &quot;$TAG -&gt; ComposeViewState: $viewState&quot;)<br>    }<br><br>    @Composable<br>    fun ComposeViewEffect(composeViewEffect: (EFFECT) -&gt; Unit) {<br>        val lifecycleOwner = LocalLifecycleOwner.current<br>        LaunchedEffect(viewModel.viewEffects, lifecycleOwner.lifecycle) {<br>            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {<br>                withContext(Dispatchers.Main.immediate) {<br>                    viewModel.viewEffects.collect {<br>                        if (logging) Log.d(MAIN_TAG, &quot;$TAG -&gt; ComposeViewEffect: $it&quot;)<br>                        composeViewEffect(it)<br>                    }<br>                }<br>            }<br>        }<br>    }<br>}</pre><h3>What Hasn’t Changed</h3><p>The conceptual contracts — ViewState, ViewEffect, ViewEvent — are identical to the original article. The file-per-feature convention (keeping all three in one file per screen) is still the recommendation. The process() entry point and copy()-based state mutation are unchanged. This evolution is purely in the plumbing, not the pattern.</p><p>Thanks for reading!<br><strong>Rohit Surwase</strong></p><p>If you liked this article, clap clap clap 👏👏👏 as many times as you can.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=67bd201ad397" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Best Architecture For Android : MVI + LiveData + ViewModel = ❤️]]></title>
            <link>https://proandroiddev.com/best-architecture-for-android-mvi-livedata-viewmodel-71a3a5ac7ee3?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/71a3a5ac7ee3</guid>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[architecture]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Sat, 28 Mar 2020 13:37:43 GMT</pubDate>
            <atom:updated>2021-08-22T04:20:44.960Z</atom:updated>
            <content:encoded><![CDATA[<h3>Best Architecture For Android : <strong>MVI + LiveData + ViewModel</strong> = ❤️</h3><h4>Best of MVVM and MVI architecture patterns merged into one, for a perfect architecture for any Android project.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*GzCcYxuo2egUdPc4lyWXZw.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@timmossholder?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Tim Mossholder</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><blockquote><strong>TL;DR<br></strong>If you are already aware of basic principles of architecture patterns and MVVM and MVI patterns in detail then skip the basics and jump to <strong>MVI + LiveData + ViewModel </strong>(or second)<strong> </strong>section of the article.</blockquote><h3>Preface</h3><p>There are so many architecture patterns available, each has some pros and cons. All those patterns try to achieve the same <em>basic principles of architecture</em>:</p><p><strong>1. <em>Separation of concerns</em></strong><em> (</em><strong><em>SoC</em></strong><em>): It is a design principle for separating a computer program into distinct sections such that each section addresses a separate concern. A concern is anything that matters in providing a solution to a problem.</em></p><p>This principle is closely related to the <strong>Single Responsibility Principle </strong>of object-oriented programming which<em> </em>states that<em> “every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class, module or function.”</em> -Wikipedia</p><p><strong>2. Drive UI from a model: </strong>App should drive UI from a model, preferably a persistent model. <em>Models</em> are independent from the View objects and app components, so they&#39;re unaffected by the app&#39;s life-cycle and the associated concerns.</p><p>Let’s also look at the summary of some popular architecture patterns-</p><h4>⭐ MVC Architecture:</h4><p>Model-View-Controller architecture by <a href="https://en.wikipedia.org/wiki/Trygve_Reenskaug">Trygve Reenskaug</a> is the base of all modern architecture patterns. Let’s see the responsibility of each component defined on it’s Wikipedia page-</p><blockquote>* The <strong>model is responsible for managing the data</strong> of the application. <strong>It receives user input from the controller</strong>.<br>* The <strong>view means presentation of the model in a particular format</strong>.<br>* The controller responds to the user input and performs interactions on the data model objects. The <strong>controller receives the input, optionally validates it and then passes the input to the model</strong>.</blockquote><p>So, the model is responsible for representing state, structure, and behavior of the view and view is nothing but the representation of that model.</p><h4>⭐ MVVM Architecture:</h4><p>In Model-View-ViewModel architecture, view has the instance of ViewModel and it calls respective function based on the user input/action. Also, view observes different observable properties of ViewModel for changes. ViewModel processes user input based on the business logic and modifies respective observable property.</p><h4>⭐ MVI Architecture:</h4><p>In Model-View-Intent architecture, view exposes <em>view-events</em> (user input/action) and observes model for <em>view-state</em> changes. We process <em>view-events</em> and convert them into respective <em>intent</em> and pass it to the model. The model layer creates a new immutable <em>view-state</em> using <em>intent</em> and previous <em>view-state</em>. So, this way it follows the <em>Unidirectional Data Flow</em> principle i.e. data flows only in one direction: View &gt; Intent &gt; Model &gt; View.</p><p>In summary, the best part of MVVM architecture is ViewModel but I think, it does not follow the concept of <em>model</em> defined in MVC pattern, as, in MVVM DAO (Data Access Object) abstraction is considered as model and view observes multiple observable properties from ViewModel for state changes and view is not driven by model, directly. Also, these multiple observable properties from ViewModel can cause state overlapping issue (two different states being shown unexpectedly).</p><p>MVI pattern solves this problem by adding an actual ‘model’ layer which is observed by view for state changes. As this model is an immutable and single source of truth for the current view state, state overlapping will not happen.</p><p>In the following architecture, I tried to combine the best of MVVM and MVI patterns for a better architecture of any Android project, on top of that I abstracted as many things as possible by creating base classes for View and ViewModel.</p><p>🌟 <strong>MVI + LiveData + ViewModel</strong> <strong>= ❤️ Architecture:</strong><br>Before proceeding, let’s reemphasize some basic terms from MVI architecture:<br><strong><em>ViewState</em></strong>: As the name suggests this is part of the model layer, and our view observes this model for state changes. <em>ViewState</em> should represent the current state of the view at any given time. So this class should have all the variable content on which our view is dependent. Every time there is any user input/action we will expose modified <em>copy</em> (to maintain the previous state which is not being modified) of this class. We can create this model using Kotlin’s <strong>data</strong> <strong>class</strong>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ff06fdff8c4f73bba41f1f4d5581832d/href">https://medium.com/media/ff06fdff8c4f73bba41f1f4d5581832d/href</a></iframe><p><strong>ViewEffect</strong>: In Android, we have certain actions that are more like fire-and-forget, for example- Toast, in those cases, we can not use ViewState as it maintains state. It means, if we use ViewState to show a Toast, it will be shown again on configuration change or every time there is a new state unless and until we reset its state by passing ‘toast is shown’ event. And if you do not wish to do that, you can use ViewEffect as it is based on <a href="https://github.com/android/architecture-samples/blob/dev-todo-mvvm-live/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/SingleLiveEvent.java">SingleLiveEvent</a> and does not maintain state. ViewEffect is also part of our model, and we can create it using Kotlin’s <strong>sealed class</strong>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7e090ee963fe08319d19f59d664db0d0/href">https://medium.com/media/7e090ee963fe08319d19f59d664db0d0/href</a></iframe><p><strong>ViewEvent</strong>: It represents all actions/events a user can perform on the view. This is used to pass user input/action to the ViewModel. We can create this event set using Kotlin’s <strong>sealed class</strong>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1cbb49408144f11e861d486c695fe880/href">https://medium.com/media/1cbb49408144f11e861d486c695fe880/href</a></iframe><p>I suggest you, keep these three classes in a single file as it will give you an overall idea of all doable actions and variable content being handled by the target view.</p><p>Now, let’s dive deeper into the architecture:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/769/1*w0QeeQqrnISXLhYkYZWoAg.png" /></figure><p>The above diagram may have given you the core idea of this architecture. If not, the core idea of this architecture is, we are including an actual immutable model layer in the MVVM architecture and our view is dependent this model for state changes. This way ViewModel will have to modify and expose this single model.</p><p>To avoid redundancy and simplify the use of this architecture in multiple places, I have created two abstract classes one for our view (separate for activity, fragment, custom-view) and one for ViewModel.</p><p><strong>AacMviViewModel: </strong>A generic base class to create ViewModel. It needs three classes STATE, EFFECT, and EVENT. We have already seen an example of these classes above.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/efbf22aae2c341a67c398c32e69e1e84/href">https://medium.com/media/efbf22aae2c341a67c398c32e69e1e84/href</a></iframe><p>As you can see we have viewState: STATE and viewEffect: EFFECT and two private LiveData containers _viewStates: MutableLiveData&lt;STATE&gt; and _viewEffect: SingleLiveEvent&lt;EFFECT&gt; which are being exposed using public functions viewStates() and viewEffects(). Please note we are extending <a href="https://developer.android.com/reference/androidx/lifecycle/AndroidViewModel">AndroidViewModel</a> as it will allow us to use the Application context (only) if required. Moreover, we are logging each viewEvent, that we will be processing.</p><p>This is how we create a ViewModel for any of our activity/fragment/view:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/faa437701302a52b228591191ebfbcd5/href">https://medium.com/media/faa437701302a52b228591191ebfbcd5/href</a></iframe><p>All we have to do is initialize viewState inside init{} block and modify the viewState further using copy() function of the data class whenever required. Please do not modify the same instance of viewState instead modify its copy, this way we are keeping ViewState immutable. If you modify the same instance of viewState, you may encounter an unexpected behavior as you may change some properties which are being processed by the view.</p><pre>viewState = viewState.copy(fetchStatus = FetchStatus.Fetched, newsList = result.data)</pre><p>That’s it, the rest of the part (i.e. _viewStates.setValue() ) is being handled by the AacMviViewModel class.</p><p><strong>AacMviActivity: </strong>A generic abstract class to create compatible Activities for this architecture.<br>(Please refer <a href="https://github.com/RohitSurwase/AAC-MVI-Architecture">this repository</a> for generic classes required for fragment and custom-views.)</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c5d717faf85043fb9ee5a487e1bffb98/href">https://medium.com/media/c5d717faf85043fb9ee5a487e1bffb98/href</a></iframe><p>It has viewModel , renderViewState() and renderViewEffect() abstract properties/functions which we need to implement. Also, it creates viewStateObserver, viewEffectObserver LiveData-Observers internally and starts observing viewStates() and viewEffects() exposed by the ViewModel in onCreate() of the Activity. So, this abstract activity does everything that we would have to do anyway inside each activity. Moreover, it logs each observed viewState and viewEffect.</p><p>Now, creating a new activity for this architecture is very easy:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9ee35839d8a390789ff33bc12ffd97bf/href">https://medium.com/media/9ee35839d8a390789ff33bc12ffd97bf/href</a></iframe><p>That’s all, with this we have everything in place, working seamlessly, logging each action and content we are processing. No chance of state overlapping as the model is a single source of truth for the view’s state change.</p><p>Note: If you are new to this ‘model-driven UI’ you may think we are adding more complexity than straight forward processing as for some complex views ViesState data class will have so many properties as it has to have content for each widget and its visibility and so on. But believe me, it will pay off as tracking down the cause of any issue/crash will be super easy.</p><p><strong>Further Improvements:</strong><br>1. Many of you would suggest using <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-conflated-broadcast-channel/">ConflatedBroadcastChannel</a> as it posts the latest values only but it is still experimental so I preferred to use LiveData. And anyway even if we decide to use ConflatedBroadcastChannel/Flow in the future, we will have to modify the abstract classes only.</p><p>2. What if we have single activity and multiple fragments or custom-views? How can we communicate between activity-to-fragment, fragment-to-fragment? F̵o̵r̵ ̵t̵h̵i̵s̵ ̵u̵s̵e̵ ̵c̵a̵s̵e̵,̵ ̵w̵e̵ ̵h̵a̵v̵e̵ ̵i̵n̵t̵r̵o̵d̵u̵c̵e̵d̵ ̵D̵a̵t̵a̵S̵t̵o̵r̵e̵,̵ ̵m̵o̵r̵e̵ ̵o̵n̵ ̵t̵h̵i̵s̵ ̵i̵n̵ ̵t̵h̵e̵ ̵n̵e̵x̵t̵ ̵a̵r̵t̵i̵c̵l̵e̵.̵<br><strong>Update: </strong>The moment I started to write about our <em>DataStore </em>implementation, I realized it was breaking MVI’s core concept, so dropped that idea.</p><p>For a detailed version (with some improvements) of this architecture that handles Activities, Fragments, and <strong>Custom Views </strong>with an example, please check out this <a href="https://github.com/RohitSurwase/AAC-MVI-Architecture">GitHub repository</a>.</p><p><strong>Credits &amp; References:<br></strong>⚈ Special thanks to my colleagues <a href="https://medium.com/u/68db08e4caf">Aalap Shah</a> and <a href="https://medium.com/u/d369ca0da82a">Akhilesh Swarnkar</a> for questioning each component/concept that I have used and helping me evolve this architecture. We had so many discussions over the incremental iteration of this architecture. Thanks to both of you for asking me to minimize and simplify as many things as possible so further implementation becomes easy.<br>⚈ <a href="https://developer.android.com/jetpack/docs/guide">Official guide on MVVM Architecture</a><br>⚈ <a href="http://hannesdorfmann.com/android/mosby3-mvi-1">MVI Architecture by Hannes Dorfmann</a><br>⚈ <a href="https://github.com/kanawish/android-mvi-sample">MVI Architecture by Etienne Caron</a> &amp; <a href="https://github.com/kanawish/upvote">this example</a><br>⚈ <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">MVC Architecture - Wikipedia</a><br>⚈ <a href="https://github.com/kaushikgopal/movies-usf-android">Concept of ViewEffect by Kaushik Gopal</a><br>⚈ <a href="https://kotlinlang.org/docs/reference/properties.html#backing-properties">Kotlin Backing Properties</a><br>⚈ Wikipedia- <a href="https://en.wikipedia.org/wiki/Separation_of_concerns">Separation of concerns</a> &amp; <a href="https://en.wikipedia.org/wiki/Single_responsibility_principle">Single responsibility principle</a><br>⚈ <a href="https://github.com/RohitSurwase/AAC-MVI-Architecture"><strong>GitHub: source code and example</strong></a></p><p>Thanks for reading!<br><strong>Rohit Surwase</strong></p><p>If you liked this article, clap clap clap 👏👏👏 as many times as you can.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=71a3a5ac7ee3" width="1" height="1" alt=""><hr><p><a href="https://proandroiddev.com/best-architecture-for-android-mvi-livedata-viewmodel-71a3a5ac7ee3">Best Architecture For Android : MVI + LiveData + ViewModel = ❤️</a> was originally published in <a href="https://proandroiddev.com/">ProAndroidDev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Test Robo Scripts Locally: Useful For Firebase Test Lab & Pre-launch Reports]]></title>
            <link>https://medium.com/android-news/test-robo-scripts-locally-useful-for-firebase-test-lab-pre-launch-reports-41da83d5769f?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/41da83d5769f</guid>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[testing]]></category>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Sun, 27 Oct 2019 13:24:03 GMT</pubDate>
            <atom:updated>2020-03-29T01:50:34.921Z</atom:updated>
            <content:encoded><![CDATA[<h4>I found the least known way to test robo scripts locally. Using this, we can be assured that the robo scripts that we have recorded &amp; uploaded for the <strong>Google </strong>Play Store Pre-launch Report or Firebase Test Lab will work, most probably.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/612/0*T6Evp2jguuqEu4xI" /><figcaption>Monkey/Robo Test (Source: https://www.freeimages.com)</figcaption></figure><p>I am sure, most Android developers are aware of <strong>Firebase Test Lab</strong> and <strong>Google Play Console Pre-launch Report</strong>. These are really useful tools. Today, I am not gonna explain why, when and how to use those tools. But, I am gonna tell you, how can we get the most out of those tools with minimum efforts. So, before we begin with our topic, let me take everyone on the same page.</p><h4>Robo Script:</h4><p><strong>Robo Test </strong>(similar to Monkey Test)<strong> </strong>is a test performed by a robo on the target app which usually performs random actions by clicking random UI elements. But, that is not what we want. For example, we might want to test a new user onboarding flow or day-night mode. So, random actions would not satisfy our needs. We would want to have control over robo actions. That’s where robo scripts come into play. <strong>Robo Script</strong> is a JSON file that has a set of actions (commands that robo can understand 😊) that robo can perform in the given order. And, creating this robo script is also simple, all you have to do is record yourself walking through the desired workflow using Android Studio. You can find this recording option under <strong>Android Studio &gt; Tools &gt; Firebase &gt; Test Lab &gt; Record Robo Script</strong>. Once done, you have to upload the script file on the <strong>Firebase Test Lab</strong> and <strong>Google Play Console Pre-launch Report </strong>console, separately.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6398fbaf8b271bb7082d7d033816c5ee/href">https://medium.com/media/6398fbaf8b271bb7082d7d033816c5ee/href</a></iframe><h4><strong>Problem:</strong></h4><p>Whether it is <strong>Firebase Test Lab</strong> or <strong>Google Play Console Pre-launch Report </strong>the robo scripts we have recorded and uploaded do not work as expected most of the time. It fails in-between and we have to retry with another script. But we are already short on the number of retries we can get as <strong>Play Console Pre-launch Report </strong>runs only when we upload a new APK and with the free plan of <strong>Firebase Test Lab, </strong>we are allowed to run tests only once in 24hrs. So, there should be a way to test those robo scripts multiple times, locally before we upload it on the console. How can we do this?</p><p>After searching in every possible way I was pretty sure that this is not possible as of now so I decided to request this feature as a part of Firebase Assistance Tool in Android Studio. I sent the feature request and I got a response too. <em>Noel </em>from Firebase Support team responded and informed me that this is an interesting feature that can be considered and forwarded it ahead. And after a few days, <em>Noel</em> came up with the solution that he got from the team. Here is the elaborated version of the solution given by him.</p><h4><strong>Solution:</strong></h4><p>You might have heard about <a href="https://developer.android.com/training/testing/crawler"><strong>App Crawler</strong></a><strong>, </strong>it is part of<strong> Android Jetpack </strong>and works the same as Robo Test, that too locally. But the official doc or any other source does not say that we can provide a <strong>Robo Script </strong>to the App Crawler, right? Let’s see-</p><p>Here are the steps to setup App Crawler that can be used for our purpose<br>(I am assuming you already have a recent version of Android SDK)-</p><p><strong>Step 1:</strong> <a href="https://dl.google.com/appcrawler/beta1/app-crawler.zip">Download the crawler binary archive</a> and extract to the directory of your choice.<br><strong>Step 2:</strong> Start an emulator or connect a physical device using a USB cable.<br><strong>Step 3:</strong> Open the terminal/command window where you have extracted the app-crawler binary.</p><p>That’s all, simple, isn’t it?</p><p><strong>Step 4: </strong>You can invoke the crawler to test target apk with random actions, just the way Test Lab does, using the following command-</p><pre>java -jar crawl_launcher.jar --apk-file &lt;apk-path&gt; --android-sdk &lt;android-sdk-path&gt;</pre><p>Now, here is the catch, Can we provide our robo script to app crawler? Yes… we can… though the official doc does not say anything about this feature. Thankfully, I got to know about this from <em>Noel and the team.</em></p><h4>Step 5: Provide a Robo script to the App crawler using following command-line option-</h4><blockquote><strong>— robo-script-file &lt;robo-script-path&gt;</strong></blockquote><pre>java -jar crawl_launcher.jar --robo-script-file &lt;robo-script-path&gt; --apk-file &lt;apk-path&gt; --android-sdk &lt;android-sdk-path&gt;</pre><p>Hurray! Mission accomplished.</p><p>Now, that we can run and test the robo scripts locally, it is time to fix them. For that, we can use logcat files generated by App Crawler, it is stored under the <strong>build</strong> &gt; <strong>crawl_output </strong>directory.</p><p>I hope, this helps you.</p><h4>Useful Resources:</h4><ol><li><a href="https://firebase.google.com/docs/test-lab/android/robo-ux-test">Firebase Test Lab Robo tests</a></li><li><a href="https://developer.android.com/training/testing/crawler">Setup App Crawler</a></li><li><a href="https://support.google.com/googleplay/android-developer/answer/7002270?hl=en">Use pre-launch reports to identify issues : Control how the pre-launch report explores your app</a></li></ol><p>Thanks for reading!<br><strong>Rohit Surwase</strong></p><p>If you liked the article, clap clap clap 👏👏👏 as many times as you can.</p><p><strong>LIKED SO MUCH!</strong> Medium allows up to <strong>50 claps</strong>. <br>Also, post a <strong>Tweet on </strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a>.</p><p>Let’s be friends on <a href="http://in.linkedin.com/in/rohitss5"><strong><em>LinkedIn</em></strong></a>, <a href="http://medium.com/@rohitss"><strong><em>Medium</em></strong></a><strong><em>, </em></strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a><strong> </strong>and <a href="http://github.com/RohitSurwase/"><strong><em>GitHub</em></strong></a><strong><em>.</em></strong></p><p><a href="https://proandroiddev.com/best-architecture-for-android-mvi-livedata-viewmodel-71a3a5ac7ee3">Best Architecture For Android : MVI + LiveData + ViewModel = ❤️</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=41da83d5769f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/android-news/test-robo-scripts-locally-useful-for-firebase-test-lab-pre-launch-reports-41da83d5769f">Test Robo Scripts Locally: Useful For Firebase Test Lab &amp; Pre-launch Reports</a> was originally published in <a href="https://medium.com/android-news">AndroidPub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Add Stable Coroutines to Android-Kotlin Project]]></title>
            <link>https://medium.com/android-news/add-stable-coroutines-to-android-kotlin-project-731e3d9d96e3?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/731e3d9d96e3</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[coroutine]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[kotlin-beginners]]></category>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Wed, 17 Oct 2018 06:01:02 GMT</pubDate>
            <atom:updated>2020-03-29T01:50:10.769Z</atom:updated>
            <content:encoded><![CDATA[<h4>We will see, how can we add the latest stable version of Coroutines to Android project.</h4><p>This article is a part of “<a href="https://android.jlelse.eu/coroutines-basic-terminologies-and-usage-b4242bd1b2a4"><strong>Android Kotlin Coroutines: Basic Terminologies &amp; Usage</strong></a>” article.</p><blockquote><strong>Updated</strong> (30 Oct 2018)<strong> </strong>for <strong>stable </strong>Kotlin plugin v1.3.<br><strong>Note: </strong>Now onward, this post will not be updated unless required.<strong> </strong>So, it would be better if you add required dependencies from the official doc: <a href="https://github.com/Kotlin/kotlinx.coroutines"><strong>https://github.com/Kotlin/kotlinx.coroutines</strong></a></blockquote><p>Here we are going see the steps involved in adding the stable version of Coroutines included in Kotlin v1.3.</p><p><strong>Android Studio</strong>:<strong><br>Step 1. </strong>Go to Tools → Kotlin → Configure Kotlin Plugin Updates, select “Stable” in the Update channel drop-down list, and then click Check for updates.</p><p><strong>Step 2.</strong> Add following lines to your project level <strong>build.gradle<br></strong>(As EAPs are not public we need to add private URL)</p><pre>dependencies {<br>    //...<br>    classpath &#39;org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0&#39;<br>}</pre><p><strong>Step 3.</strong> Add following lines to your app/module level <strong>build.gradle<br></strong>We are adding<strong> </strong><em>coroutines-core</em> along with <em>coroutines-android</em>.</p><pre>dependencies {<br>    //...<br>    implementation &#39;org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0&#39;<br>    implementation &#39;org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0&#39;<br>}</pre><p>Now, sync your project with gradle files and you are ready use the latest Coroutines.</p><p>Thanks for reading!<br><strong>Rohit Surwase</strong></p><p>If you liked the article, clap clap clap 👏👏👏 as many times as you can.</p><p><strong>LIKED SO MUCH!</strong> Medium allows up to <strong>50 claps</strong>. <br>Also, post a <strong>Tweet on </strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a>.</p><p>Let’s be the friends on <a href="http://in.linkedin.com/in/rohitss5"><strong><em>LinkedIn</em></strong></a>, <a href="http://medium.com/@rohitss"><strong><em>Medium</em></strong></a><strong><em>, </em></strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a><strong> </strong>and <a href="http://github.com/RohitSurwase/"><strong><em>GitHub</em></strong></a><strong><em>.</em></strong></p><p><a href="https://proandroiddev.com/best-architecture-for-android-mvi-livedata-viewmodel-71a3a5ac7ee3">Best Architecture For Android : MVI + LiveData + ViewModel = ❤️</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=731e3d9d96e3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/android-news/add-stable-coroutines-to-android-kotlin-project-731e3d9d96e3">Add Stable Coroutines to Android-Kotlin Project</a> was originally published in <a href="https://medium.com/android-news">AndroidPub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Kotlin Coroutines: Basic Terminologies & Usage]]></title>
            <link>https://medium.com/android-news/coroutines-basic-terminologies-and-usage-b4242bd1b2a4?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/b4242bd1b2a4</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[kotlin]]></category>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Wed, 17 Oct 2018 06:01:02 GMT</pubDate>
            <atom:updated>2020-03-29T01:49:43.175Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/636/1*Nc1kvMztZC2aerJxVmdNUA.jpeg" /><figcaption>Modified (Original Photo by <a href="https://unsplash.com/photos/-mXmp_bcMHw?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Nick Dietrich</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a>)</figcaption></figure><h4>The common terms used in Coroutines. Knowing these terms in advance will ease your learning process.</h4><blockquote>This article is a brief summary of official Kotlin doc and a few other resources that I have gone through over time during my learning. This is my attempt to simplify common terms used in the context of coroutines and give you the taste of the power, coroutine gives you which would inspire you to jump in the world of coroutines as asynchronous or non-blocking programming is a new reality.</blockquote><p>Developing Apps using Kotlin is really fun. <em>Code less, perform better</em> suits very well for Kotlin. And with coroutines, it becomes sweeter. <strong>Coroutines are now stable</strong> and can be used within an early-access-preview version of Kotlin v1.3. So, it is the best time to learn about coroutines and start using it in your Android projects.</p><h4>What are Coroutines?</h4><p><em>“A coroutine is an instance of suspendable computation, conceptually similar to a thread, in the sense that it takes a block of code to run and has a similar life-cycle, it is created and started, but it is not bound to any particular thread. It may suspend its execution in one thread and resume in another one. Moreover, like a future or promise, it may complete with some result or exception.”</em></p><p><strong>Coroutines are lightweight threads </strong>and they are so cheap to create which open the doors to asynchronous programming for developers. This means we can call more than one functions asynchronously and get the result in a shorter duration in very effective way. Now imagine how faster our validations would be as we can call them asynchronously. All those things (API calls, Database access…) which need a background thread to process are just a few lines away than creating separate worker threads for it and then managing it. And making coroutines life-cycle aware is so simple.</p><pre>suspend fun validateEntries(str: String): Boolean =<br>    <em>coroutineScope </em><strong>{<br>        </strong>val deferred1 = <em>async </em><strong>{ </strong>firstValidation(str) <strong>}<br>        </strong>val deferred2 = <em>async </em><strong>{ </strong>secondValidation(str) <strong>}<br>        </strong>deferred1.await() &amp;&amp; deferred2.await()<br>    <strong>}</strong></pre><p>Coroutine is language feature of Kotlin, it means Kotlin provides core level APIs (like suspending function) which enable various other libraries to utilize coroutines such as <strong>kotlinx.coroutines</strong>, a library developed by JetBrains which need to be added in any Kotlin project which provides a number of high-level coroutine-enabled primitives. Otherwise, you can directly use core level APIs to build your own.</p><blockquote>If you are curious about adding the latest stable version of coroutines to your project, you can refer the link I have shared at the end of this article.</blockquote><h4>Suspending Function</h4><p>“A suspending function (marked with suspend modifier) may suspend the execution of the code without blocking the current thread of execution by invoking other suspending functions.” So how does it work? Instead of returning just result, a suspending function also returns context which is used by the caller. In simple words, it is a function which executes a part of the code and then suspends while keeping the context and reference to the remaining code (like a lambda consisting remaining code). So whenever the function resumes it has the context and the remaining code that need to be executed. Suspending functions are only allowed to be called from a coroutine or another suspend function.</p><h4>CoroutineBuilder</h4><p><em>“A function that takes some </em><strong><em>suspending lambda </em></strong><em>(lambda </em>marked with suspend modifier<em>) as an argument, creates a coroutine, and, optionally, gives access to its result in some form.”</em></p><pre>fun suspendableLambda(block: suspend () -&gt; Unit) = //...</pre><p>Few of coroutine builders provided by <strong>kotlinx.coroutines</strong>:<br><strong>launch {}</strong>: Launch a<strong> </strong>coroutine that does not have any result<strong>, </strong>it<strong> </strong>returns Job.<br><strong>async{}</strong>: Returns a single value result using Deffered- a light-weight non-blocking future that represents a promise to provide a result later. So, we can use .await() on a deferred value to get its eventual result, but Deferred is also a Job, so we can cancel it if needed.<br><strong>runBlocking{}</strong>: Blocks current thread until the execution of coroutine.</p><p>delay is a special suspending function that does not block a thread but suspends coroutine.</p><h4>CoroutineScope</h4><p>Each coroutine run inside a scope defined by us, so we can make it application wide or specific for a view with well-defined life-cycle such as Activities or Fragments. Every coroutine builder is an extension on CoroutineScope. Each coroutine waits for all the coroutines inside their block/scope to complete before completing themselves. <br>CoroutineScope provides properties like coroutineContext and it is a set of various elements like Job of the coroutine and its <em>dispatcher</em>. We can check whether coroutine is active or not using isActive property of Job. GlobalScope is used to launch a coroutine with a lifetime of the whole application.</p><h4>CoroutineDispatcher</h4><p>CoroutineDispatcher can confine (restrict) coroutine execution to a specific thread, dispatch it to a thread pool, or let it run unconfined (unrestricted). It means CoroutineDispatcher determines what thread or threads the corresponding coroutine uses for its execution.</p><p><strong>kotlinx.coroutines </strong>provides following dispatchers:<br><strong>Dispatchers.Default</strong>: Used by all standard builder if no dispatcher is specified. It uses a common pool of shared background threads. This is an appropriate choice for compute-intensive coroutines that consume CPU resources.<br><strong>Dispatchers.IO</strong>: Uses a shared pool of on-demand created threads and is designed for offloading of IO-intensive <em>blocking</em> operations (like file I/O and blocking socket I/O).<br><strong>Dispatchers.Unconfined</strong> — Unrestricted to any specific thread or pool and should not be used normally in code. There will be certain use-cases where we might need to use this.<br><strong>newSingleThreadContext</strong>: To create new private single-threaded coroutine context.<br><strong>newFixedThreadPoolContext</strong>: To create private thread pool of fixed size.<br><strong>asCoroutineDispatcher</strong>: Extension function to convert <em>Executor</em> to dispatcher.</p><p>All coroutines builders like launch and async accept an optional CoroutineContext parameter that can be used to explicitly specify the dispatcher for new coroutine and other context elements. So, launch(Dispatchers.Default) { ... } uses the same dispatcher as GlobalScope.launch { ... }.</p><h4>Job</h4><p>“Conceptually, a job is a cancellable thing with a life-cycle that destroys on its completion. It can be arranged into parent-child hierarchies where cancellation of the parent leads to an immediate cancellation of all its children. Failure or cancellation of a child with an exception other than CancellationException immediately cancels its parent. This way, a parent can cancel its own children (including all their children recursively) without canceling itself.”<br>We can make a job non-cancellable using NonCancellable . Also, we can make a coroutine to cancel itself after timeout using withTimeout function.</p><blockquote>Recently, I have started a series which covers MVP &amp; Architecture Components in Kotlin with basic examples <strong>without any libraries </strong>to give you the exact idea of these patterns.</blockquote><p><a href="https://proandroiddev.com/best-architecture-for-android-mvi-livedata-viewmodel-71a3a5ac7ee3">Best Architecture For Android : MVI + LiveData + ViewModel = ❤️</a></p><h4>Actors</h4><p>An actor is a combination of a coroutine state, behavior and a channel to communicate with other coroutines. Actor processes stream of messages. It can also be used for tasks that should not be performed concurrently. This is useful when we do not want to respond to all back-to-back (unnecessary) clicks by a user on a button but only first or recent click.</p><h4>Channels</h4><p>Channel is a non-blocking primitive for communication between sender and receiver. It has suspending operations instead of blocking ones. It provide a way to transfer a stream of values between coroutines. <em>Channels are (till mid of October ’18) an </em><strong><em>experimental</em></strong><em> feature of </em><em>kotlinx.coroutines.</em></p><h4>Structured Concurrency</h4><p>In general programming, threads are global by default but coroutines are always related to some local <em>scope</em> like a UI element with well-defined life-cycle. And we should launch coroutines in the specific scope of the operation we are performing.</p><h4>Android</h4><p>In Android, we need a coroutine dispatcher context that restricts the coroutine execution to main UI thread. This can be achived by adding Android specific <a href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/kotlinx-coroutines-android"><strong>kotlinx-coroutines-android</strong></a> library dependency along with core library mentioned above. It provides <strong>Dispatchers.Main</strong> context for Android applications.</p><blockquote>I hope, I was able to give you some basic idea of coroutines. If you think I have missed something or something need to be improved/corrected, please comment, I would do necessary changes.</blockquote><p>Also, check out<strong> Linkaive</strong> App, an app to save news, articles, and all useful links for later. One of the best app for users who like to have a collection of their favorite links. I am redesigning this app using Kotlin and Architecture Components.</p><p><a href="https://play.google.com/store/apps/details?id=com.rohitss.saveme">Linkaive: Save For Later - Apps on Google Play</a></p><p>Link to <a href="https://android.jlelse.eu/add-stable-coroutines-to-android-kotlin-project-731e3d9d96e3"><strong>Add Stable Coroutines to Android-Kotlin Project</strong></a><strong> </strong>article.</p><h4>References &amp; Further Readings</h4><ol><li>Official coroutines guide: <a href="https://kotlinlang.org/docs/reference/coroutines-overview.html">https://kotlinlang.org/docs/reference/coroutines-overview.html</a></li><li>Exploring Coroutines in Kotlin: <a href="https://www.youtube.com/watch?v=jT2gHPQ4Z1Q">https://www.youtube.com/watch?v=jT2gHPQ4Z1Q</a></li><li>kotlinx.coroutine library: <a href="https://github.com/Kotlin/kotlinx.coroutines">https://github.com/Kotlin/</a><a href="https://github.com/Kotlin/kotlinx.coroutines">kotlinx.coroutines</a></li><li>Core primitives to work with coroutines: <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/">https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/</a></li><li>Guide to UI programming: <a href="https://github.com/kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md">https://github.com/kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md</a></li><li>Structured Concurrency: <a href="https://medium.com/@elizarov/structured-concurrency-722d765aa952">https://medium.com/@elizarov/structured-concurrency-722d765aa952</a></li></ol><p>Thanks for reading!<br><strong>Rohit Surwase</strong></p><p>If you liked the article, clap clap clap 👏👏👏 as many times as you can.</p><p><strong>LIKED SO MUCH!</strong> Medium allows up to <strong>50 claps</strong>. <br>Also, post a <strong>Tweet on </strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a>.</p><p>Let’s be the friends on <a href="http://in.linkedin.com/in/rohitss5"><strong><em>LinkedIn</em></strong></a>, <a href="http://medium.com/@rohitss"><strong><em>Medium</em></strong></a><strong><em>, </em></strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a><strong> </strong>and <a href="http://github.com/RohitSurwase/"><strong><em>GitHub</em></strong></a><strong><em>.</em></strong></p><p>Few of my other articles:</p><ul><li><a href="https://psiloveyou.xyz/introverts-decoded-why-am-i-still-single-a046d1596d1e">Introverts Decoded: Unspoken Dilemma While In Love</a></li><li><a href="https://psiloveyou.xyz/introverts-decoded-by-rohit-surwase-edc36c8fbbe5">Introverts Decoded</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b4242bd1b2a4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/android-news/coroutines-basic-terminologies-and-usage-b4242bd1b2a4">Android Kotlin Coroutines: Basic Terminologies &amp; Usage</a> was originally published in <a href="https://medium.com/android-news">AndroidPub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android App Architectures: Example of MVP with Kotlin]]></title>
            <link>https://medium.com/hackernoon/https-medium-com-rohitss-android-app-architectures-mvp-with-kotlin-f255b236010a?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/f255b236010a</guid>
            <category><![CDATA[kotlin-mvp]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android-development]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-app-architecture]]></category>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Wed, 22 Aug 2018 19:05:07 GMT</pubDate>
            <atom:updated>2020-03-29T01:45:48.410Z</atom:updated>
            <content:encoded><![CDATA[<h4>How MVP works and its simple example showing implementation in Kotlin</h4><blockquote><strong>TL;DR</strong><br>Practically it is not feasible to summaries the MVP pattern here in few lines. But I can assure you that every single line of the following article worth your time. I bet it will give you enough understanding that you can implement it on your own. And still, if you are in a hurry I would suggest jumping at “<strong>How does MVP works?</strong>” section. <a href="https://github.com/RohitSurwase/Android-Architectures-Kotlin/tree/master/News-Kotlin-MVP"><strong>Source code of the</strong> <strong>example</strong></a><strong>.</strong></blockquote><p>Whenever any Android developer starts thinking of improving technical skills, the first thing that comes to his or her mind is the Architecture of the App. Not to mention that they already know the pain involved in maintaining the standard code-base after it grows.</p><p>I did the same and learned about architectures and later chosen to focus on MVP. After trying it with test projects and later production apps, I moved to learn Kotlin language, and now the latest Architecture Components. But during this transition, I realized that there are very few to none; examples showcasing the implementation of these concepts in Kotlin. So, here I am with these examples showing the implementation of some architectural patterns in Kotlin. I promise you that these examples are gonna be so simple that you can grasp the core of these concepts with ease.</p><p>In this series I will target two main architectural patterns: Model -View-Presenter &amp; Android Architecture Components.</p><blockquote><a href="https://hackernoon.com/https-medium-com-rohitss-android-app-architectures-mvp-with-kotlin-f255b236010a">1. Android App Architectures: MVP Example With Kotlin (This)</a><br>2̵.̵ ̵A̵n̵d̵r̵o̵i̵d̵ ̵A̵r̵c̵h̵i̵t̵e̵c̵t̵u̵r̵e̵ ̵C̵o̵m̵p̵o̵n̵e̵n̵t̵s̵ ̵E̵x̵a̵m̵p̵l̵e̵ ̵W̵i̵t̵h̵ ̵K̵o̵t̵l̵i̵n̵<br>3̵.̵ ̵A̵n̵d̵r̵o̵i̵d̵ ̵A̵r̵c̵h̵i̵t̵e̵c̵t̵u̵r̵e̵ ̵C̵o̵m̵p̵o̵n̵e̵n̵t̵s̵:̵ ̵A̵d̵v̵a̵n̵c̵e̵m̵e̵n̵t̵s̵<br><a href="https://proandroiddev.com/best-architecture-for-android-mvi-livedata-viewmodel-71a3a5ac7ee3"><strong>4. Best Architecture For Android : MVI + LiveData + ViewModel = ❤️</strong></a></blockquote><p><strong>Model-View-Presenter </strong>(<strong>MVP</strong>)<strong> </strong>with<strong> Kotlin</strong></p><p>While designing the architecture of any software the basic principle to keep in mind is the<strong> separation on concerns.</strong> MVP is one of the best pattern to separate presentation layer from the business logic. Moreover MVP also helps you easily implement more advanced, complete architecture patterns like Clean Architecture.</p><p>Here I am not gonna answer questions like WHAT<strong> </strong>and WHY but <strong>HOW</strong>.<br>How to use MVP? How does it work? and How to implement it using Kotlin? <br>So in this 1st article we will see the implementation of MVP using Kotlin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MOy8GojDSVYKmED6stX5sQ.png" /></figure><p>View</p><p><strong>Who</strong>: Combination Activity/Fragment/View and its contract i.e. interface.<br><strong>Purpose</strong>: Do all UI related stuff and all that needs Android context.<br><strong>Job</strong>: Whenever it needs to do some action, it should trigger respective function of Presenter using object. It should not have any business logic.</p><p>Though the XML layout is part of the view, it is not alone called as View. All necessary functions are added to our Activity/Fragment/View via contract/interface. Adapters are also the part of the View. Activity/Fragment/View directly initializes Presenter including Model indirectly as Presenter initializes Model in its constructor.</p><p>Example tasks:<br><em>- Show/hide progress bar.<br>- Request data to be shown and later show received data/error.<br>- Get </em>LayoutInflater<em>, Resources, Android System Services.</em></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b08875f67b86a4c1cf97ce0329a5b602/href">https://medium.com/media/b08875f67b86a4c1cf97ce0329a5b602/href</a></iframe><p>Presenter</p><p><strong>Who</strong>: It is a separate class which should not have Android context.<br><strong>Purpose</strong>: Act as a mediator between two main layers of the architecture View and Model.<br><strong>Job</strong>: Trigger respective function of Model depending on the request made by View.</p><p>Presenter has the objects of View and Model. It should not have any business logic. Moreover, it should not have direct access to any UI elements.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/79f906aad7a57fe70be0ff4f136dc548/href">https://medium.com/media/79f906aad7a57fe70be0ff4f136dc548/href</a></iframe><p>Model</p><p><strong>Who</strong>: Database, models (schema), API calls and all other business logic is part of this layer. This is where all business logic will be located.<br><strong>Purpose</strong>: To separate business logic so we can test it in isolation without View. This also helps us to reuse code-base across platforms.<br><strong>Job</strong>: Get/calculate data or error to shown on the UI.</p><p>This is where we should make API, Database calls, and other calculations.<br>But to make it more solid, we can add one more layer inside Model:</p><p><strong>Repository</strong>: <br>Decides the source of the data to be shown depending on the predefined conditions. It means it will get the data from offline or online sources depending on the conditions like network availability.<br>(To keep things simple, the included app does not have repository layer.)</p><p><strong>This way our Model will get the data to be shown irrespective of the network conditions.</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a836f093ab0ed427b21cc499207bab70/href">https://medium.com/media/a836f093ab0ed427b21cc499207bab70/href</a></iframe><blockquote>Recently, I have published an article on Coroutines, check out-</blockquote><p><a href="https://android.jlelse.eu/coroutines-basic-terminologies-and-usage-b4242bd1b2a4">Android Kotlin Coroutines: Basic Terminologies &amp; Usage</a></p><h4>How does MVP works?</h4><p>In brief words: View calls functions of Presenter, Presenter transfers it to Model, Model requests the data to be shown. After Model gets the data, it passes it to Presenter and Presenter passes it to View where View shows that data on the screen.</p><p><strong>Direct</strong> Communication of:<br>View ==&gt; Presenter ==&gt; Model</p><p><strong>Indirect</strong> Communication using contract/interface:<br>Model =/=&gt; Presenter =/=&gt; View</p><p>Here is the simple workflow diagram of MVP in Android. I request all to spend a few minutes with the following illustration, it will help you a lot to understand MVP in Android. <br>Note: Model is referred to as <strong>Interactor</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1001/1*5SedT0fi85LohkIkNI2gbA.png" /></figure><p>I hope I was clear enough to make this understandable.<br>Here is the complete <a href="https://github.com/RohitSurwase/Android-Architectures-Kotlin/tree/master/News-Kotlin-MVP"><strong>source code of the</strong> <strong>example</strong></a>.</p><p><a href="https://github.com/RohitSurwase/Android-Architectures-Kotlin">RohitSurwase/Android-Architectures-Kotlin</a></p><p>Thanks for reading!<br><strong>Rohit Surwase</strong></p><p>If you liked the article, clap clap clap 👏👏👏 as many times as you can.</p><p><strong>LIKED SO MUCH!</strong> Medium allows up to <strong>50 claps</strong>. <br>Also post a <strong>Tweet on </strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a>.</p><p>Let’s be the friends on <a href="http://in.linkedin.com/in/rohitss5"><strong><em>LinkedIn</em></strong></a>, <a href="http://medium.com/@rohitss"><strong><em>Medium</em></strong></a><strong><em>, </em></strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a><strong> </strong>and <a href="http://github.com/RohitSurwase/"><strong><em>GitHub</em></strong></a><strong><em>.</em></strong></p><h4>Also, check out few other stories published by me:</h4><ul><li><a href="https://psiloveyou.xyz/introverts-decoded-why-am-i-still-single-a046d1596d1e">Introverts Decoded: Unspoken Dilemma While In Love</a></li><li><a href="https://psiloveyou.xyz/introverts-decoded-by-rohit-surwase-edc36c8fbbe5">Introverts Decoded</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f255b236010a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/https-medium-com-rohitss-android-app-architectures-mvp-with-kotlin-f255b236010a">Android App Architectures: Example of MVP with Kotlin</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android RecyclerView onItemClickListener & getAdapterPosition(): A Better Way]]></title>
            <link>https://medium.com/hackernoon/android-recyclerview-onitemclicklistener-getadapterposition-a-better-way-3c789baab4db?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/3c789baab4db</guid>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[tips-and-tricks]]></category>
            <category><![CDATA[android-development]]></category>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Fri, 17 Aug 2018 01:46:09 GMT</pubDate>
            <atom:updated>2020-03-29T01:48:53.230Z</atom:updated>
            <content:encoded><![CDATA[<h4>A lighter way to get that thing done smoothly.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xAFAiAxqZVrOVLBTo9tf6w.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/photos/xrVDYZRGdw4?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Émile Perron</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p><strong>Update 1</strong>: Included lambda functions as an alternative.<br><strong>Update 2</strong>: Included a link to <a href="https://github.com/RohitSurwase/RvClickListenerExample"><strong>Implementation example App on GitHub</strong></a>.</p><blockquote><strong>TL;DR<br></strong>Instead of creating a custom ItemClickListener <strong>interface</strong> to getAdapterPosition() inside your Activity/Fragment/View and end up creating <strong>multiple Anonymous Classes under the hood for each item</strong>. Better pass the RecyclerView.<strong>ViewHolder</strong> using view.<strong>setTag()</strong> inside your ViewHolder and then <strong>getAdapterPosition()</strong> (…and many other things) from view.<strong>getTag()</strong> i.e. <strong>ViewHolder’s </strong>object inside your Activity/Fragment/View.<br><a href="https://github.com/RohitSurwase/RvClickListenerExample"><strong>Implementation example App on GitHub</strong></a></blockquote><h3><strong>The Most Popular Way; </strong>The Problem</h3><p>I know you must have already found a way to get onItemClickListener() for your RecyclerView Adapter. But sometimes the way we think is not always the correct or a better way. Whenever anyone tries to find a way to getAdapterPosition() inside their Activity/Fragment/View. He or she comes across the well-known pattern of creating a custom interface with <strong><em>view</em></strong> and <strong><em>position</em></strong> as the parameters.</p><pre>public <strong>interface ItemClickListener</strong> {<br>    void onItemClick(View view, int position);<br>}</pre><p>Later, we initialize the ItemClickListener instance using the public setter.</p><pre>private<strong> ItemClickListener </strong>onItemClickListener;</pre><pre>...</pre><pre>public void setItemClickListener(<strong>ItemClickListener</strong> clickListener) {<br>    onItemClickListener = clickListener;<br>}</pre><p>OR… by passing the ItemClickListener instance via constructor.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d04f5e386b99fb58feae9904c6138ff4/href">https://medium.com/media/d04f5e386b99fb58feae9904c6138ff4/href</a></iframe><p>And finally we pass our <strong><em>view</em></strong><em> </em>and <strong><em>adapter position</em></strong> to the listener.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5b7d4e262c63aa6326bc09761f67d7ec/href">https://medium.com/media/5b7d4e262c63aa6326bc09761f67d7ec/href</a></iframe><p>And we know the rest of the part very well.<br><strong>Most of the articles out there depict the same thing</strong>. But there is a problem.</p><p>When we say <em>itemView</em><strong><em>.setOnClickListener(new View.OnClickListener()…</em></strong></p><blockquote><strong>It creates an Anonymous inner class under the hood which implements our ItemClickListener listener.</strong></blockquote><pre><strong>class</strong> AnonymousItemClickListener <strong>implements ItemClickListener</strong>{<br>    @Override<br>    public void onItemClick(View view, int position) {<br>        <br>    }<br>}</pre><p>And we end up creating and initializing multiple Anonymous inner classes under the hood just for a click listener.</p><pre>new AnonymousItemClickListener(){<br>        @Override<br>        public void onItemClick(View view, int position) {<br>            <br>        }<br>    };</pre><p>I used to do the same. Because that is what we get to see all over the internet.</p><p><a href="https://psiloveyou.xyz/introverts-decoded-why-am-i-still-single-a046d1596d1e">Introverts Decoded: Unspoken Dilemma While In Love</a></p><blockquote><strong>Update:</strong><br>As suggested by <a href="https://medium.com/u/f02e354ffc0c">Sachin Chandil</a> in the comments section, we can use Java8’s <strong>Lambda functions</strong> to get rid of anonymous class creation at compile time. Lambda functions work with SAM types (interfaces with Single Abstract Method) and the reason it does not create anonymous inner class is the InvokeDynamic instruction, introduced in Java 7.<br>So with Lambdas, we can convert above function as below:</blockquote><pre>itemView.setOnClickListener(view -&gt; onItemClickListener.onItemClick(view, getAdapterPosition());</pre><h4>And for some reasons you can not use Java8’s Lambda functions then go ahead as following way is still a better option for you.</h4><h3>A Better Way; The Solution</h3><p>But, recently I got a suggestion from a <a href="https://medium.com/@aalap_shah"><strong>colleague</strong></a> to implement it in a better and lighter way. Let’s see how <em>we can get </em><strong><em>adapter position,</em></strong><em> </em><strong><em>item id</em></strong> (…and many other things)<em> inside our Activity/Fragment/View.</em></p><p>So, instead of creating new custom interface, we will use <strong>View.OnClickListener </strong>and initialize it in the way we used to do.</p><pre>private<strong> View.OnClickListener </strong>onItemClickListener;</pre><pre>...</pre><pre>public void setItemClickListener(<strong>View.OnClickListener</strong> clickListener) {<br>    onItemClickListener = clickListener;<br>}</pre><p>And we will pass the <em>instance </em>of <strong><em>ViewHolder </em></strong>via <strong>itemView</strong> to the listener using <strong>setTag()</strong>.</p><pre>private class MyViewHolder extends RecyclerView.ViewHolder {<br>    public MyViewHolder(View itemView) {<br>        super(itemView);<br>        <strong>itemView.setTag(this);</strong><br>    }<br>}</pre><p>In this way, we do not need to create Anonymous inner class as we can directly give the reference of <strong>onItemClickListener</strong></p><pre>private class MyViewHolder extends RecyclerView.ViewHolder {<br>    public MyViewHolder(View itemView) {<br>        super(itemView);<br>        itemView.setTag(this);<br>        <strong>itemView.setOnClickListener(onItemClickListener);</strong><br>    }<br>}</pre><p>Finally we can get <strong><em>adapter position</em></strong><em> and </em><strong><em>item id </em></strong>(…and many other things)<em> inside our Activity/Fragment/View from the </em><strong><em>ViewHolder</em></strong><em> instance.</em></p><pre>RecyclerView.ViewHolder <strong>viewHolder</strong> = (RecyclerView.ViewHolder) <strong>view.getTag()</strong>;</pre><pre>int <strong>position</strong> = viewHolder.<strong>getAdapterPosition()</strong>;<br>// viewHolder.<strong>getItemId()</strong>;<br>// viewHolder.<strong>getItemViewType()</strong>;<br>// viewHolder.<strong>itemView</strong>;</pre><p><strong>Simple!</strong> Isn’t it? <br>Let me know your views and suggestions about this in the comment section.</p><p>If you still have some doubts, here is the source code of the <a href="https://github.com/RohitSurwase/RvClickListenerExample"><strong>Implementation example App on GitHub</strong></a><strong>.</strong></p><p>Thanks for reading!<br><strong>Rohit Surwase</strong></p><p>If you like the article, clap clap clap 👏👏👏 as many times as you can.</p><p><strong>LIKED SO MUCH!</strong> Medium allows up to <strong>50 claps</strong>. <br>Also post a <strong>Tweet on </strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a>.</p><p>Let’s be the friends on <a href="http://in.linkedin.com/in/rohitss5"><strong><em>LinkedIn</em></strong></a>, <a href="http://medium.com/@rohitss"><strong><em>Medium</em></strong></a><strong><em>, </em></strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a><strong> </strong>and <a href="http://github.com/RohitSurwase/"><strong><em>GitHub</em></strong></a><strong><em>.</em></strong></p><blockquote>Latest stories by me</blockquote><p><a href="https://proandroiddev.com/best-architecture-for-android-mvi-livedata-viewmodel-71a3a5ac7ee3">Best Architecture For Android : MVI + LiveData + ViewModel = ❤️</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3c789baab4db" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/android-recyclerview-onitemclicklistener-getadapterposition-a-better-way-3c789baab4db">Android RecyclerView onItemClickListener &amp; getAdapterPosition(): A Better Way</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introverts Decoded: Unspoken Dilemma While In Love]]></title>
            <link>https://medium.com/p-s-i-love-you/introverts-decoded-why-am-i-still-single-a046d1596d1e?source=rss-6d6a65c4e6fa------2</link>
            <guid isPermaLink="false">https://medium.com/p/a046d1596d1e</guid>
            <category><![CDATA[love]]></category>
            <category><![CDATA[psychology]]></category>
            <category><![CDATA[human-behavior]]></category>
            <category><![CDATA[people]]></category>
            <category><![CDATA[introvert]]></category>
            <dc:creator><![CDATA[Rohit Surwase]]></dc:creator>
            <pubDate>Sun, 24 Jun 2018 18:19:02 GMT</pubDate>
            <atom:updated>2018-10-16T04:54:11.338Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ITvbUq939Vmvk8MGeROC2w.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/photos/vXymirxr5ac?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Anthony Tran</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><h4>Things they never tell anyone else.</h4><blockquote>Updated title of the article from “Introverts Decoded: Why am I still single?”</blockquote><p><em>LOVE</em>, love is a romantic yet a scary word for many introverts. For introverts, it is not easy to fall in love. Love takes away so many things from them, more importantly, solitude. In the first article of <a href="https://medium.com/@rohitss/introverts-decoded-by-rohit-surwase-edc36c8fbbe5"><strong><em>Introverts Decoded</em></strong></a>, we have seen how different they are from many others. We have seen they are loving and caring by heart.<br>Then, why are they still single? Why are they scared of falling in love? How could not everyone see their inner beauty?<br>Let’s see if we can find the answer.</p><p><a href="https://psiloveyou.xyz/introverts-decoded-by-rohit-surwase-edc36c8fbbe5">Introverts Decoded</a></p><p>We know, we all are carried in two different directions by our heart and mind. The problem with introverts is that their mind is much stronger than their heart. Though they have one loving and caring heart but always dominated by their ruthless mind. This is why they are different and this is why they are introverts.</p><p>A battle starts within them-self whenever they fall in love with someone. A never-ending battle until any one of it wins. The battle is between their heart and mind. The heart strives for its love and the mind strives for its priorities, both are already firm in their fields. If heart wins then the love, the <em>one</em> also becomes the priority and if mind wins the heart has to sacrifice the love. But why does this happen?</p><p>Introverts work in a strange way. The work they do, the time they spend is always organized by their priorities. This is why they never feel lonely, this is why they are always busy with something meaningful. Their loved ones always have the highest priority with all exceptions. But reaching to that level is really difficult for anyone. It seems rare that they approach someone so instantly though they can sense any person in the first interaction. They wait until they pass through all stages of their mind. Like- Is it lust? Is it obsession? or Is it just their thoughts? Once they are assured that they actually like the <em>one</em> their heart starts making moves. And this is where the problem begins.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*dxzTqqzCdlrxvYTV9LA1GQ.jpeg" /><figcaption>HEART of introvert is hijacked by MIND. (Original Source: <a href="https://78.media.tumblr.com/tumblr_maoiaqeFfa1rnm4f6o1_500.jpg">tumblr.com</a>)</figcaption></figure><p>Wanna See! How does it go inside? Let’s see the conversation between heart ❤ and mind of an introvert.</p><blockquote><strong><em>Heart</em></strong> ❤: Let’s plan something, so we can spend some time together. Let’s call her out.<br><strong><em>Mind</em></strong>: Oh! Come on bro! She is not interested in you.<br><strong><em>Heart</em></strong>❤: Yeah! But why would anyone be interested in me if I never show up?<br><strong><em>Mind</em></strong>: Don’t you have anything to do? Get back to work.<br><strong><em>Heart</em></strong>❤: But I like her.<br><strong><em>Mind</em></strong>: I know! But aren’t we both happier together?<br><strong><em>Heart</em></strong>❤: Yes! We are… but sometimes I need someone, the one.<br><strong><em>Mind</em></strong>: But you already have so many. Do not open up yourself until you are sure.</blockquote><p>And it goes on…</p><blockquote>Somehow the heart ❤ succeeds in calling her out, the mind settles for a while.<br>Now if she says YES!, the mind will allow the heart ❤ to continue. And if she says NO!, then the end of the story before it begins. Next time mind will come up with thousands of reasons to not to try again.</blockquote><p>Here is the difference between others and introverts. Others try several times while introverts’ heart ❤ can try only fewer times. They will choose to lose the <strong><em>one</em></strong> than to run behind him or her. Harsh but the truth!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/250/1*V6WK00X6A5Oi0JkkJ6ZyEw.gif" /><figcaption>Heart vs Mind</figcaption></figure><p><em>The stronger my heart tries to make a move closer to the one, the harder my mind tries to control and even suppress my feelings. <br>My poor heart.</em></p><p>Whoever they are it is just because of the combination of their loving heart and commanding mind. The mind regulates priorities to the <em>things</em> and the <em>people</em> which helps them to focus on fewer but important stuff. This enables their loving heart to spread whatever it has among those very close to it.</p><p>The tragedy is, no-one will get to see their inner beauty unless he or she gets on their priority list. You need to show real interest in them, to know them. But, you would ask: how could anyone be interested in anyone without knowing anything about him or her? It is possible, it is possible because you already got intuitions from him or her, you just need to trust in those. Snap-judgments which tells you that he or she is not exactly same as everyone knows, he or she has something different hidden inside. Remember! They are very good at catching lies, observing people, so never try that way. Once you are able to open the locked doors of their heart, they are all yours.</p><blockquote>“If you are ever lucky enough to find a weirdo, never let them go.” -Matthew Gray Gubler</blockquote><p>It is not like, introverts do not fall in love but it is the fact that the <strong><em>one</em></strong> has to try harder. His or her love must be compelling enough to set their heart free from their mind, to get himself or herself on their priority list.</p><p>If you like the article, clap clap clap 👏👏👏 as many times as you can. It inspires me to come up with more beautiful articles.<br>LIKED! Share on Facebook, LinkedIn, WhatsApp.</p><p><strong>LIKED SO MUCH!</strong> Medium allows up to <strong>50 claps</strong>. <br>Also post a <strong>Tweet on </strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a>.</p><p>About:<strong> Rohit Surwase</strong>,<br>Techy by Birth. Writer by Hobby. Observer by Nature. Unpredictable by Character. Android Developer (Google Certified) by Profession.</p><p>Let’s be the friends on <a href="http://in.linkedin.com/in/rohitss5"><strong><em>LinkedIn</em></strong></a>, <a href="http://medium.com/@rohitss"><strong><em>Medium</em></strong></a><strong><em>, </em></strong><a href="https://twitter.com/Rohit5rss"><strong>Twitter</strong></a><strong> </strong>and <a href="http://github.com/RohitSurwase/"><strong><em>GitHub</em></strong></a><strong><em>.</em></strong></p><p>Previous article of this series: <a href="https://medium.com/@rohitss/introverts-decoded-by-rohit-surwase-edc36c8fbbe5"><strong>Introverts Decoded</strong></a></p><p><a href="https://psiloveyou.xyz/introverts-decoded-by-rohit-surwase-edc36c8fbbe5">Introverts Decoded</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a046d1596d1e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/p-s-i-love-you/introverts-decoded-why-am-i-still-single-a046d1596d1e">Introverts Decoded: Unspoken Dilemma While In Love</a> was originally published in <a href="https://medium.com/p-s-i-love-you">P.S. I Love You</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>