<?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 MobileDev - NK on Medium]]></title>
        <description><![CDATA[Stories by MobileDev - NK on Medium]]></description>
        <link>https://medium.com/@n20?source=rss-9e0ca594ea55------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*KVueZ1rO3DyG3QcgyQXLKg.jpeg</url>
            <title>Stories by MobileDev - NK on Medium</title>
            <link>https://medium.com/@n20?source=rss-9e0ca594ea55------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 06:41:43 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@n20/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[targetSdk = 36 — Yes, This Can Break Your App]]></title>
            <link>https://medium.com/@n20/targetsdk-36-yes-this-can-break-your-app-3a69bd9809d6?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/3a69bd9809d6</guid>
            <category><![CDATA[android-developers]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[androiddev]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Tue, 20 Jan 2026 12:57:17 GMT</pubDate>
            <atom:updated>2026-01-20T12:57:57.831Z</atom:updated>
            <content:encoded><![CDATA[<h3>targetSdk = 36 — Not just a Number Change</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ycOIdZhSDkFwdRwGpBumsA.png" /></figure><p>So you did this:</p><pre>targetSdk = 36</pre><p>Build passed. <br>App opened.<br>Before you celebrate — <strong>pause</strong>.</p><p>Android 16 changes rules, and <strong>this is the checklist you should go through before releasing</strong>.</p><h3>1. Check If Your App Breaks on Big Screens</h3><p>Android 16 is serious about tablets and foldables.</p><p><strong>Check this</strong></p><ul><li>Does your app stretch weirdly?</li><li>Any UI overlapping?</li><li>Locked orientation ignored?</li></ul><p><strong>What to do</strong></p><ul><li>Use flexible layouts</li><li>Test on tablet/foldable emulator</li><li>Fix screens that assume phone size</li></ul><p>If it looks bad, Android 16 won’t hide it anymore.</p><h3>2. Check Back Navigation Everywhere</h3><p>Back navigation changes are one of the most common surprises.</p><p><strong>Check this</strong></p><ul><li>Back button behavior on every screen</li><li>Swipe-back gesture</li><li>Dialog and bottom-sheet dismissal</li></ul><p><strong>Fix</strong></p><ul><li>Remove custom back logic if not needed</li><li>Use OnBackPressedDispatcher</li><li>Let system handle back when possible</li></ul><p>If back feels wrong, users notice instantly.</p><h3>3. Re-Test All Permission Flows</h3><p>Permissions often break silently.</p><p><strong>Check this</strong></p><ul><li>Photo access</li><li>Location usage</li><li>Local network access</li></ul><p><strong>What to do</strong></p><ul><li>Test on fresh install</li><li>Don’t assume old permissions still work</li><li>Remove permissions you don’t need</li></ul><p>Clean permission flow = fewer Play Store issues.</p><h3>4. Review Your AndroidManifest</h3><p>Target 36 makes the system less forgiving.</p><p><strong>Check this</strong></p><ul><li>exported components</li><li>Unused permissions</li><li>Old intent filters</li></ul><p><strong>Tip</strong><br>If something is not required, remove it.<br>Android 16 rewards clean manifests.</p><h3>5. Look for Deprecated Stuff</h3><p>Old code finally starts hurting.</p><p><strong>Check</strong></p><ul><li>Deprecated layout attributes</li><li>Old UI flags</li><li>Legacy APIs</li></ul><p>If Android warns you — don’t ignore it this time 😄</p><h3>6. Test App Update Flow (Not Just Fresh Install)</h3><p>Many bugs appear only during updates.</p><p><strong>Check</strong></p><ul><li>App update from older version</li><li>Data migration</li><li>Feature flags</li></ul><p>Fresh install working doesn’t mean upgrade works.</p><h3>7. Check Notifications &amp; Background Work</h3><p>Background rules keep getting tighter.</p><p><strong>Check</strong></p><ul><li>Notifications showing correctly</li><li>Long-running tasks</li><li>WorkManager / foreground services</li></ul><p>If something stops in background, targetSdk is usually the reason.</p><h3>8. Optional: Look at What’s New</h3><p>Not required, but useful:</p><ul><li>Progress-based notifications</li><li>Better haptics</li><li>Performance improvements</li></ul><p>Use only what makes sense for your app.</p><h3>Final Thought</h3><p>Setting:</p><pre>targetSdk = 36</pre><p>is easy.</p><p><strong>Making sure your app behaves well after that is the real work.</strong></p><p>Run this checklist once, and Android 16 won’t surprise you later 😉</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3a69bd9809d6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding Kotlin Coroutines: Lightweight Threads Explained]]></title>
            <link>https://medium.com/@n20/understanding-kotlin-coroutines-lightweight-threads-explained-aeb754217dbd?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/aeb754217dbd</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[kotlin-coroutines]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Tue, 02 Sep 2025 14:51:11 GMT</pubDate>
            <atom:updated>2025-09-02T15:54:19.661Z</atom:updated>
            <content:encoded><![CDATA[<p>Coroutines are often called <strong>lightweight threads</strong>, and there’s a reason for that. In Java, if we want any task to run, we create a <strong>thread</strong> or use the <strong>main thread</strong>. Each process has at least one thread, called the <strong>main thread</strong>.</p><p>If we want to run a task <strong>in parallel</strong>, we create a new thread that runs separately from the main thread. For the main thread, there’s a <strong>Looper</strong> and a <strong>message queue</strong>. So, any task we perform on the main thread gets added to the queue and is then gets handover to main thread using Looper.</p><p>Now, if we run a <strong>long-running task on the main thread</strong>, it will be blocked. So, we move such tasks to a <strong>background thread</strong>.</p><h3>Threads in Java vs Coroutines</h3><p>In Java, there’s a limit to how many threads we can create per process. If a thread has a task that is <strong>waiting</strong> for some data from the network or is paused, it can’t do another task; for that, a new thread must be created.</p><p>In <strong>coroutines</strong>, however, when a task is waiting, <strong>another coroutine can use that same thread first coroutine was using</strong>. Once the first task is ready, it resumes on the same thread. Because coroutines <strong>run on threads</strong> and allow thread reuse, they are called <strong>lightweight</strong>.</p><p>Creating a thread in Java takes around <strong>2 MB</strong> of memory. Coroutines are cheaper because multiple coroutines can share the same thread.</p><p>The term “<strong>coroutine</strong>” comes from <strong>cooperative routines</strong> — tasks that cooperate with each other. Kotlin developers built this framework to efficiently manage concurrency using threads.</p><p>For example:</p><blockquote><em>If a thread has </em><strong><em>task1</em></strong><em>, which is waiting for a network response, and </em><strong><em>task2</em></strong><em> comes in, instead of creating a new thread, the existing thread is reused. If no free thread is available, a new one is taken from the thread pool.</em></blockquote><h3>Coroutine Basics: Context, Scope, and Dispatcher</h3><ol><li><strong>Scope</strong> defines the <strong>lifetime</strong> of a coroutine — which scope it’s attached to, e.g., MainScope, CoroutineScope, GlobalScope, viewModelScop, lifecycleScope.</li><li><strong>Context</strong> defines the <strong>thread</strong> the coroutine will use, e.g., Dispatchers.IO, Dispatchers.Main, or Dispatchers.Default.</li></ol><p>To create a custom coroutine:</p><pre>CoroutineScope(Dispatchers.IO).launch {<br>    // your task here<br>}</pre><h3>Suspend Functions</h3><p>The suspend keyword indicates that this particular task can be <strong>suspended</strong> at any point. That means it can <strong>wait</strong> for output, or it can pause so that other tasks can do their work using yield().</p><h3>Launch vs Async</h3><p>These are methods used to run tasks in a coroutine:</p><ul><li><strong>launch</strong> returns a <strong>Job</strong>, which we can use to start, join, or cancel the task. It is <strong>fire-and-forget</strong>.</li><li><strong>async</strong> returns a <strong>Deferred&lt;T&gt;</strong>, which we can use to get the result of the task. We use await() to wait for the result.</li></ul><p>In launch, we can use join() to wait for the task to finish.</p><h3>Switching Context with withContext</h3><p>Using the withContext suspend function, we can <strong>switch the context</strong> of a running coroutine. For example, one part can run on one thread, and another part can run on a different thread:</p><pre>scope.launch{<br>  withContext(Dispatchers.IO) {<br>      // network operation<br>  }<br>  withContext(Dispatchers.Main) {<br>      // update UI, after network operation is done<br>  }<br>}</pre><h3>RunBlocking</h3><p>runBlocking is used to run code in a coroutine <strong>and block the current thread</strong> until it completes:</p><pre>fun main() = runBlocking {<br>    launch {<br>        delay(1000L)<br>        println(&quot;World&quot;)<br>    }<br>    println(&quot;Hello&quot;)<br>}</pre><p>In this example, “Hello” prints first, then “World” prints after the coroutine finishes.</p><p>Coroutines provide an <strong>efficient, lightweight way to handle concurrency</strong> in Kotlin, reusing threads, avoiding heavy memory overhead, and simplifying asynchronous programming. By understanding scopes, contexts, dispatchers, suspend functions, and the difference between launch and async, we can harness the full power of coroutines in Kotlin.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=aeb754217dbd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android’s Move to 16 KB Memory Pages: What It Means for Developers]]></title>
            <link>https://medium.com/@n20/androids-move-to-16-kb-memory-pages-what-it-means-for-developers-c966adcf7698?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/c966adcf7698</guid>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-apps]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android-development]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Mon, 01 Sep 2025 16:17:35 GMT</pubDate>
            <atom:updated>2025-09-01T18:55:06.561Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_tu0KEhrI-xy2BjT4EDTFg.png" /></figure><p>Google has quietly introduced one of the most impactful low-level changes in Android in years: <strong>support for 16 KB memory pages</strong>. Starting with <strong>Android 15</strong>, devices can ship with larger memory pages, and by <strong>November 2025</strong>, all apps on Google Play targeting Android 15+ must support them.</p><p>This change isn’t just a technical curiosity — it directly translates into <strong>faster app launches, better battery usage, and quicker camera/system boot times</strong>. Let’s break down what’s happening, why it matters, and what developers need to do.</p><h3>🔹 What Are Memory Pages?</h3><p>Modern operating systems don’t manage RAM byte by byte. Instead, they split memory into fixed-size chunks called <strong>pages</strong>.</p><ul><li>On most systems, the page size has been <strong>4 KB (4096 bytes)</strong>.</li><li>With this change, Android now supports <strong>16 KB pages (16,384 bytes)</strong>.</li></ul><p>When an app requests memory, the OS allocates it in units of pages. Virtual memory addresses are mapped to physical addresses via these pages.</p><p>👉 <strong>Key takeaway:</strong> On Android 15+, memory can now be managed in <strong>bigger chunks (16 KB each)</strong> instead of 4 KB.</p><h3>🔹 Why Does Page Size Matter?</h3><p>Page size affects performance at multiple levels:</p><ol><li><strong>Page faults</strong> — If a memory page isn’t loaded, the OS must fetch it. More/smaller pages = more faults.</li><li><strong>Page tables</strong> — Each page needs an entry. More pages = bigger tables and more CPU overhead.</li><li><strong>TLB (Translation Lookaside Buffer)</strong> — CPU cache for page mappings. Fewer pages = fewer entries = fewer misses.</li></ol><p>Smaller pages reduce wasted space but increase overhead. Larger pages reduce overhead but risk more internal fragmentation. Android’s move to <strong>16 KB</strong> is a sweet spot for modern workloads.</p><h3>👉 <strong>This optimization only impacts native code (NDK)</strong></h3><p>the parts of your app written in <strong>C or C++</strong> and compiled into .so libraries.<br>If your app is entirely in <strong>Java or Kotlin</strong>, you don’t need to worry — there’s <strong>no impact</strong>.</p><h3>When You Need to Care</h3><p>You should pay attention if your app:</p><ul><li>Uses NDK directly with C/C++ code.</li><li>Depends on third-party SDKs that include .so libraries (native code).</li></ul><h3>What You Should Do</h3><p>To make sure your app runs smoothly across devices (both <strong>4 KB</strong> and <strong>16 KB</strong> page sizes):</p><ul><li>✅ Use the <strong>latest NDK</strong> version that supports 16 KB pages.</li><li>✅ If you rely on prebuilt native SDKs, <strong>update them</strong> to versions that support 16 KB pages.</li><li>✅ Don’t hardcode the page size. Instead, use sysconf(_SC_PAGESIZE) to fetch it dynamically at runtime.</li><li>✅ Always <strong>test on both 4 KB and 16 KB devices</strong> to confirm stability and performance.</li></ul><h3>🔹 Performance Gains: How 16 KB Pages Help</h3><h3>1. Faster App Launches</h3><p>When an app starts, the system loads lots of code and libraries. With 16 KB pages:</p><ul><li>Fewer page faults</li><li>Fewer page table entries</li><li>Less kernel overhead</li></ul><p>👉 Google measured up to <strong>30% faster launches (3.16% average)</strong>.</p><h3>2. Better Battery Usage</h3><ul><li>Fewer TLB misses → less CPU time wasted</li><li>Smaller page tables → lighter kernel work</li><li>Bigger chunks from storage → more efficient I/O</li></ul><p>👉 Saves <strong>4–6% battery</strong> in common scenarios.</p><h3>3. Quicker Camera &amp; System Boot</h3><ul><li><strong>Camera</strong>: Big native libraries (image processing, ML models) load faster.</li><li><strong>Boot</strong>: Fewer page faults across system services.</li></ul><p>👉 Results in <strong>~0.8s faster boot</strong> and noticeably quicker camera startup.</p><h3>🔹 Example: 4 KB vs 16 KB Pages</h3><p>Let’s say we load a <strong>64 KB library</strong>.</p><p><strong>With 4 KB pages:</strong></p><ul><li>Needs 64 ÷ 4 = 16 pages</li><li>16 page faults, 16 entries, 16 TLB slots</li></ul><p>[Page1][Page2][Page3]…[Page16]</p><p><strong>With 16 KB pages:</strong></p><ul><li>Needs 64 ÷ 16 = 4 pages</li><li>4 page faults, 4 entries, 4 TLB slots</li></ul><p>[Page1……………][Page2……………][Page3……………][Page4……………]</p><p>👉 Fewer pages = less overhead = faster.</p><h3>🔹 Developer Impact</h3><h3>1. Native Code Compatibility</h3><p>If you use the <strong>NDK (C/C++ libraries)</strong>, your binaries must support 16 KB alignment.</p><ul><li>Use <strong>NDK r28+</strong> and <strong>AGP 8.5.1+</strong> (enabled by default)</li><li>Or add linker flags manually: -Wl,-z,max-page-size=16384</li><li>Avoid hardcoding PAGE_SIZE == 4096. Instead use: <strong>sysconf(_SC_PAGESIZE);</strong></li></ul><h3>2. Testing Tools</h3><ul><li><strong>Android Studio</strong>: Emulators with 16 KB page size (ARM64 + x86_64)</li><li><strong>Pixel Devices</strong>: Toggle in Developer Options (Android 15 QPR1+)</li><li><strong>Samsung Remote Test Lab</strong>: Cloud devices with 16 KB enabled</li></ul><h3>3. Play Store Deadline</h3><ul><li>From <strong>Nov 1, 2025</strong>, <strong>all new apps and updates</strong> targeting Android 15+ must support 16 KB pages.</li></ul><h3>🔹 Why Not Go Even Bigger (64 KB, 2 MB)?</h3><ul><li><strong>64 KB / 2 MB</strong> pages → even fewer faults, but much higher internal fragmentation.</li><li>Many Android apps allocate small objects; wasting 60 KB every time would hurt memory efficiency.</li><li><strong>16 KB is a balance</strong>: fewer faults without too much waste.</li></ul><h3>✅ Takeaway</h3><p>The move to <strong>16 KB memory pages</strong> is one of those under-the-hood changes that end users will feel without ever knowing why:</p><ul><li><strong>Apps launch faster</strong></li><li><strong>Battery lasts longer</strong></li><li><strong>Cameras and devices boot quicker</strong></li></ul><p>For developers, the message is clear:</p><ul><li>Update your toolchains</li><li>Test on 16 KB devices/emulators</li><li>Don’t assume 4 KB pages anymore</li></ul><p>Android is evolving for performance, and 16 KB pages are a big step in that journey.</p><p>💡 <em>If you’re an Android developer working with native code, now’s the time to future-proof your apps for the 16 KB era.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c966adcf7698" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[EncryptedSharedPreferences is Deprecated — What Should Android Developers Use Now?]]></title>
            <link>https://medium.com/@n20/encryptedsharedpreferences-is-deprecated-what-should-android-developers-use-now-7476140e8347?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/7476140e8347</guid>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Fri, 29 Aug 2025 14:19:54 GMT</pubDate>
            <atom:updated>2025-08-29T14:19:54.579Z</atom:updated>
            <content:encoded><![CDATA[<h3>EncryptedSharedPreferences is Deprecated — What Should Android Developers Use Now?</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KUUWDOfOGkJz-8_lVydb1Q.png" /></figure><p>For years, <strong>EncryptedSharedPreferences (ESP)</strong> was the go-to solution for securely storing small pieces of sensitive data (tokens, user IDs, session keys) on Android. It provided an easy API, handled key management behind the scenes, and used AES encryption under the hood.</p><p>But if you’ve checked the AndroidX release notes recently, you might have noticed:</p><p>👉 EncryptedSharedPreferences (and its sibling EncryptedFile) are now <strong>deprecated</strong> in the latest <strong>Security Crypto</strong>library.</p><p>So what does that mean for your app? And what should you use instead? Let’s break it down.</p><h3>🚨 Why is EncryptedSharedPreferences Deprecated?</h3><ul><li>Google is moving away from <strong>SharedPreferences</strong> in general.</li><li>DataStore is now the recommended replacement for preferences and key-value storage.</li><li>For encryption, newer libraries like <strong>App Security</strong> (Crypto 1.1.0+) give more flexible, future-proof primitives.</li></ul><p>ESP was convenient, but it had limitations:</p><ul><li>Heavy reliance on legacy SharedPreferences APIs.</li><li>Limited flexibility for customizing cryptography.</li><li>Not the best choice for modern coroutine/Flow-based apps.</li></ul><h3>✅ What to Use Instead</h3><h3>1. Jetpack DataStore (Recommended)</h3><p>Google now recommends <strong>DataStore</strong> over SharedPreferences.</p><ul><li>Uses Kotlin coroutines + Flow.</li><li>Type-safe (with Proto DataStore).</li><li>Async and non-blocking.</li></ul><p>➡️ But DataStore by itself is <strong>not encrypted</strong>. If you’re storing sensitive data, you need to encrypt values before writing.</p><h3>2. Tink (Google’s Cryptography Library)</h3><p><a href="https://github.com/google/tink?utm_source=chatgpt.com">Tink</a> is a cross-platform, production-ready cryptography library from Google.</p><ul><li>Supports AES-GCM, AES-SIV, and more.</li><li>Built-in key rotation.</li><li>Easy integration with Android’s KeyStore.</li></ul><p>You can combine Tink + DataStore:</p><pre>val keysetHandle = AndroidKeysetManager.Builder()<br>    .withSharedPref(context, &quot;master_keyset&quot;, &quot;my_pref&quot;)<br>    .withKeyTemplate(AesGcmKeyManager.aes256GcmTemplate())<br>    .withMasterKeyUri(&quot;android-keystore://master_key&quot;)<br>    .build()<br>    .keysetHandle</pre><pre>val aead = keysetHandle.getPrimitive(Aead::class.java)<br>val ciphertext = aead.encrypt(&quot;my_secret&quot;.toByteArray(), null)<br>val plaintext = aead.decrypt(ciphertext, null)</pre><h3>3. Encrypted DataStore (Custom Implementation)</h3><p>Since there’s no official EncryptedDataStore (yet), you can build a wrapper:</p><ul><li>Use <strong>Tink</strong> or <strong>Crypto</strong> for encryption.</li><li>Store values in PreferencesDataStore.</li><li>Expose them as safe getters/setters.</li></ul><p>Example wrapper:</p><pre>class SecureStorage(private val context: Context) {<br><br>    private val Context.dataStore by preferencesDataStore(&quot;secure_store&quot;)<br><br>    private val KEY_ACCESS_TOKEN = stringPreferencesKey(&quot;access_token&quot;)<br><br>    suspend fun saveToken(token: String) {<br>        context.dataStore.edit { prefs -&gt;<br>            prefs[KEY_ACCESS_TOKEN] = encrypt(token)<br>            // Same normal data store but save with Encryption enabled<br>        }<br>    }<br><br>    // Retrove the value after decryption<br>    val accessToken: Flow&lt;String?&gt; = context.dataStore.data<br>        .map { prefs -&gt; prefs[KEY_ACCESS_TOKEN]?.let { decrypt(it) } }<br><br>}</pre><h3>⚖ Which One Should You Pick?</h3><ul><li><strong>For new apps:</strong> Use <strong>DataStore + Tink/Encryption</strong>.</li><li><strong>For existing ESP apps:</strong> Don’t panic. ESP still works, but migration is wise if you’re future-proofing.</li><li><strong>For enterprise/finance apps:</strong> Stick with Tink — it’s maintained by Google Security engineers.</li></ul><h3>Takeaway</h3><p>EncryptedSharedPreferences served us well, but it’s time to move forward.<br>The <strong>future of secure local storage on Android</strong> is:</p><p>👉 <strong>DataStore for persistence</strong><br>👉 <strong>Tink (or Security Crypto) for encryption</strong><br>👉 <strong>Android Keystore for key protection</strong></p><p>By combining these, you get a modern, coroutine-friendly, and secure solution that will be supported long-term.</p><p>💡 Pro tip: If you’re migrating from ESP → DataStore, consider writing a small migration layer that reads old ESP values, decrypts them, and saves them into your new DataStore. That way, users don’t lose their tokens or session.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7476140e8347" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ Android 16 quietly disables portrait lock on large screens — and why that’s actually good news]]></title>
            <link>https://medium.com/@n20/android-16-quietly-disables-portrait-lock-on-large-screens-and-why-thats-actually-good-news-a616dd6a2d1e?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/a616dd6a2d1e</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-16]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Fri, 25 Jul 2025 22:29:08 GMT</pubDate>
            <atom:updated>2025-07-25T22:38:34.639Z</atom:updated>
            <content:encoded><![CDATA[<h3>📱 Android 16 quietly disables portrait lock on large screens — and why that’s actually good news</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YpY0PugxaBU-R29mZCjMfg.png" /></figure><p>Android 16 is around the corner, and Google just slipped in a change that might break a few assumptions:</p><blockquote><strong><em>Your app can no longer force portrait mode on large-screen devices by default.</em></strong></blockquote><p>Yep. Tablets, foldables, Chromebooks — they’re done being treated like oversized phones.<br>Let’s talk about why Google did this, what it means, and what else is coming soon (spoiler: Play Store targeting deadlines).</p><h3>🤦 Why this happened</h3><p>Locking apps to portrait on big screens often leads to awkward UX:</p><ul><li>Users dock their tablet or attach a keyboard — and your app stubbornly sticks to portrait.</li><li>Huge black bars appear, turning your UI into a tiny island in the middle.</li><li>Foldables and Chromebooks feel especially broken.</li></ul><p>Bottom line: users expect large screens to respect how they hold (or fold) the device. And Google wants us to build for that.</p><h3>🧰 What to do instead</h3><p>✅ Build responsive layouts using ConstraintLayout or Jetpack Compose.<br>✅ Use WindowSizeClass or check orientation to adapt content dynamically.<br>✅ Handle onConfigurationChanged properly — or better yet, let the system re-create the activity.<br>✅ If your app <em>really</em> needs portrait (e.g., camera, AR), explain why in your Play Console declarations.</p><p>Quick Compose idea:</p><pre>val config = LocalConfiguration.current<br>if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {<br>    // show vertical layout<br>} else {<br>    // show horizontal or two-pane layout<br>}</pre><h3>😂 What not to do</h3><ul><li>Write hacks to force orientation — the system will quietly ignore them.</li><li>Assume tablets are rare — they’re not, and foldables are everywhere now.</li></ul><h3>📦 Play Store deadlines you can’t ignore</h3><p>Besides portrait lock changes, here’s what’s coming:</p><p><strong>August 31, 2025:</strong></p><ul><li>New apps &amp; app updates must target <strong>API level 35 (Android 15)</strong>.</li><li><strong>Existing apps</strong> that already target API 34 (Android 14):</li><li>Can stay on the store and remain discoverable — even on Android 15 devices — unless Google updates the policy.</li></ul><p>So if you’re stuck on older SDKs, it’s time to plan an upgrade.</p><h3>🗓 And Android 16?</h3><p>Right now, it’s in beta.<br>Google’s pattern suggests: around <strong>August–September 2026</strong>, targeting API 36 (Android 16) will become mandatory for new apps &amp; updates.</p><h3>🏆 Why this is actually great</h3><ul><li>Your app will finally look natural on tablets, foldables, and Chromebooks.</li><li>Better UX → happier users → better reviews.</li><li>Staying up to date keeps Google happy (and keeps your app visible).</li></ul><h3>✅ TL;DR</h3><ul><li>Android 16 disables forced portrait lock on big screens → go responsive.</li><li>By Aug 31, 2025: new apps &amp; updates must target Android 15.</li><li>Existing apps on API 34 can remain, for now.</li><li>Android 16 target likely required around Aug–Sep 2026.</li></ul><p>It’s a good time to future-proof your layouts — your users (and your Play Store rating) will thank you.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a616dd6a2d1e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why DSA Though? I Just Wanted to Build Apps]]></title>
            <link>https://medium.com/@n20/why-dsa-though-i-just-wanted-to-build-apps-818e0c0c5e66?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/818e0c0c5e66</guid>
            <category><![CDATA[dsa-and-algorithm]]></category>
            <category><![CDATA[interview-preparation]]></category>
            <category><![CDATA[sad]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Thu, 05 Jun 2025 21:32:47 GMT</pubDate>
            <atom:updated>2025-06-05T21:32:47.686Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oin1B9xm84kjgJzpohbOSA.png" /></figure><p>Let’s be real.</p><p>You joined tech thinking you’ll build cool apps, maybe even the next big startup. But suddenly you’re stuck dry-running a binary search tree on a whiteboard, with an interviewer staring like you’re defusing a bomb.<br><strong>Why??</strong></p><p>Isn’t app development mostly about layouts, APIs, and fixing that one annoying crash that only happens on Android 9?</p><p>Yes.<br><strong>But here’s the catch — DSA is not just about code. It’s about <em>you</em>.</strong></p><h3>DSA ≠ Just Algorithms. It’s Mindset + How You React When Stuff Breaks</h3><p>When you’re given a problem in interviews, they’re not just looking for a working solution.<br>They’re watching:</p><ul><li>Do you panic when it doesn’t work on the first try?</li><li>Do you go quiet or talk through it?</li><li>Do you ask smart questions?</li><li>And honestly… do you give up or keep trying?</li></ul><p>It’s like a <em>live personality test with code</em>.</p><h3>Anyone Can Code. Few Can Think Clearly</h3><p>Let’s be honest — with enough Stack Overflow and ChatGPT, anyone can build a to-do app.<br>But when a real bug hits production and it’s not in any tutorial — what do you do?</p><p>That’s where DSA prep helps. It trains your brain to:</p><ul><li>Break problems down</li><li>Spot patterns</li><li>Handle chaos without throwing your laptop</li></ul><p>It’s like mental CrossFit. Painful at first. But later, when a bug hits on Friday at 6 PM? You’re calm. You’ve <em>seen worse</em>(hello, dynamic programming).</p><h3>Bonus Points: It Shows How You Communicate</h3><p>Interviewers don’t just want the right answer.<br>They want to see <strong>how you think</strong> — especially under pressure.</p><p>If you can explain your thoughts clearly, adapt when they add new constraints, and stay chill when stuck — congrats, you’re already ahead of 80% of the crowd.</p><h3>TL;DR</h3><p><strong>DSA isn’t about building apps. It’s about building <em>you</em>.</strong></p><p>You’re not just proving you can code — you’re showing:</p><ul><li>You can solve tough problems</li><li>You stay composed under pressure</li><li>You think, explain, and learn fast</li></ul><p>The problem is just an excuse. <strong>You’re the real test. </strong>nd don’t forget to include these rounds as an interviewer.</p><p>#HappyCoding</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=818e0c0c5e66" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI vs ML vs Deep Learning: An Easy Guide]]></title>
            <link>https://medium.com/@n20/ai-vs-ml-vs-deep-learning-an-easy-guide-47046ed98c11?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/47046ed98c11</guid>
            <category><![CDATA[chatgpt]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[aiml]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Tue, 25 Feb 2025 22:37:17 GMT</pubDate>
            <atom:updated>2025-02-25T22:37:17.250Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*R6bVMIJnlxHSZ8fY5iYaHg.png" /></figure><h3>AI, ML, and Deep Learning — What’s the Deal?</h3><p>Let’s start with a simple fact: <strong>Artificial Intelligence (AI) is the boss</strong>, and <strong>Machine Learning (ML) is its hardworking employee</strong>. AI is the broad concept of making machines smart, while ML is a specific way AI learns from data. If AI were a school, ML would be the student doing all the homework (and complaining about it).</p><p>But wait, there’s more! <strong>ML itself has multiple techniques</strong>, and one of them is <strong>Deep Learning (DL)</strong>. Deep Learning is a specialized method within ML that uses <strong>Neural Networks</strong>, but ML can also learn using other methods like decision trees, support vector machines, and statistical models.</p><h3>The Learning Secret: Data, Data, and More Data</h3><p>ML is like a child learning to recognize objects. If you don’t show it pictures of cats and dogs, it won’t know which is which. No data = No learning. Simple as that.</p><p>In ML, data is usually split into three parts:</p><ul><li><strong>70% Training Data</strong> (The School Phase) — The algorithm learns from this.</li><li><strong>15% Validation Data</strong> (The Test) — It checks if learning is on track.</li><li><strong>15% Test Data</strong> (The Final Exam) — It evaluates if ML actually understood or just memorized everything.</li></ul><h3>Deep Learning: One of ML’s Powerful Tools</h3><p>Deep Learning is one of the most advanced methods used in ML, but it’s not the only way ML can work. Traditional ML techniques often rely on structured data and predefined rules, whereas Deep Learning uses multi-layered neural networks to automatically extract patterns from raw data.</p><h3>Neural Networks — Not Real Neurons!</h3><p>Neural networks sound fancy, but they’re just mathematical functions, not actual biological neurons. They mimic how our brains process information by passing data through layers of artificial neurons, which apply <strong>weights and biases</strong> to make decisions. If you want to go deeper into this topic, you can explore concepts like backpropagation and gradient descent separately.</p><h3>Types of Learning</h3><p>Deep Learning can learn in different ways:</p><ol><li><strong>Supervised Learning</strong> — The teacher provides answers (labeled data). Example: Feeding an AI thousands of pictures of cats and dogs, labeled accordingly.</li><li><strong>Unsupervised Learning</strong> — No teacher, just raw data. The AI finds patterns by itself (kinda like figuring out office politics). Example: Grouping customer types in a business.</li><li><strong>Reinforcement Learning</strong> — The trial-and-error method. The AI gets rewards for good behavior, like training a dog with treats. Example: AI learning to play chess by practicing against itself.</li></ol><h3>The Accuracy Game: Loss Functions and Optimizations</h3><p>In deep learning, we aim for accuracy, but mistakes happen. The <strong>loss function</strong> tells us how wrong our model is. It’s also known as <strong>cost function</strong> or <strong>error function</strong> (because it’s costly to have errors!).</p><p>Think of a loss function as a teacher grading an exam. If you get all answers wrong, your loss is high. If you get them right, your loss is low. The AI adjusts itself to minimize this loss, improving accuracy over time.</p><p>The loss function is calculated in such a way that it minimizes the error in the learning data. When we provide the expected output, the model checks its generated output against it and determines how much error it has made. This feedback helps the model learn and improve in future iterations.</p><h3>Example: AI Learning to Recognize Cats</h3><ol><li>You give an AI a dataset of 10,000 cat pictures.</li><li>It trains, but at first, it thinks some dogs are cats (big mistake!).</li><li>The loss function tells it, “Nope, that’s not a cat!”</li><li>It adjusts its model and tries again. It can try the same data multiple times to reduce the error — this repetition is called an <strong>epoch</strong>, and the number of epochs is set manually by the creator.</li><li>Over time, the AI becomes a cat-recognition master!</li></ol><h3>How ChatGPT Works — A Real-World Example</h3><p>Ever wondered how ChatGPT gives you answers so quickly? Here’s what happens internally when you type a query:</p><ol><li><strong>Input Processing</strong> — Your text is converted into numerical representations (tokens) so the model understands it.</li><li><strong>Neural Network Magic</strong> — The input is passed through multiple layers of a transformer model, which analyzes context, meaning, and past patterns.</li><li><strong>Prediction &amp; Probability</strong> — The model predicts the next words based on learned patterns and assigns probabilities to possible answers.</li><li><strong>Loss Function Check</strong> — It evaluates how accurate its predictions are based on past training data.</li><li><strong>Refinement &amp; Output</strong> — The final response is generated, refined, and sent to you.</li></ol><p>It’s like having a super-fast librarian who scans millions of books in a second to find the best answer!</p><h3>Wrapping It Up</h3><p>AI is the big picture, ML is the student learning from data, and Deep Learning is one of the advanced techniques ML uses to learn. ML also has other methods, and the right approach depends on the problem being solved.</p><p>And remember, without data, <strong>AI is just a clueless machine</strong>, like a student who never studied before an exam!</p><p>Hope this made AI/ML a little more fun for you! 🚀</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=47046ed98c11" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Integrating Transifex in Your Android App for Seamless Localization]]></title>
            <link>https://medium.com/@n20/integrating-transifex-in-your-android-app-for-seamless-localization-b47e3ce7adf3?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/b47e3ce7adf3</guid>
            <category><![CDATA[transifex-android]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[transifex]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Tue, 04 Feb 2025 13:12:01 GMT</pubDate>
            <atom:updated>2025-02-04T13:12:21.609Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ua6nMIxHYhGGDK5OmOtRBw.png" /></figure><p>Localization is essential for reaching a global audience, and Transifex makes it easier to manage translations efficiently. In this guide, we’ll walk through integrating Transifex into your Android project, handling runtime language changes, and ensuring seamless string retrieval.</p><h3>Initializing Transifex</h3><p>First, we define a global variable to manage language state across the app:</p><pre>object LanguageManager {<br>    var langState: LocaleState? = null<br>}</pre><p>This langState variable allows dynamic language switching at runtime.</p><h3>Setting Up Transifex in Application Class</h3><p>To initialize Transifex, add the following method in your Application class:</p><pre>private fun initTransifex() {<br>    try {<br>        val preferredLanguage = &quot;en&quot; // Change to &quot;es&quot;, &quot;ja&quot;, &quot;de&quot; as needed<br>        <br>        LanguageManager.langState = LocaleState(<br>            TxNative.wrap(this),<br>            &quot;en&quot;, // Default locale (usually English)<br>            arrayOf(&quot;en&quot;, &quot;es&quot;, &quot;ja&quot;, &quot;de&quot;), // Supported locales<br>            Locale(preferredLanguage) // Initial locale<br>        )<br>        TxNative.init(<br>            TxNative.wrap(this),<br>            LanguageManager.langState!!,<br>            BuildConfig.TRANSIFEX_TOKEN,<br>            null,<br>            null,<br>            null<br>        )<br>        <br>        TxNative.fetchTranslations(preferredLanguage, null)<br>        LanguageManager.langState?.setCurrentLocale(Locale(preferredLanguage))<br>        TxNative.fetchTranslations(null, null)<br>    } catch (e: Exception) {<br>        Timber.e(&quot;Transifex Throwable %s&quot;, e.message)<br>    }<br>}</pre><p>Now, call this method inside the onCreate() method of your Application class:</p><pre>override fun onCreate() {<br>    super.onCreate()<br>    initTransifex()<br>}</pre><h3>Changing Language at Runtime</h3><p>To change the language dynamically from anywhere in your app, call:</p><pre>LanguageManager.langState?.setCurrentLocale(Locale(&quot;es&quot;)) // Spanish</pre><p>This will instantly update the app’s language to Spanish.</p><h3>Handling Strings Properly</h3><p>When using Transifex, make sure to wrap string retrievals using TxNative.wrap(). Instead of:</p><pre>context.getString(R.string.string_id)<br>stringResource(R.string.string_id)</pre><p>Use:</p><pre>TxNative.wrap(LocalContext.current).getString(R.string.string_id)</pre><p>This ensures that Transifex applies the correct translations based on the active locale.</p><h3>Uploading Strings to Transifex</h3><p>Once you’ve updated your string files, upload them to Transifex using the following command in the terminal:</p><pre>java -jar &lt;/path/to&gt;/transifex.jar push -t &lt;token&gt; -s &lt;secret&gt; -m app -v</pre><h3>Downloading Transifex CLI</h3><p>Download the Transifex CLI tool (transifex.jar) from <a href="https://developers.transifex.com/docs/android-sdk#transifex-command-line-tool">here</a> and replace &lt;/path/to&gt; in the above command with its actual location.</p><h3>Conclusion</h3><p>Integrating Transifex into your Android app enables efficient localization with minimal effort. By following the steps above, you can seamlessly manage translations, change languages dynamically, and keep your app user-friendly for a global audience.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b47e3ce7adf3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding Coroutines in Kotlin: A Beginner’s Guide with a Tricky Twist]]></title>
            <link>https://medium.com/@n20/understanding-coroutines-in-kotlin-a-beginners-guide-with-a-tricky-twist-41c6656f8a3c?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/41c6656f8a3c</guid>
            <category><![CDATA[kotlin-coroutines]]></category>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[coroutine]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Tue, 03 Dec 2024 14:31:05 GMT</pubDate>
            <atom:updated>2024-12-03T14:31:05.617Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FnlR3VZO4wvcIuZehbavAA.png" /></figure><p>So, you’ve just heard about coroutines in Kotlin, and everyone around you is hyping them up. “They’re lightweight!” “They simplify asynchronous programming!” “They make your coffee!” (Okay, maybe not that last one… yet.) But what exactly are coroutines? And how do they work under the hood? Grab your cup of coffee (made without coroutines, sadly), and let’s dive in.</p><h3>🚀 What Are Coroutines?</h3><p>Imagine you’re at a party with two kinds of people:</p><ol><li><strong>Threads</strong>: These are your overly enthusiastic friends who need a lot of space and energy to function.</li><li><strong>Coroutines</strong>: These are the chill folks who get the job done without hogging all the snacks (or system resources).</li></ol><p>Coroutines are Kotlin’s way of handling asynchronous programming. They allow you to write non-blocking, concurrent code that looks like it’s sequential. This means you can perform tasks like network calls or database operations without freezing your app, and without the headache of callbacks.</p><h3>🌟 What Coroutines Offer Over Threads</h3><p>Let’s take a closer look at how coroutines outshine threads:</p><h3>1. Lightweightness</h3><ul><li>Threads are heavyweight because each one takes up a significant chunk of system memory (about 1MB per thread).</li><li>Coroutines, however, are extremely lightweight. You can create <strong>thousands of coroutines</strong> in the same memory space as a handful of threads.</li></ul><h3>2. Non-Blocking Code Execution</h3><ul><li>In threads, blocking calls (like Thread.sleep) block the entire thread.</li><li>Coroutines, on the other hand, can <strong>suspend</strong> their execution without blocking the thread. This allows other coroutines to run on the same thread.</li></ul><h3>3. Faster Context Switching</h3><p>Let’s dig into <strong>context switching</strong>, a term that often leaves developers scratching their heads.</p><h3>What Is Context Switching?</h3><p>Context switching is the process of saving the state of a running task (like a thread or coroutine) so that another task can run, and then restoring the saved state to resume the original task.</p><ul><li><strong>In Threads</strong>:<br>The operating system saves and restores the <em>entire thread state</em> — CPU registers, stack, etc. This is like changing into a completely new outfit, and it’s expensive.</li><li><strong>In Coroutines</strong>:<br>Only the coroutine’s execution state (like its stack frame) is saved in memory. Think of this as just putting on or taking off a hat — quick and efficient.</li></ul><h3>🧑‍🏫 Example of Context Switching and Thread Usage</h3><p>Let’s say you’re using coroutines to perform two tasks: downloading data and processing it.</p><h4>How Threads Work:</h4><p>When switching tasks, the OS might pause a thread running <strong>Task A</strong> and resume a different thread running <strong>Task B</strong>. This involves:</p><ol><li>Saving all the thread’s data (registers, stack).</li><li>Switching the CPU to the new thread.</li></ol><p>Threads have a one-to-one relationship with the CPU core, and switching between them is costly.</p><h4>How Coroutines Work:</h4><p>Coroutines run on a <strong>Dispatcher</strong> (e.g., Dispatchers.Default, Dispatchers.IO):</p><ol><li>When <strong>Task A</strong> (a coroutine) is suspended (e.g., waiting for network data), the thread is <strong>freed</strong> to run other coroutines.</li><li>When <strong>Task B</strong> resumes, it may run on the <strong>same thread</strong> or a <strong>different thread</strong>, depending on the dispatcher.</li></ol><h3>🧵 What Happens to Threads During Context Switch?</h3><p>Here’s the secret sauce of coroutines:</p><ul><li>When a coroutine is <strong>suspended</strong>, the thread it’s running on is freed up to perform other tasks.</li><li><strong>The thread doesn’t get blocked or wasted.</strong> This is why coroutines are so efficient compared to traditional thread management.</li></ul><h3>Example in Action:</h3><pre>fun main() = runBlocking {<br>    launch(Dispatchers.IO) { <br>        println(&quot;Downloading data on thread: ${Thread.currentThread().name}&quot;)<br>        delay(1000)  // Suspends coroutine, thread is freed<br>        println(&quot;Resuming download on thread: ${Thread.currentThread().name}&quot;)<br>    }<br><br>    launch(Dispatchers.Default) { <br>        println(&quot;Processing data on thread: ${Thread.currentThread().name}&quot;)<br>        delay(500)  // Suspends coroutine, thread is freed<br>        println(&quot;Resuming processing on thread: ${Thread.currentThread().name}&quot;)<br>    }<br>}</pre><p><strong>Output:</strong></p><pre>Downloading data on thread: DefaultDispatcher-worker-1<br>Processing data on thread: DefaultDispatcher-worker-2<br>Resuming processing on thread: DefaultDispatcher-worker-3<br>Resuming download on thread: DefaultDispatcher-worker-1</pre><p>Notice that the coroutine might resume on a <strong>different thread</strong> or the <strong>same one</strong>, depending on availability. This makes coroutines highly flexible and efficient.</p><h3>4. Structured Concurrency</h3><p>Coroutines provide <strong>structured concurrency</strong> — a mechanism to manage their lifecycle easily. When a coroutine scope ends, all its child coroutines are canceled automatically, preventing leaks and rogue tasks.</p><h3>5. Error Handling</h3><p>Coroutines offer <strong>exception transparency</strong>. If a coroutine fails, the parent coroutine can handle it, maintaining a clean and predictable error flow.</p><h3>🎭 Tricky Question: What Really Happens on Suspension?</h3><p>Let’s address a common question:</p><h3>What happens to the thread when delay or any suspend function is called?</h3><p>When a coroutine calls a suspend function:</p><ol><li>The coroutine <strong>pauses</strong> its execution and saves its state.</li><li>The <strong>thread is freed</strong> and can be used to run other tasks.</li><li>Once the coroutine resumes, it may:</li></ol><ul><li>Continue on the <strong>same thread</strong>.</li><li>Be assigned a <strong>different thread</strong>, depending on the dispatcher.</li></ul><h3>🎉 Wrapping It Up</h3><p>Coroutines are the secret weapon for writing clean, efficient, and readable asynchronous code in Kotlin. They offer lightweight context switching, structured concurrency, and excellent error handling, all while freeing up threads when they’re not needed.</p><p>So next time someone praises threads, you can say:<br><em>“Threads walk, but coroutines run — and they clean up after themselves too!”</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=41c6656f8a3c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android WorkManager: Your App’s Personal Assistant That Never Sleeps]]></title>
            <link>https://medium.com/@n20/android-workmanager-your-apps-personal-assistant-that-never-sleeps-1d277e157535?source=rss-9e0ca594ea55------2</link>
            <guid isPermaLink="false">https://medium.com/p/1d277e157535</guid>
            <category><![CDATA[workmanager]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[MobileDev - NK]]></dc:creator>
            <pubDate>Thu, 14 Nov 2024 19:30:19 GMT</pubDate>
            <atom:updated>2024-11-14T19:30:19.231Z</atom:updated>
            <content:encoded><![CDATA[<p>Picture this: You’re building an app, and you need it to sync data with a server. But wait! The user closes the app, their phone crashes, or they decide to put their device in airplane mode. Now your app’s important data sync never happens. Bummer, right?</p><p>That’s where <strong>Android WorkManager</strong> swoops in like a caped crusader, ensuring that your background tasks always get done, even when your app takes a nap or the phone restarts. Let’s break it down with a <strong>practical example</strong>.</p><h3>Meet the Scenario: The Never-Ending Sync</h3><p>Let’s say you’ve got a weather app, and it needs to sync weather data every 15 minutes so users get the latest forecast. But we all know that users aren’t always on the app, right? They may close it, or the phone may go into battery-saving mode, but the app still needs to get that data.</p><h3>How WorkManager Saves the Day</h3><ol><li><strong>The Request (AKA The “To-Do List” for WorkManager)</strong><br>First, you create a <strong>WorkRequest</strong>. This is like giving WorkManager a sticky note that says, “Hey, sync weather data every 15 minutes.” Now, WorkManager knows what needs to happen, and it’s ready to jump into action at the right time.</li><li><strong>The Worker (The Hard Worker)</strong><br>Enter the <strong>Worker</strong> class. This is the actual task-doer. Think of it as your friend who always shows up to help, no matter what. Inside the doWork() method, you’ll tell it exactly what to do—like making an HTTP request to fetch the weather data.</li></ol><pre>public class WeatherSyncWorker extends Worker {   <br>  @NonNull     <br>  @Override     <br>  public Result doWork() {         <br>        // Get the latest weather data here         <br>        fetchWeatherData();         <br>        return Result.success();     <br>  } <br>}</pre><ol><li><strong>Handling Conditions (Because Timing Is Everything)</strong><br>But here’s the thing: you don’t want the app syncing when the phone is on 5% battery or when it’s in a place with no Wi-Fi. So, you set <strong>constraints</strong>. Maybe you tell WorkManager: “Only sync when the phone is charging or connected to Wi-Fi.”</li></ol><pre>Constraints constraints = new Constraints.Builder()     <br>.setRequiresCharging(true)     <br>.setRequiredNetworkType(NetworkType.UNMETERED) <br>// Only Wi-Fi     .build();</pre><ol><li>Now, WorkManager only works when the conditions are perfect — like waiting for a sunny day to take your dog out for a walk.</li><li><strong>Reliability (Even If Your App Decides to Nap)</strong><br>The real magic happens here: WorkManager doesn’t just forget the task if something goes wrong. If the app is closed, the phone restarts, or the app crashes mid-task, WorkManager will try again. It’s like your task is armed with a parachute, always ready to land safely, no matter what happens. So even after a device reboot, your weather sync will eventually run.</li></ol><h3>Why You’ll Love WorkManager</h3><p>In real life, it’s like asking a friend to send your mom a birthday gift, but you give them strict instructions: <em>“Don’t do it unless you’re at a store that has gift wrap, and only when you’re not on a time crunch, and please, do it even if you lose your phone on the way there!”</em></p><ul><li><strong>Background Tasks?</strong> ✅</li><li><strong>Guaranteed Execution (Even on Reboots)?</strong> ✅</li><li><strong>Flexible Scheduling and Conditions?</strong> ✅</li></ul><p>In the end, WorkManager is your app’s trustworthy assistant who always makes sure things get done, no matter how many distractions (or crashes) happen along the way.</p><p>So, next time you need to schedule background work that should run reliably — whether it’s syncing data, sending notifications, or anything else — think of <strong>Android WorkManager</strong>. It’s the unsung hero in your app’s background story, and it’s <em>always</em> working, even when you don’t see it.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1d277e157535" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>