<?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:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Android Nomad]]></title><description><![CDATA[Reading club for Android Developers. Discover fresh insights, explore new ideas, and enhance your skills every Monday with a new post.]]></description><link>https://www.androidnomad.com</link><image><url>https://substackcdn.com/image/fetch/$s_!FQnk!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png</url><title>Android Nomad</title><link>https://www.androidnomad.com</link></image><generator>Substack</generator><lastBuildDate>Sun, 05 Apr 2026 12:43:07 GMT</lastBuildDate><atom:link href="https://www.androidnomad.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[SidoPillai]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[androidnomad@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[androidnomad@substack.com]]></itunes:email><itunes:name><![CDATA[Sido]]></itunes:name></itunes:owner><itunes:author><![CDATA[Sido]]></itunes:author><googleplay:owner><![CDATA[androidnomad@substack.com]]></googleplay:owner><googleplay:email><![CDATA[androidnomad@substack.com]]></googleplay:email><googleplay:author><![CDATA[Sido]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Android Nomad #63 - The Design Infra]]></title><description><![CDATA[Building a Foundation for Scalable Product Teams]]></description><link>https://www.androidnomad.com/p/android-nomad-63-the-design-infra</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-63-the-design-infra</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 21 Jul 2025 16:02:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQnk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today&#8217;s fast-paced product development world, seamless collaboration between design and engineering is crucial. In my previous <a href="https://androidnomad.substack.com/p/android-nomad-62-maximizing-design">post</a>, I covered how design and engineering teams can efficiently maneuver frictions. As product matures and teams grow, managing design tokens&#8212;the variables that define a product&#8217;s colors, typography, spacing, and more&#8212;becomes a core part of maintaining consistency and efficiency. This is where a robust &#8220;Design Infra&#8221; comes into play before implementing one into app&#8217;s codebase as discussed <a href="https://androidnomad.substack.com/i/152668668/building-a-material-color-scheme-with-tokens">here</a>.</p><h3>Why do we need this?</h3><ul><li><p><strong>Single Source of Truth</strong></p><p>A design system should provide a single, reliable source for all design decisions. As highlighted in ServiceTitan&#8217;s journey, the debate between making design files or code the source of truth is central. While design teams often prefer design files as the source, engineering teams lean toward code. The ideal is a system where both stay in sync, but this requires strong process and tooling.</p></li></ul><ul><li><p><strong>Proof of Work</strong></p><p>Every change&#8212;whether in design or code&#8212;should be traceable. ServiceTitan&#8217;s approach emphasizes the importance of version control and auditability, ensuring that updates are visible and can be rolled back if needed.</p></li></ul><ul><li><p><strong>Easy to Audit</strong></p><p>With a modular infrastructure, it&#8217;s easier to review, update, or even replace parts of the system (like third-party libraries or documentation tools) without disrupting the whole.</p></li></ul><h2>The Design-to-Code Pipeline</h2><p>Imagine multiple design teams&#8212;let&#8217;s call them Team A, Team B, and Team C&#8212;working on different features or platforms. Each team continuously iterates on design files, updating tokens as the product evolves. But how do these updates make their way into the codebase, ensuring both Android and iOS platforms stay in sync?</p><p>The clean way to manage this long-term is to maintain tokens as a separate entity, with methodical read and write rules. ServiceTitan&#8217;s experience shows that leveraging existing tools (like Figma, Sketch, or Abstract) and integrating them with code repositories can jumpstart adoption and reduce friction.</p><p><strong>Source of Truth: Design, Code, or Both?</strong></p><ul><li><p><strong>Design as Source of Truth:</strong><br>Fast edits, but risks fragmentation if changes aren&#8217;t communicated to engineering.</p></li><li><p><strong>Code as Source of Truth:</strong><br>Ensures what users see is always up to date, but can leave designers out of the loop.</p></li><li><p><strong>Two Sources of Truth:</strong><br>Possible, but requires exceptional communication and discipline to keep both in sync.</p><p></p></li></ul><p><a href="https://www.figma.com/community/plugin/1263743870981744253/design-tokens-manager">Design Token Manager </a>is a ready to use plugin in Figma that can be used to export tokens in a standardized JSON format that follows this <a href="https://www.designtokens.org/tr/drafts/format/">spec</a> which is widely adopted. There are many others, pick a mechanism that works the best. </p><p>Once you have them, version it as part of the manifest.json such that tokens are strictly tied to the version.</p><h2>Automated Validation and Distribution</h2><p>Once tokens are updated, a Continuous Integration (CI) process kicks in. The CI validates the new tokens, checking for errors or inconsistencies. If everything checks out, it automatically builds new platform-specific files for Android and iOS.</p><p>Developers then grab these token files from the repository. While the process may still involve manually copying the new tokens into the codebase for each platform, the path from design intent to implementation is clear and traceable.</p><h2>Documentation and the Design System</h2><p>Supporting this workflow is comprehensive documentation&#8212;a living design system. This documentation ensures that every stakeholder, from designers to developers, understands how tokens are structured, updated, and used across the product.</p><h2>Why Invest in Design Infra?</h2><p>A scalable design infrastructure bridges the gap between design and engineering. It reduces friction, minimizes inconsistencies, and accelerates the release of high-quality features. By investing in tools and processes that automate and document the flow of design tokens, teams can focus more on creativity and less on manual handoffs.</p><p></p><h2>Design Infra Workflow</h2><p>Here&#8217;s a visually representation of this post.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-EfF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-EfF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-EfF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-EfF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-EfF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-EfF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png" width="1024" height="1536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1536,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1886751,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://androidnomad.substack.com/i/168862244?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-EfF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!-EfF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!-EfF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!-EfF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff62bce88-c911-4e9d-bf4f-8c23bdac4b27_1024x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>In short, a well-oiled design infra isn&#8217;t just a nice-to-have&#8212;it&#8217;s essential for any team aiming for speed, quality, and cohesion in their product development lifecycle.</p><div><hr></div><h2>Join the Conversation</h2><p>How does your team manage the flow between design and engineering? Have you faced challenges with keeping your design tokens in sync, or found tools and workflows that really work? Share your experiences, questions, or lessons learned in the comments below&#8212;let&#8217;s learn from each other and keep improving our design infra together!</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #62 - Maximizing Design Briefings]]></title><description><![CDATA[A field guide for developers working with designers]]></description><link>https://www.androidnomad.com/p/android-nomad-62-maximizing-design</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-62-maximizing-design</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 14 Jul 2025 16:31:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQnk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Why Design Briefings Matter</h3><p>As Android developers, we&#8217;re often handed Figma files and told to &#8220;implement it.&#8221; But skipping the design briefing is a mistake. A design briefing isn&#8217;t just a handoff&#8212;it&#8217;s a <strong>collaboration checkpoint</strong> where goals, platform constraints, and intent are clarified. When done well, it reduces rework, aligns expectations, and ensures the final product feels intentional, not improvised.</p><h2>1.  Start With Shared Purpose</h2><p>Designers build experiences grounded in user empathy and business context. Developers give that experience form and function. That handoff moment is critical.</p><p><strong>Kick off every design handoff by asking:</strong></p><p>- What user problems are we solving?</p><p>- What are the key success metrics (e.g., retention, engagement)?</p><p>- What Android-specific or accessibility constraints were considered?</p><p>- What does &#8220;done&#8221; actually look like&#8212;across devices, states, and flows?</p><blockquote><p>Tip: Treat design briefings like a user story kickoff&#8212;understand the "why" before implementing the "what."</p></blockquote><h2>2. Translate Designs Into Buildable Specs</h2><p>Designs are visuals&#8212;but code needs <strong>precision</strong>. Don&#8217;t assume everything you need is in the mockups.</p><h3>Developers should clarify:</h3><ul><li><p><strong>Interactions:</strong> How do components behave? What about touch feedback, ripple effects, or gestures? Is it an adaptation of the existing <a href="https://androidnomad.substack.com/p/android-nomad-54-creating-a-scalable">design system</a>.</p></li><li><p><strong>Responsiveness:</strong> How do layouts adjust on tablets, foldables, or landscape mode?</p></li><li><p><strong>Edge cases:</strong> Long names, missing images, offline behavior, error states (Snackbar, Toast, etc.)</p></li><li><p><strong>Content flexibility:</strong> Can text wrap or truncate? How does localization affect layouts (RTL, long strings)?</p></li></ul><p>Tools like <strong>Figma</strong> and <strong>Zeplin</strong>, plus Android Studio&#8217;s Layout Inspector, help convert these conversations into specs.</p><h2>3. Collaborate in Loops, Not Lines</h2><p>Design isn&#8217;t a one-way street. The best outcomes happen when developers and designers loop continuously:</p><ul><li><p>Share live previews early, especially for animations and layout shifts</p></li><li><p>Push back on infeasible interactions with alternatives&#8212;not rejections</p></li><li><p>Keep communication async and lightweight (Figma comments, Slack threads)</p></li></ul><blockquote><p>Good teams hand off files. Great teams hand off <strong>context and collaboration.</strong></p></blockquote><h2>4. Common Pitfalls&#8212;and How to Avoid Them</h2><ul><li><p>Skipping nuanced states - Design all button states, form validations, modals</p></li><li><p>Guessing interactions - Clarify animations, gestures, loading and error flows</p></li><li><p>Platform assumptions - Android behaves differently from iOS&#8212;ask about platform conventions early</p></li><li><p>Visual drift - Sync on spacing, typography, theming, and Material guidelines</p></li></ul><p>Avoiding these pitfalls isn&#8217;t about perfection&#8212;it&#8217;s about *<em>predictability</em>*.</p><h2>5. Feature Flagging &amp; Phased Rollouts</h2><p>Features are rarely launched all at once. Android teams often use <strong>feature flags</strong> (e.g., Firebase Remote Config) for <a href="https://androidnomad.substack.com/i/166998197/phased-rollout">phased rollouts</a>.</p><p>This allows teams to:</p><ul><li><p>Mitigate risk</p></li><li><p>A/B test new features</p></li><li><p>Gather early feedback from Play Store beta channels</p></li><li><p>Gracefully scale rollout</p></li></ul><h3>Designers &amp; Developers Should Sync On:</h3><ul><li><p>What features are behind flags?</p></li><li><p>What does the UI look like when a flag is off (legacy vs. new screens)?</p></li><li><p>Will components change dynamically during rollout?</p></li><li><p>Are fallback states designed and tested on all supported Android versions?</p></li></ul><blockquote><p>Example: If a new checkout flow is enabled only for 10% of users, the UI must support <em>both</em> versions&#8212;and toggle between them cleanly.</p></blockquote><h3>Best Practices:</h3><ul><li><p>Design both &#8220;on&#8221; and &#8220;off&#8221; states explicitly.</p></li><li><p>Wrap flag logic in components early, so toggling is trivial.</p></li><li><p>Use staging environments, internal test tracks, and mock accounts to simulate rollouts.</p></li><li><p>Keep rollout plans visible for cross-team clarity.</p></li></ul><h2>6. Final Checklist for Design-Dev Harmony</h2><ul><li><p>Avoid implementing solutions in search of a problem</p></li><li><p>Capture hover states, gestures, accessibility details</p></li><li><p>Identify design elements that are hard to implement early</p></li><li><p>Know what&#8217;s gated by feature flags and design fallback UIs</p></li><li><p>Build feedback loops into the workflow</p></li></ul><h2>Final Thoughts</h2><p>A good design handoff isn&#8217;t just about <strong>mockups</strong>&#8212;it&#8217;s about <strong>intent</strong>. It&#8217;s about building <strong>shared understanding</strong> between people who solve problems in different ways. When developers and designers sync early, iterate often, and respect each other&#8217;s domain expertise, the result is more than just a beautiful UI&#8212;it&#8217;s a resilient, scalable, delightful product.</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #61 - Should You Really Support Your App on Every Form Factor?]]></title><description><![CDATA[Supporting Android Apps Across Form Factors: Why, When, and How]]></description><link>https://www.androidnomad.com/p/android-nomad-61-should-you-really</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-61-should-you-really</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 07 Jul 2025 23:20:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQnk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Given high adoption beyond phones i.e. tablets, smart tvs, watch, in-car systems, it may seem a natural to support for these form-factors. But that&#8217;s necessarily <strong>not</strong> the case even if there is a push from the framework to do so. </p><p>Supporting a wide range of devices sounds appealing in theory: your app reaches more users, and you can showcase versatility. However, the reality is that each form factor brings its own unique challenges. Tablets require thoughtful use of screen real estate and often call for multi-pane layouts. Smart TVs have remote-based navigation and expect a &#8220;lean back&#8221; experience, which is vastly different from touch-driven interfaces. In-car systems demand minimal distractions, voice-first interactions, and strict safety guidelines.</p><p>Before jumping into multi-device support, it&#8217;s crucial to ask: does your app genuinely benefit from being available on these platforms? For some apps&#8212;like media players, messaging, or navigation&#8212;the answer may be yes. For others, shoehorning the experience onto a TV or car screen can lead to a clunky, unsatisfying user experience.</p><p>There&#8217;s also the maintenance burden. Every new form factor means additional testing, UI adaptations, and a need to keep up with platform-specific guidelines. Fragmentation is real, and it&#8217;s easy to stretch your resources thin chasing every device class.</p><p></p><h3>How Do You Decide Which Form Factors to Support?</h3><p>The decision to expand your app beyond phones shouldn&#8217;t be made lightly. Here are some guiding questions and considerations to help you make a strategic choice:</p><ul><li><p><strong>Who are your users, and where do they want your app?</strong><br>Analyze your user base and usage data. Are people sideloading your app onto tablets, TVs, or watches? Do you receive requests for support on specific devices? User demand is a strong signal, but it&#8217;s not the only factor.</p></li><li><p><strong>Does your app&#8217;s core value translate well to other form factors?</strong><br>Not every app benefits from being on every device. For example, a reading app might thrive on tablets, but not make sense for cars. Consider whether your primary features can be meaningfully adapted to different environments.</p></li><li><p><strong>What is the cost of support?</strong><br>Each new form factor introduces design, development, and testing overhead. Are you equipped to maintain quality across platforms, or will this dilute your focus?</p></li><li><p><strong>Is there a strategic advantage?</strong><br>Sometimes, early adoption of a new platform can give your app a competitive edge or open up new markets. Weigh this against the risks of spreading your team too thin.</p></li><li><p><strong>Can you iterate?</strong><br>Rather than a big-bang launch, consider incremental support&#8212;start with responsive layouts, then add platform-specific features as you validate demand.</p></li></ul><p></p><h3>Post Evaluation</h3><p>If you do choose to expand, do it iteratively. Jetpack Compose offers a wide range of options to choose from that help you support <a href="https://developer.android.com/design/ui/large-screens">multiple form factors</a> in manageable steps.</p><p>I highly encourage to read <a href="https://developer.android.com/develop/ui/views/layout/canonical-layouts">canonical layouts</a> that gives you a head start on pretty much <a href="https://developer.android.com/develop/ui/views/layout/canonical-layouts#applicability">90%</a> use cases along with these <a href="https://developer.android.com/guide/topics/large-screens">guides</a>.</p><p>In the end, supporting more devices can be rewarding&#8212;but only when it&#8217;s intentional and user-driven. Don&#8217;t let FOMO or external pressure dictate your roadmap. Focus on delivering the best possible experience where it truly matters.</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #60 - Swift on Android]]></title><description><![CDATA[Bridging the Divide&#8212;How iOS and Android Developers Are Reacting]]></description><link>https://www.androidnomad.com/p/android-nomad-60-swift-on-android</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-60-swift-on-android</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 30 Jun 2025 14:02:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQnk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The announcement of the Swift <a href="https://www.swift.org/android-workgroup/">Android Workgroup</a> marks a significant milestone in the evolution of mobile development. For years, Swift has been synonymous with iOS, while Kotlin has reigned supreme on Android. Now, with the official backing of the Swift community to bring first-class Swift support to Android, the landscape is poised for transformation&#8212;and developers on both sides of the platform divide are taking notice.</p><h3>A Dream of Unified Development</h3><p>Among iOS developers, the sentiment is largely optimistic. Many see this as an opportunity to leverage their Swift expertise beyond the Apple ecosystem. The idea of writing once in Swift and running everywhere&#8212;on iOS and Android alike&#8212;has long been a dream. As one developer put it, &#8220;Congratulations Android developers! Today Android devs can develop in the great language Kotlin, soon they can develop in the AMAZING language Swift!&#8221; The potential for shared business logic, reduced code duplication, and faster onboarding excites those who have long juggled two separate codebases.</p><p>Some iOS professionals, however, remain pragmatic. While the prospect of cross-platform Swift is appealing, they acknowledge the maturity and stability of Kotlin and Jetpack Compose on Android. &#8220;If you&#8217;re starting from scratch, Flutter or KMM might still make more sense, but Swift on Android is a game-changer for teams invested in Apple&#8217;s ecosystem,&#8221; notes an iOS engineer who has dabbled in Android development.</p><h3>Android Developers: Cautious Curiosity</h3><p>On the Android side, the reactions are more mixed. Some developers are skeptical, questioning the necessity of Swift when Kotlin Multiplatform Mobile (KMP) already enables code sharing across platforms. &#8220;My thing is, why? We already have KMP,&#8221; one Android developer remarked. Concerns about tooling, app size, and the learning curve are prevalent, with some recalling past attempts at cross-platform Swift that fizzled out.</p><p>Yet, there&#8217;s also a sense of curiosity and cautious optimism. The open-source nature of Swift and the community-driven approach of the Android Workgroup are seen as positives. For those who favor language diversity and want to tap into Swift&#8217;s performance and safety features, the initiative is worth watching. &#8220;Swift going multiplatform is probably one of the biggest dev announcements (to me) from Apple,&#8221; shared another developer, highlighting the potential for innovation.</p><h3>Challenges and Opportunities Ahead</h3><p>The road to seamless Swift-on-Android development isn&#8217;t without hurdles. Issues like APK size, Swift-to-Java bridging, and the need for robust tooling remain. However, with projects like Skip enabling native SwiftUI apps on Android and the workgroup&#8217;s commitment to continuous integration and best practices, momentum is building.</p><p>For now, Swift on Android represents hope for a more unified future&#8212;a future where developers can focus on building great experiences rather than wrestling with platform boundaries. Whether this dream becomes a widespread reality will depend on the community&#8217;s willingness to contribute, experiment, and push the boundaries of what&#8217;s possible.</p><p>One thing is clear: the conversation is just beginning, and both iOS and Android developers have a stake in shaping what comes next.</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #59 - Mastering Mobile Release Management]]></title><description><![CDATA[A Step-by-Step Guide to Releasing High-Quality App Updates on iOS and Android]]></description><link>https://www.androidnomad.com/p/android-nomad-59-mastering-mobile</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-59-mastering-mobile</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Fri, 27 Jun 2025 20:00:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQnk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Releasing updates for iOS and Android apps is a complex, high-stakes process. Unlike web applications, where changes can be deployed instantly, mobile app releases must navigate app store reviews, device fragmentation, and the expectation of flawless user experiences. Here&#8217;s a comprehensive guide to handling mobile releases for both platforms, drawing on industry best practices and real-world processes.</p><h3><strong>1. Choose Your Release Cadence</strong></h3><p>Teams typically select one of three release styles:</p><ul><li><p><strong>Feature-based:</strong> Releases go out when specific features are ready. This approach can accelerate delivery of high-priority features, but may introduce unpredictability, as releases are tied to when features are completed rather than a set schedule.</p></li><li><p><strong>Time-based:</strong> Releases happen at scheduled intervals (e.g., every two weeks), regardless of which features are finished. This method creates a steady rhythm, making it easier for teams to plan and for users to anticipate updates.</p></li><li><p><strong>Release train:</strong> Releases occur on a fixed schedule, and only features completed by the cutoff make it in; unfinished work waits for the next train. This hybrid approach combines predictability with flexibility, ensuring regular releases without rushing incomplete work.</p></li></ul><p>For most scaling teams, a time-based or release train approach helps maintain predictability and reduces bottlenecks, enabling more efficient collaboration and planning.</p><h3><strong>2. Plan and Document the Process</strong></h3><p>Document every step of your release process in detail. This should include prerequisites, decision points, checklists, and responsibilities for each stage. Clear documentation ensures anyone in the team can manage a release, even if they only do so occasionally. After each release, conduct a retrospective to review what went well and what didn&#8217;t, and update your documentation to reflect new learnings and improvements. This living document becomes a critical resource for onboarding and cross-team alignment.</p><h3><strong>3. Branching Strategy</strong></h3><p>When you&#8217;re ready to prepare a release, create a dedicated &#8220;release branch&#8221; from your main development branch. This isolates the code that will ship, allowing ongoing development to continue without interfering with the release candidate. Only critical bug fixes and last-minute changes should be cherry-picked into this branch to minimize risk and maintain stability. Encourage developers to follow clear guidelines for what qualifies as a critical fix, and require code reviews for any changes merged into the release branch.</p><h3><strong>4. Continuous Integration &amp; Testing</strong></h3><p>Set up Continuous Integration (CI) pipelines to automatically build and test your release candidate for both iOS and Android. Automated tests should cover unit, integration, and UI scenarios to catch regressions early. However, manual QA is still crucial, especially for device-specific or platform-specific issues that automated tests might miss. Leverage device farms or cloud-based testing platforms to increase coverage across different hardware, OS versions, and screen sizes.</p><h3><strong>5. Assign Release Managers</strong></h3><p>Designate a rotating release manager or &#8220;release captain&#8221; for each cycle. This person coordinates the release, ensures all checklist items are completed, and acts as the point of contact for blockers or urgent decisions. The release manager should also facilitate communication between engineering, QA, product, and support teams, ensuring everyone is aligned and ready for launch.</p><h3><strong>6. Communication and Coordination</strong></h3><p>Create a dedicated communication channel (like a Slack, Teams or Internal RC channel) for each release. This centralizes updates, discussions, and troubleshooting, especially when hotfixes or simultaneous releases are needed. Having a single source of truth for release status helps prevent miscommunication and enables rapid response to issues. Share release notes, timelines, and known issues early and often to keep stakeholders informed.</p><h3><strong>7. App Store Submission</strong></h3><p>Once the release candidate passes all tests and reviews, submit it to the App Store and Play Store. Aim for a mid-week submission (Tuesday or Wednesday) to buffer against unexpected review delays or rejections, as app store review teams are typically more responsive during these days. Prepare all required assets in advance&#8212;screenshots, descriptions, and compliance information&#8212;to avoid last-minute scrambles. Monitor submission status closely and be prepared to respond promptly if the review team requests changes or clarifications.</p><h3><strong>8. Phased Rollout</strong></h3><p>After approval, start with a staged rollout&#8212;release the update to a small percentage of users first. Both the App Store and Play Store offer tools to manage staged rollouts. Monitor for crashes, regressions, and negative feedback using analytics and crash reporting tools. If all goes well, gradually ramp up to a full rollout over several days. This approach limits the impact of any unforeseen issues and provides an opportunity to intervene before all users are affected.</p><h3><strong>9. Post-Release Monitoring</strong></h3><p>Use analytics and crash reporting tools (like Sentry or Firebase Crashlytics) to track the health of your release in real time. Each component owner or team should monitor their key metrics&#8212;such as crash rates, user engagement, and retention&#8212;and be ready to respond if issues emerge. Establish clear criteria for triggering a rollback or hotfix, and communicate these thresholds to the team.</p><h3><strong>10. Handling Hotfixes</strong></h3><p>If a critical bug slips through, follow a strict protocol for hotfixes. Only urgent, user-impacting issues should trigger a hotfix to avoid introducing new problems. Prepare, test, and submit the fix as quickly as possible, adhering to the same quality checks as a regular release. Communicate clearly with your users about the update, explaining what was fixed and any necessary actions they should take.</p><h3><strong>Final Thoughts</strong></h3><p>Mobile release management is a team sport that balances speed, quality, and user trust. By standardizing your process, automating as much as possible, and fostering clear communication, you can ship updates confidently and frequently&#8212;delighting users on both iOS and Android. Remember, every release is an opportunity to build trust with your users and demonstrate your commitment to quality.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #58 - Semantics vs. clearAndSetSemantics in Compose]]></title><description><![CDATA[This information is crucial to enhance the accessibility of their user interfaces (UIs) for users with disabilities.]]></description><link>https://www.androidnomad.com/p/android-nomad-58-semantics-vs-clearandsetsemanti</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-58-semantics-vs-clearandsetsemanti</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 27 Jan 2025 15:01:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When implementing accessibility in a Compose UI, <code>semantics</code> and <code>clearAndSetSemantics</code> plays a critical role to order or group content for accessibility services.</p><h3>Core Concepts</h3><ul><li><p><strong>Semantics</strong>: In Jetpack Compose, semantics are used to provide non-visual information about UI elements to accessibility services like TalkBack. This information helps users who are visually impaired or have other disabilities understand and interact with the app.</p></li><li><p><strong>semantics Modifier</strong>: This modifier allows developers to customize the semantic information associated with a composable element.</p></li><li><p><strong>clearAndSetSemantics Modifier</strong>: This modifier provides a way to completely replace the default semantics of a composable element with a custom description.</p></li></ul><h3>Choosing Between semantics and clearAndSetSemantics</h3><p>The choice between these modifiers depends on the structure and desired accessibility behavior of the composable:</p><ul><li><p><strong>semantics for Single Elements or Merging Children</strong>: Use the semantics modifier when:</p></li><li><p>The composable element has no children, and you want to modify its default accessibility behavior.</p></li><li><p>The composable element has children, but you want them to be treated as a single logical element by accessibility services. In this case, set the mergeDescendants parameter to true.</p></li><li><p>You want some children to retain their individual semantics even when merging; set mergeDescendants to true on those specific children.</p></li><li><p><strong>clearAndSetSemantics for Custom Parent Descriptions</strong>: Use the clearAndSetSemantics modifier when you want to:</p></li><li><p>Provide a completely custom description for a parent element, overriding any descriptions from its children. This ensures only the custom description is read by accessibility services.</p></li></ul><h3>Important Considerations</h3><ul><li><p><strong>Combined Descriptions</strong>: Even when using mergeDescendants with a custom contentDescription in the semantics modifier, the system will read both the custom description and the merged descendant description. This could lead to redundant or confusing information for the user.</p></li></ul><h3>Conclusion</h3><p>By understanding the nuances of semantics and clearAndSetSemantics, we can create more inclusive and accessible apps for all users. Proper implementation of these modifiers ensures that users with disabilities can effectively navigate and interact with the UI through assistive technologies like TalkBack.</p><p>You can read my previous post on how to implement accessibility for android apps. </p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;df679439-147e-496f-95fe-c41879c46293&quot;,&quot;caption&quot;:&quot;Before implementing accessibility in Android apps, it's essential to consider a range of factors to ensure your app is inclusive and usable by people with disabilities. Here's a breakdown:&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Android Nomad #49 - Apps for Accessibility&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2161969,&quot;name&quot;:&quot;Sido&quot;,&quot;bio&quot;:&quot;Android Engineer &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdfee6a4-b881-4f20-9779-7ed909fdeea5_1032x1034.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2024-11-18T19:30:53.601Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c14f1634-cbfa-4be9-8fe3-2f55da41d709_1024x1024.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://androidnomad.substack.com/p/android-nomad-49-designing-apps-for&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:151711272,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Android Nomad&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div>]]></content:encoded></item><item><title><![CDATA[Android Nomad #57 - Dependency Injection vs Service Locator]]></title><description><![CDATA[This post explores the nuances between two prominent Inversion of Control (IoC) patterns: Dependency Injection (DI) and Service Locator (SL).]]></description><link>https://www.androidnomad.com/p/android-nomad-57-dependency-injection</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-57-dependency-injection</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 20 Jan 2025 15:03:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post explores the nuances between two prominent Inversion of Control (IoC) patterns: Dependency Injection (DI) and Service Locator (SL). While both enhance code flexibility, readability, and testability, their implementation and implications differ significantly.</p><h3>Dependency Injection</h3><ul><li><p><strong>Compile-time resolution</strong></p></li><li><p><strong>Promotes loose coupling</strong></p></li><li><p><strong>Provides dependencies via setters, constructors, fields</strong></p></li></ul><p>Lets take a look at an example.</p><pre><code>// Service interface
interface ApiService {
    fun fetchData(): String
}

// Service implementation
class ApiServiceImpl : ApiService {
    override fun fetchData(): String {
        return "Data fetched from the API"
    }
}

// Consumer class using DI
class DataManager(private val apiService: ApiService) {
    fun processData(): String {
        val data = apiService.fetchData()
        // Process the data
        return "Processed data: $data"
    }
}

// Usage
val apiService = ApiServiceImpl()
val dataManager = DataManager(apiService)
val result = dataManager.processData()</code></pre><p>In the above example, the <code>DataManager</code> class depends on the <code>ApiService</code> interface. The dependency is provided through the constructor of the <code>DataManager</code> class, allowing for easy substitution of dependencies during testing or runtime configuration.</p><h3>Service Locator</h3><ul><li><p><strong>Runtime resolution</strong></p></li><li><p><strong>Components receives dependencies via centralized registry or container</strong></p></li></ul><p>Lets take a look at an example.</p><pre><code>/ Service interface
interface ApiService {
    fun fetchData(): String
}

// Service implementation
class ApiServiceImpl : ApiService {
    override fun fetchData(): String {
        return "Data fetched from the API"
    }
}

// Service Locator
object ServiceLocator {
    private lateinit var apiService: ApiService

    fun provideApiService(): ApiService {
        if (!::apiService.isInitialized) {
            apiService = ApiServiceImpl()
        }
        return apiService
    }
}

// Consumer class using Service Locator
class DataManager {
    private val apiService: ApiService = ServiceLocator.provideApiService()

    fun processData(): String {
        val data = apiService.fetchData()
        // Process the data
        return "Processed data: $data"
    }
}

// Usage
val dataManager = DataManager()
val result = dataManager.processData()</code></pre><p>In this example, the <code>ServiceLocator</code> object acts as a central registry for providing the <code>ApiService</code> dependency. The <code>DataManager</code> class retrieves the dependency from the <code>ServiceLocator</code>, resulting in simpler code within the consumer class.</p><h3>Key Takeaways</h3><ul><li><p><strong>Compile-time vs. Runtime:</strong> The core distinction between DI and SL lies in their dependency resolution timing. DI's compile-time approach offers enhanced safety and predictability.</p></li><li><p><strong>Trade-offs:</strong> While SL boasts simplicity, DI's compile-time safety mitigates the risk of runtime crashes and potential performance issues.</p></li><li><p><strong>Flexibility:  </strong>DI allows flexibility to swap objects at runtime, SL requires configuration to map dependencies</p></li></ul><h3>Conclusion</h3><p>The simplicity and easy setup of Service Locator often don't outweigh the compile-time safety of Dependency Injection. Runtime crashes from unresolved dependencies can be detrimental, highlighting the importance of DI's proactive approach.</p><p>This analysis suggests that while both DI and SL offer advantages, DI's compile-time safety makes it a more robust and reliable choice for large-scale and complex applications where stability and predictability are paramount.</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #56 - Build Flavors]]></title><description><![CDATA[What are Build Variants?]]></description><link>https://www.androidnomad.com/p/android-nomad-56-build-flavors</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-56-build-flavors</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 13 Jan 2025 15:03:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>What are Build Variants?</strong></p><p>Build variants are essential for creating different versions of an Android application during development and release phases. </p><p>They are useful for scenarios like:</p><ul><li><p>Creating debug builds with or without <a href="https://open.substack.com/pub/androidnomad/p/proguard-pointers-for-your-android?r=1ac6p&amp;utm_campaign=post&amp;utm_medium=web&amp;showWelcomeOnShare=false">Proguard</a>.</p></li><li><p>Developing separate builds for free and paid users.</p></li><li><p>Generating builds  tailored for different Android versions.</p></li></ul><p>Build variants eliminate the need for maintaining separate projects for each version, as the core codebase remains largely consistent, with variations mainly in configurations and APIs.</p><p><strong>Understanding Build Types and Product Flavors:</strong></p><p>Build variants are created using a combination of <strong>Build Types</strong> and <strong>Product Flavors</strong>:</p><ul><li><p><strong>Build Types</strong>: Represent the build configuration, such as <em>debug</em> and <em>release</em>. Android Studio provides these by default.</p></li><li><p><strong>Product Flavors</strong>: Define different versions of the app with varying content or features but sharing the same code base. Examples include <em>free</em> and <em>paid</em> versions.</p></li></ul><p><strong>Adding Build Types:</strong></p><p>To add a new build type, modify the buildTypes block in the module-level <strong>build.gradle</strong> file. The article provides an example with three build types:</p><pre><code>buildTypes {
    debug {
        versionNameSuffix ".dev"
        debuggable true
        ...

    }
    release {
        debuggable false
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        ...
    }
    minifiedDebug {
        versionNameSuffix ".dev"
        debuggable true
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        ...
    }
}</code></pre><p>Here, minifiedDebug combines the properties of debug with Proguard enabled. You can also create a noMinifiedRelease build type that disables Proguard in the release configuration.</p><p>For creating a new build type based on an existing one, use the initWith property:</p><pre><code>newBuildType {
    initWith debug
    versionNameSuffix ".newbuild"
    ...
}</code></pre><p><strong>Adding Product Flavors:</strong></p><p>Product flavors are defined within the productFlavors block inside the android block of the <strong>build.gradle</strong> file. </p><p>Each flavor must belong to a <strong>flavor dimension</strong>:</p><pre><code>flavorDimensions "version"
productFlavors {
    development {
        dimension "version"
        versionNameSuffix ".dev"
    }

    production {
        dimension "version"
        versionNameSuffix ".prod"
    }
}</code></pre><p>This code creates two flavors, development and production, both belonging to the &#8220;version" dimension. </p><p>Combining these flavors with existing build types (<em>debug</em> and <em>release</em>) results in four build variants:</p><ul><li><p>developmentDebug</p></li><li><p>developmentRelease</p></li><li><p>productionDebug</p></li><li><p>productionRelease</p></li></ul><p>You can define multiple flavor dimensions by separating them with commas:</p><pre><code>flavorDimension "dimensionOne", "dimensionTwo", "dimensionThree"</code></pre><p><strong>Conclusion:</strong></p><p>Utilizing build variants effectively through a combination of build types and product flavors allows developers to manage different application versions efficiently within a single project. This approach significantly streamlines the development and release process, particularly for apps requiring multiple tailored versions.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #55 - Implementing Rate Limiters]]></title><description><![CDATA[Ensuring Optimal Performance and Preventing Overload with Effective Rate Limiting Strategies]]></description><link>https://www.androidnomad.com/p/android-nomad-55-implementing-rate</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-55-implementing-rate</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 06 Jan 2025 16:02:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In a world where API efficiency and user experience are paramount, implementing rate limiters in Android apps is a crucial skill. Rate limiters help prevent excessive API calls, ensuring app performance, reducing costs, and adhering to server restrictions. </p><div><hr></div><h4>What is Rate Limiting?</h4><p>Rate limiting is a strategy used to control the frequency of API requests made by a client within a specific time frame. It prevents scenarios like server overload, accidental denial-of-service (DoS) attacks, or API quota breaches. Commonly, APIs enforce rate limits such as "100 requests per minute," but app developers can also enforce limits locally for various use cases:</p><ol><li><p>Avoiding redundant requests due to repeated user actions.</p></li><li><p>Protecting against rogue processes causing high-frequency requests.</p></li><li><p>Ensuring adherence to server-side policies.</p></li></ol><div><hr></div><h4>Key Types of Rate Limiters</h4><ol><li><p><strong>Fixed Window:</strong> Limits requests within a fixed time interval (e.g., 100 requests per minute).</p></li><li><p><strong>Sliding Window:</strong> Tracks requests over a sliding time window to smooth out traffic spikes.</p></li><li><p><strong>Token Bucket:</strong> Tokens are added at a steady rate, and each request consumes a token. If no tokens remain, the request is denied.</p></li><li><p><strong>Leaky Bucket:</strong> Similar to token bucket but ensures a fixed rate of processing requests regardless of bursts.</p></li></ol><div><hr></div><h4>Implementing a Rate Limiter in an Android App</h4><p>Here&#8217;s how you can implement a simple token bucket rate limiter for an Android app using Kotlin:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist135281338\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-apiservice-kt\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot; class=\&quot;Box-body p-0 blob-wrapper data type-kotlin  \&quot;>\n\n        \n<div class=\&quot;js-check-bidi js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;ApiService.kt\&quot;>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>class</span> <span class=\&quot;pl-en\&quot;>ApiService</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>val</span> <span class=\&quot;pl-smi\&quot;>rateLimiter</span><span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-en\&quot;>RateLimiter</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>val</span> <span class=\&quot;pl-smi\&quot;>analyticsService</span><span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-en\&quot;>AnalyticsService</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>) {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>suspend</span> <span class=\&quot;pl-k\&quot;>fun</span> <span class=\&quot;pl-en\&quot;>logEvent</span>(<span class=\&quot;pl-smi\&quot;>event</span><span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-en\&quot;>AnalyticsEvent</span>): <span class=\&quot;pl-en\&quot;>Response</span>&amp;lt;<span class=\&quot;pl-en\&quot;>Data</span>&amp;gt;<span class=\&quot;pl-k\&quot;>?</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=\&quot;pl-k\&quot;>return</span> withContext(<span class=\&quot;pl-en\&quot;>Dispatchers</span>.<span class=\&quot;pl-en\&quot;>IO</span>) {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=\&quot;pl-k\&quot;>if</span> (rateLimiter.execute()) {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=\&quot;pl-k\&quot;>try</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    analyticsService.postEvent(event)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                } <span class=\&quot;pl-k\&quot;>catch</span> (e<span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-en\&quot;>Exception</span>) {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=\&quot;pl-en\&quot;>Log</span>.e(<span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>ApiService<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>, <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Error fetching data<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>, e)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=\&quot;pl-c1\&quot;>null</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            } <span class=\&quot;pl-k\&quot;>else</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=\&quot;pl-en\&quot;>Log</span>.d(<span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>ApiService<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>, <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Rate limit exceeded<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=\&quot;pl-c1\&quot;>null</span> <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Rate limit exceeded</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-apiservice-kt-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-apiservice-kt-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>}</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/SidoPillai/0ca2e5270369a24841bdf54cb57c86fd/raw/fec41b8efd537ebf60732a34be0cd7759ef1d56b/ApiService.kt\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/SidoPillai/0ca2e5270369a24841bdf54cb57c86fd#file-apiservice-kt\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          ApiService.kt\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-ratelimiter-kt\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot; class=\&quot;Box-body p-0 blob-wrapper data type-kotlin  \&quot;>\n\n        \n<div class=\&quot;js-check-bidi js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;RateLimiter.kt\&quot;>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>/*</span>*</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;> * @param scope - Coroutine scope to manage background tasks</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;> * @param maxTokens - Maximum number of allowed events (tokens)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;> * @param rateWindowMillis - Time window for token refill (in milliseconds)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;> * @param callback - Action to perform when an event is allowed</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;> * @param onExpire - Action when rate limiter expires</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;> * @param EXPIRATION_DURATION - Time after which limiter expires (e.g., 15 seconds)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;> <span class=\&quot;pl-c\&quot;>*/</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>internal</span> <span class=\&quot;pl-k\&quot;>class</span> <span class=\&quot;pl-en\&quot;>RateLimiter</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>val</span> <span class=\&quot;pl-smi\&quot;>scope</span><span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-en\&quot;>CoroutineScope</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>val</span> <span class=\&quot;pl-smi\&quot;>maxTokens</span><span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-c1\&quot;>Int</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>val</span> <span class=\&quot;pl-smi\&quot;>rateWindowMillis</span><span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-c1\&quot;>Long</span>, </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>val</span> <span class=\&quot;pl-smi\&quot;>callback</span><span class=\&quot;pl-k\&quot;>:</span> () <span class=\&quot;pl-k\&quot;>-&amp;gt;</span> <span class=\&quot;pl-c1\&quot;>Unit</span> = {},</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>val</span> <span class=\&quot;pl-smi\&quot;>onExpire</span><span class=\&quot;pl-k\&quot;>:</span> () <span class=\&quot;pl-k\&quot;>-&amp;gt;</span> <span class=\&quot;pl-c1\&quot;>Unit</span> = {},</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>val</span> <span class=\&quot;pl-smi\&quot;>EXPIRATION_DURATION</span><span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-c1\&quot;>Long</span> = <span class=\&quot;pl-c1\&quot;>15000L</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>) {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>var</span> refillCollection<span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-en\&quot;>Job</span> <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Job to handle token collection</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>var</span> refillEmission<span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-en\&quot;>Job</span> <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Job to handle periodic token refill</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>var</span> tokens<span class=\&quot;pl-k\&quot;>:</span> <span class=\&quot;pl-en\&quot;>AtomicInteger</span> <span class=\&quot;pl-k\&quot;>=</span> <span class=\&quot;pl-en\&quot;>AtomicInteger</span>(maxTokens) <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Current token count, thread-safe</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>val</span> <span class=\&quot;pl-en\&quot;>_refillFlow</span> <span class=\&quot;pl-k\&quot;>=</span> <span class=\&quot;pl-en\&quot;>MutableSharedFlow</span>&amp;lt;<span class=\&quot;pl-en\&quot;>Unit</span>&amp;gt;() <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Flow to emit refill events</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>val</span> refillFlow <span class=\&quot;pl-k\&quot;>=</span> <span class=\&quot;pl-en\&quot;>_refillFlow</span>.asSharedFlow() <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Expose the refill flow as read-only</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Initialization block, starts token refill and token collection jobs</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-en\&quot;>init</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Emission job refills tokens at regular intervals, stops after expiration duration</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        refillEmission <span class=\&quot;pl-k\&quot;>=</span> scope.launch {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=\&quot;pl-k\&quot;>for</span> (i <span class=\&quot;pl-k\&quot;>in</span> <span class=\&quot;pl-c1\&quot;>0</span><span class=\&quot;pl-k\&quot;>..</span>ceil(<span class=\&quot;pl-en\&quot;>EXPIRATION_DURATION</span>.toDouble() <span class=\&quot;pl-k\&quot;>/</span> rateWindowMillis).toInt()) {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                delay(rateWindowMillis) <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Wait for the rate window</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=\&quot;pl-en\&quot;>_refillFlow</span>.emit(<span class=\&quot;pl-c1\&quot;>Unit</span>) <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Emit token refill event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            stop() <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Stop refilling after expiration</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L35\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;35\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC35\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L36\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;36\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC36\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Collection job listens for refill events and refills tokens</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L37\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;37\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC37\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        refillCollection <span class=\&quot;pl-k\&quot;>=</span> scope.launch {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L38\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;38\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC38\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            refillFlow.collect {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L39\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;39\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC39\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                refillTokens() <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Refill the tokens when a new event is collected</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L40\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;40\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC40\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L41\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;41\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC41\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L42\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;42\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC42\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L43\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;43\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC43\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L44\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;44\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC44\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Refills the token to the maximum count</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L45\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;45\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC45\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>private</span> <span class=\&quot;pl-k\&quot;>fun</span> <span class=\&quot;pl-en\&quot;>refillTokens</span>() {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L46\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;46\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC46\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        tokens.set(maxTokens)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L47\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;47\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC47\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L48\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;48\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC48\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L49\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;49\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC49\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Called to execute an event if tokens are available</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L50\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;50\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC50\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>fun</span> <span class=\&quot;pl-en\&quot;>execute</span>() {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L51\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;51\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC51\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=\&quot;pl-k\&quot;>if</span> (tokens.getAndDecrement() <span class=\&quot;pl-k\&quot;>&amp;gt;</span> <span class=\&quot;pl-c1\&quot;>0</span>) {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L52\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;52\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC52\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            callback.invoke() <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Trigger event if tokens are available</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L53\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;53\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC53\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        } <span class=\&quot;pl-k\&quot;>else</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L54\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;54\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC54\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=\&quot;pl-en\&quot;>Log</span>.d(<span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>RateLimiter<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>, <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Limit exceeded<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>) <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Log if the rate limit is exceeded</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L55\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;55\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC55\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L56\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;56\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC56\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L57\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;57\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC57\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L58\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;58\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC58\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Stops the rate limiter when expired</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L59\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;59\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC59\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-k\&quot;>fun</span> <span class=\&quot;pl-en\&quot;>stop</span>() {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L60\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;60\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC60\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        onExpire.invoke() <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Trigger onExpire callback</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L61\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;61\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC61\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        refillEmission.cancel() <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Cancel the emission job</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L62\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;62\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC62\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        refillCollection.cancel() <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>//</span> Cancel the collection job</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L63\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;63\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC63\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-ratelimiter-kt-L64\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;64\&quot;></td>\n          <td id=\&quot;file-ratelimiter-kt-LC64\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>}</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/SidoPillai/0ca2e5270369a24841bdf54cb57c86fd/raw/fec41b8efd537ebf60732a34be0cd7759ef1d56b/RateLimiter.kt\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/SidoPillai/0ca2e5270369a24841bdf54cb57c86fd#file-ratelimiter-kt\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          RateLimiter.kt\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-9eb50564113f.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-9eb50564113f.css"><div id="gist135281338" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-apiservice-kt" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-kotlin  ">

        
<div class="js-check-bidi js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="ApiService.kt">
        <tbody><tr>
          <td id="file-apiservice-kt-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-apiservice-kt-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">class</span> <span class="pl-en">ApiService</span>(</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-apiservice-kt-LC2" class="blob-code blob-code-inner js-file-line">  <span class="pl-k">private</span> <span class="pl-k">val</span> <span class="pl-smi">rateLimiter</span><span class="pl-k">:</span> <span class="pl-en">RateLimiter</span>,</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-apiservice-kt-LC3" class="blob-code blob-code-inner js-file-line">  <span class="pl-k">private</span> <span class="pl-k">val</span> <span class="pl-smi">analyticsService</span><span class="pl-k">:</span> <span class="pl-en">AnalyticsService</span></td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-apiservice-kt-LC4" class="blob-code blob-code-inner js-file-line">) {</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-apiservice-kt-LC5" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-apiservice-kt-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">suspend</span> <span class="pl-k">fun</span> <span class="pl-en">logEvent</span>(<span class="pl-smi">event</span><span class="pl-k">:</span> <span class="pl-en">AnalyticsEvent</span>): <span class="pl-en">Response</span>&lt;<span class="pl-en">Data</span>&gt;<span class="pl-k">?</span> {</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-apiservice-kt-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> withContext(<span class="pl-en">Dispatchers</span>.<span class="pl-en">IO</span>) {</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-apiservice-kt-LC8" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> (rateLimiter.execute()) {</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-apiservice-kt-LC9" class="blob-code blob-code-inner js-file-line">                <span class="pl-k">try</span> {</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-apiservice-kt-LC10" class="blob-code blob-code-inner js-file-line">                    analyticsService.postEvent(event)</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-apiservice-kt-LC11" class="blob-code blob-code-inner js-file-line">                } <span class="pl-k">catch</span> (e<span class="pl-k">:</span> <span class="pl-en">Exception</span>) {</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-apiservice-kt-LC12" class="blob-code blob-code-inner js-file-line">                    <span class="pl-en">Log</span>.e(<span class="pl-s"><span class="pl-pds">"</span>ApiService<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>Error fetching data<span class="pl-pds">"</span></span>, e)</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-apiservice-kt-LC13" class="blob-code blob-code-inner js-file-line">                    <span class="pl-c1">null</span></td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-apiservice-kt-LC14" class="blob-code blob-code-inner js-file-line">                }</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-apiservice-kt-LC15" class="blob-code blob-code-inner js-file-line">            } <span class="pl-k">else</span> {</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-apiservice-kt-LC16" class="blob-code blob-code-inner js-file-line">                <span class="pl-en">Log</span>.d(<span class="pl-s"><span class="pl-pds">"</span>ApiService<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>Rate limit exceeded<span class="pl-pds">"</span></span>)</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-apiservice-kt-LC17" class="blob-code blob-code-inner js-file-line">                <span class="pl-c1">null</span> <span class="pl-c"><span class="pl-c">//</span> Rate limit exceeded</span></td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-apiservice-kt-LC18" class="blob-code blob-code-inner js-file-line">            }</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-apiservice-kt-LC19" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-apiservice-kt-LC20" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-apiservice-kt-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-apiservice-kt-LC21" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/SidoPillai/0ca2e5270369a24841bdf54cb57c86fd/raw/fec41b8efd537ebf60732a34be0cd7759ef1d56b/ApiService.kt" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/SidoPillai/0ca2e5270369a24841bdf54cb57c86fd#file-apiservice-kt" class="Link--inTextBlock">
          ApiService.kt
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-ratelimiter-kt" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-kotlin  ">

        
<div class="js-check-bidi js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="RateLimiter.kt">
        <tbody><tr>
          <td id="file-ratelimiter-kt-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-ratelimiter-kt-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">/*</span>*</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-ratelimiter-kt-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-c"> * @param scope - Coroutine scope to manage background tasks</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-ratelimiter-kt-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-c"> * @param maxTokens - Maximum number of allowed events (tokens)</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-ratelimiter-kt-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-c"> * @param rateWindowMillis - Time window for token refill (in milliseconds)</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-ratelimiter-kt-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-c"> * @param callback - Action to perform when an event is allowed</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-ratelimiter-kt-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-c"> * @param onExpire - Action when rate limiter expires</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-ratelimiter-kt-LC7" class="blob-code blob-code-inner js-file-line"><span class="pl-c"> * @param EXPIRATION_DURATION - Time after which limiter expires (e.g., 15 seconds)</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-ratelimiter-kt-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-c"> <span class="pl-c">*/</span></span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-ratelimiter-kt-LC9" class="blob-code blob-code-inner js-file-line"><span class="pl-k">internal</span> <span class="pl-k">class</span> <span class="pl-en">RateLimiter</span>(</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-ratelimiter-kt-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">val</span> <span class="pl-smi">scope</span><span class="pl-k">:</span> <span class="pl-en">CoroutineScope</span>,</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-ratelimiter-kt-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">val</span> <span class="pl-smi">maxTokens</span><span class="pl-k">:</span> <span class="pl-c1">Int</span>,</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-ratelimiter-kt-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">val</span> <span class="pl-smi">rateWindowMillis</span><span class="pl-k">:</span> <span class="pl-c1">Long</span>, </td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-ratelimiter-kt-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">val</span> <span class="pl-smi">callback</span><span class="pl-k">:</span> () <span class="pl-k">-&gt;</span> <span class="pl-c1">Unit</span> = {},</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-ratelimiter-kt-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">val</span> <span class="pl-smi">onExpire</span><span class="pl-k">:</span> () <span class="pl-k">-&gt;</span> <span class="pl-c1">Unit</span> = {},</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-ratelimiter-kt-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">val</span> <span class="pl-smi">EXPIRATION_DURATION</span><span class="pl-k">:</span> <span class="pl-c1">Long</span> = <span class="pl-c1">15000L</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-ratelimiter-kt-LC16" class="blob-code blob-code-inner js-file-line">) {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-ratelimiter-kt-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">var</span> refillCollection<span class="pl-k">:</span> <span class="pl-en">Job</span> <span class="pl-c"><span class="pl-c">//</span> Job to handle token collection</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-ratelimiter-kt-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">var</span> refillEmission<span class="pl-k">:</span> <span class="pl-en">Job</span> <span class="pl-c"><span class="pl-c">//</span> Job to handle periodic token refill</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-ratelimiter-kt-LC19" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-ratelimiter-kt-LC20" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">var</span> tokens<span class="pl-k">:</span> <span class="pl-en">AtomicInteger</span> <span class="pl-k">=</span> <span class="pl-en">AtomicInteger</span>(maxTokens) <span class="pl-c"><span class="pl-c">//</span> Current token count, thread-safe</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-ratelimiter-kt-LC21" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-ratelimiter-kt-LC22" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">val</span> <span class="pl-en">_refillFlow</span> <span class="pl-k">=</span> <span class="pl-en">MutableSharedFlow</span>&lt;<span class="pl-en">Unit</span>&gt;() <span class="pl-c"><span class="pl-c">//</span> Flow to emit refill events</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-ratelimiter-kt-LC23" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">val</span> refillFlow <span class="pl-k">=</span> <span class="pl-en">_refillFlow</span>.asSharedFlow() <span class="pl-c"><span class="pl-c">//</span> Expose the refill flow as read-only</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-ratelimiter-kt-LC24" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-ratelimiter-kt-LC25" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"><span class="pl-c">//</span> Initialization block, starts token refill and token collection jobs</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-ratelimiter-kt-LC26" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">init</span> {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-ratelimiter-kt-LC27" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"><span class="pl-c">//</span> Emission job refills tokens at regular intervals, stops after expiration duration</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-ratelimiter-kt-LC28" class="blob-code blob-code-inner js-file-line">        refillEmission <span class="pl-k">=</span> scope.launch {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-ratelimiter-kt-LC29" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">for</span> (i <span class="pl-k">in</span> <span class="pl-c1">0</span><span class="pl-k">..</span>ceil(<span class="pl-en">EXPIRATION_DURATION</span>.toDouble() <span class="pl-k">/</span> rateWindowMillis).toInt()) {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-ratelimiter-kt-LC30" class="blob-code blob-code-inner js-file-line">                delay(rateWindowMillis) <span class="pl-c"><span class="pl-c">//</span> Wait for the rate window</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-ratelimiter-kt-LC31" class="blob-code blob-code-inner js-file-line">                <span class="pl-en">_refillFlow</span>.emit(<span class="pl-c1">Unit</span>) <span class="pl-c"><span class="pl-c">//</span> Emit token refill event</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-ratelimiter-kt-LC32" class="blob-code blob-code-inner js-file-line">            }</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-ratelimiter-kt-LC33" class="blob-code blob-code-inner js-file-line">            stop() <span class="pl-c"><span class="pl-c">//</span> Stop refilling after expiration</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-ratelimiter-kt-LC34" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L35" class="blob-num js-line-number js-blob-rnum" data-line-number="35"></td>
          <td id="file-ratelimiter-kt-LC35" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L36" class="blob-num js-line-number js-blob-rnum" data-line-number="36"></td>
          <td id="file-ratelimiter-kt-LC36" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"><span class="pl-c">//</span> Collection job listens for refill events and refills tokens</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L37" class="blob-num js-line-number js-blob-rnum" data-line-number="37"></td>
          <td id="file-ratelimiter-kt-LC37" class="blob-code blob-code-inner js-file-line">        refillCollection <span class="pl-k">=</span> scope.launch {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L38" class="blob-num js-line-number js-blob-rnum" data-line-number="38"></td>
          <td id="file-ratelimiter-kt-LC38" class="blob-code blob-code-inner js-file-line">            refillFlow.collect {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L39" class="blob-num js-line-number js-blob-rnum" data-line-number="39"></td>
          <td id="file-ratelimiter-kt-LC39" class="blob-code blob-code-inner js-file-line">                refillTokens() <span class="pl-c"><span class="pl-c">//</span> Refill the tokens when a new event is collected</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L40" class="blob-num js-line-number js-blob-rnum" data-line-number="40"></td>
          <td id="file-ratelimiter-kt-LC40" class="blob-code blob-code-inner js-file-line">            }</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L41" class="blob-num js-line-number js-blob-rnum" data-line-number="41"></td>
          <td id="file-ratelimiter-kt-LC41" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L42" class="blob-num js-line-number js-blob-rnum" data-line-number="42"></td>
          <td id="file-ratelimiter-kt-LC42" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L43" class="blob-num js-line-number js-blob-rnum" data-line-number="43"></td>
          <td id="file-ratelimiter-kt-LC43" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L44" class="blob-num js-line-number js-blob-rnum" data-line-number="44"></td>
          <td id="file-ratelimiter-kt-LC44" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"><span class="pl-c">//</span> Refills the token to the maximum count</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L45" class="blob-num js-line-number js-blob-rnum" data-line-number="45"></td>
          <td id="file-ratelimiter-kt-LC45" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">private</span> <span class="pl-k">fun</span> <span class="pl-en">refillTokens</span>() {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L46" class="blob-num js-line-number js-blob-rnum" data-line-number="46"></td>
          <td id="file-ratelimiter-kt-LC46" class="blob-code blob-code-inner js-file-line">        tokens.set(maxTokens)</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L47" class="blob-num js-line-number js-blob-rnum" data-line-number="47"></td>
          <td id="file-ratelimiter-kt-LC47" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L48" class="blob-num js-line-number js-blob-rnum" data-line-number="48"></td>
          <td id="file-ratelimiter-kt-LC48" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L49" class="blob-num js-line-number js-blob-rnum" data-line-number="49"></td>
          <td id="file-ratelimiter-kt-LC49" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"><span class="pl-c">//</span> Called to execute an event if tokens are available</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L50" class="blob-num js-line-number js-blob-rnum" data-line-number="50"></td>
          <td id="file-ratelimiter-kt-LC50" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">fun</span> <span class="pl-en">execute</span>() {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L51" class="blob-num js-line-number js-blob-rnum" data-line-number="51"></td>
          <td id="file-ratelimiter-kt-LC51" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> (tokens.getAndDecrement() <span class="pl-k">&gt;</span> <span class="pl-c1">0</span>) {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L52" class="blob-num js-line-number js-blob-rnum" data-line-number="52"></td>
          <td id="file-ratelimiter-kt-LC52" class="blob-code blob-code-inner js-file-line">            callback.invoke() <span class="pl-c"><span class="pl-c">//</span> Trigger event if tokens are available</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L53" class="blob-num js-line-number js-blob-rnum" data-line-number="53"></td>
          <td id="file-ratelimiter-kt-LC53" class="blob-code blob-code-inner js-file-line">        } <span class="pl-k">else</span> {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L54" class="blob-num js-line-number js-blob-rnum" data-line-number="54"></td>
          <td id="file-ratelimiter-kt-LC54" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">Log</span>.d(<span class="pl-s"><span class="pl-pds">"</span>RateLimiter<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>Limit exceeded<span class="pl-pds">"</span></span>) <span class="pl-c"><span class="pl-c">//</span> Log if the rate limit is exceeded</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L55" class="blob-num js-line-number js-blob-rnum" data-line-number="55"></td>
          <td id="file-ratelimiter-kt-LC55" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L56" class="blob-num js-line-number js-blob-rnum" data-line-number="56"></td>
          <td id="file-ratelimiter-kt-LC56" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L57" class="blob-num js-line-number js-blob-rnum" data-line-number="57"></td>
          <td id="file-ratelimiter-kt-LC57" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L58" class="blob-num js-line-number js-blob-rnum" data-line-number="58"></td>
          <td id="file-ratelimiter-kt-LC58" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"><span class="pl-c">//</span> Stops the rate limiter when expired</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L59" class="blob-num js-line-number js-blob-rnum" data-line-number="59"></td>
          <td id="file-ratelimiter-kt-LC59" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">fun</span> <span class="pl-en">stop</span>() {</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L60" class="blob-num js-line-number js-blob-rnum" data-line-number="60"></td>
          <td id="file-ratelimiter-kt-LC60" class="blob-code blob-code-inner js-file-line">        onExpire.invoke() <span class="pl-c"><span class="pl-c">//</span> Trigger onExpire callback</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L61" class="blob-num js-line-number js-blob-rnum" data-line-number="61"></td>
          <td id="file-ratelimiter-kt-LC61" class="blob-code blob-code-inner js-file-line">        refillEmission.cancel() <span class="pl-c"><span class="pl-c">//</span> Cancel the emission job</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L62" class="blob-num js-line-number js-blob-rnum" data-line-number="62"></td>
          <td id="file-ratelimiter-kt-LC62" class="blob-code blob-code-inner js-file-line">        refillCollection.cancel() <span class="pl-c"><span class="pl-c">//</span> Cancel the collection job</span></td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L63" class="blob-num js-line-number js-blob-rnum" data-line-number="63"></td>
          <td id="file-ratelimiter-kt-LC63" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-ratelimiter-kt-L64" class="blob-num js-line-number js-blob-rnum" data-line-number="64"></td>
          <td id="file-ratelimiter-kt-LC64" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/SidoPillai/0ca2e5270369a24841bdf54cb57c86fd/raw/fec41b8efd537ebf60732a34be0cd7759ef1d56b/RateLimiter.kt" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/SidoPillai/0ca2e5270369a24841bdf54cb57c86fd#file-ratelimiter-kt" class="Link--inTextBlock">
          RateLimiter.kt
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><div><hr></div><h5><strong>Handle Rate-Limiting Errors Gracefully</strong></h5><p>If you&#8217;ve implemented server-side rate limiting, handle <code>429 Too Many Requests</code> responses:</p><pre><code>suspend fun fetchDataWithRetry(): Response&lt;Data&gt;? {
    var response: Response&lt;Data&gt;?
    var retryCount = 0

    do {
        response = fetchData()
        if (response?.code() == 429) {
            val retryAfter = response.headers()["Retry-After"]?.toLongOrNull() ?: 1000
            delay(retryAfter)
        }
        retryCount++
    } while (response?.code() == 429 &amp;&amp; retryCount &lt; MAX_RETRIES)

    return response
}</code></pre><div><hr></div><h4>Best Practices for Using Rate Limiters</h4><ol><li><p><strong>Use Dependency Injection (DI):</strong></p><ul><li><p>Integrate the <code>RateLimiter</code> with DI frameworks like Hilt or Dagger to ensure a singleton instance across the app.</p></li><li><p>Example with Hilt:</p><pre><code>@Module
@InstallIn(SingletonComponent::class)
object RateLimiterModule {
    @Provides
    @Singleton
    fun provideRateLimiter(): RateLimiter {
        return RateLimiter(
            scope = CoroutineScope(Dispatchers.IO),
            maxTokens = 10,
            rateWindowMillis = 1000L
        )
    }
}</code></pre></li></ul></li><li><p><strong>Centralize Configuration:</strong></p><ul><li><p>Define rate limiter parameters (e.g., <code>maxTokens</code>, <code>rateWindowMillis</code>) in a configuration file or constants class for easier updates and consistency.</p></li></ul></li><li><p><strong>Thread Safety:</strong></p><ul><li><p>Ensure the <code>RateLimiter</code> instance is thread-safe, especially when accessed from multiple parts of the app.</p></li></ul></li><li><p><strong>Testability:</strong></p><ul><li><p>Mock the <code>RateLimiter</code> in unit tests to simulate rate-limiting scenarios.</p></li></ul></li><li><p><strong>Scalability:</strong></p><ul><li><p>Use different rate limiter instances for different API endpoints if they have distinct rate limits.</p></li></ul></li><li><p><strong>Performance Monitoring:</strong></p><ul><li><p>Log rate limiter usage and failures to analyze patterns and optimize settings.</p></li></ul></li></ol><div><hr></div><h4>Conclusion</h4><p>Rate limiters are essential for building robust Android apps that efficiently manage network resources while providing seamless user experiences. By implementing a rate limiter, you can optimize app performance, prevent server overload, and maintain compliance with API policies. Whether you&#8217;re using a simple token bucket or integrating advanced server-side strategies, rate limiting is a cornerstone of modern app development.</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #54 - Creating a Scalable Design System with Jetpack Compose]]></title><description><![CDATA[Creating a consistent and scalable design system is a cornerstone of modern app development, and Jetpack Compose offers a powerful toolkit to make this process seamless.]]></description><link>https://www.androidnomad.com/p/android-nomad-54-creating-a-scalable</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-54-creating-a-scalable</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 16 Dec 2024 16:02:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!UKOV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Creating a consistent and scalable design system is a cornerstone of modern app development, and Jetpack Compose offers a powerful toolkit to make this process seamless. This blog will guide you through the essentials of building a robust design system with Compose, covering key concepts, practical steps, and the benefits it brings.</p><div><hr></div><h3>Design System</h3><p>A design system ensures a cohesive and recognizable user experience by maintaining consistency across all aspects of a product&#8217;s design. It serves as the foundation for streamlined collaboration between designers and developers, reducing design debt and enhancing scalability.</p><p>A comprehensive design system includes:</p><ul><li><p><strong>Component Library</strong>: Reusable UI components.</p></li><li><p><strong>Branding</strong>: Colors, typography, iconography, and shapes.</p></li><li><p><strong>Design Principles</strong>: Visual and functional aspects of the product.</p></li><li><p><strong>Content Guidelines</strong>: Standardized assets and messaging principles.</p></li><li><p><strong>Accessibility Guidelines</strong>: Inclusive design considerations.</p></li></ul><p>By combining these elements, you can streamline the development process, reduce design debt, and enhance collaboration between designers and developers.</p><div><hr></div><h3>1. Building a Material Color Scheme with Tokens </h3><p>One of the first steps in creating a design system is defining a color scheme. Using the <strong>Material Theme Builder</strong> <a href="https://www.figma.com/community/file/1035203688168086460/material-3-design-kit">Figma</a> plugin simplifies this process:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UKOV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UKOV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png 424w, https://substackcdn.com/image/fetch/$s_!UKOV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png 848w, https://substackcdn.com/image/fetch/$s_!UKOV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png 1272w, https://substackcdn.com/image/fetch/$s_!UKOV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UKOV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png" width="678" height="1648" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1648,&quot;width&quot;:678,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:171689,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UKOV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png 424w, https://substackcdn.com/image/fetch/$s_!UKOV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png 848w, https://substackcdn.com/image/fetch/$s_!UKOV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png 1272w, https://substackcdn.com/image/fetch/$s_!UKOV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe17fe427-678c-41a9-8f7a-9b1c3d3fbe14_678x1648.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>Generate Colors</strong>: Quickly create a cohesive color scheme with a few clicks.</p><p></p><p>You can literally generate the color scheme with few clicks. This plugin lets you export this color as literal theme files which can be dropped into your project. </p><p>The tokens are built-in and they are already setup for you.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u3JB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u3JB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png 424w, https://substackcdn.com/image/fetch/$s_!u3JB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png 848w, https://substackcdn.com/image/fetch/$s_!u3JB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png 1272w, https://substackcdn.com/image/fetch/$s_!u3JB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u3JB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png" width="1456" height="430" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:430,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:202772,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!u3JB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png 424w, https://substackcdn.com/image/fetch/$s_!u3JB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png 848w, https://substackcdn.com/image/fetch/$s_!u3JB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png 1272w, https://substackcdn.com/image/fetch/$s_!u3JB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50032068-f6ff-4e5a-98f1-74d2c52211ba_2040x602.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p></li><li><p><strong>Export Theme Files</strong>: Export the generated colors as theme files, including:</p><ul><li><p><code>ui/theme/Color.kt</code>: Contains all colors used by your theme and assigns them to roles.</p></li><li><p><code>ui/theme/Theme.kt</code>: Includes code to instantiate an app theme.</p></li><li><p><code>ui/theme/Type.kt</code>: Contains all typeface definitions.</p></li></ul></li></ul><h4>How to Integrate:</h4><ol><li><p>Unzip the exported files and copy them to <code>/app/src/main/java/&lt;your_package&gt;/</code>.</p></li><li><p>Update the package name from <code>com.example.ui.theme</code> to your app&#8217;s package name.</p></li></ol><p>By starting with these files, you&#8217;ll have a strong foundation for maintaining color and type consistency.</p><div><hr></div><h3>2. Modularize</h3><p>Modularization is optional but beneficial for multi-module apps. It allows better separation of concerns and easier maintenance.</p><h4>Steps to Modularize:</h4><ol><li><p>Create a new module for your design system.</p></li><li><p>Drop the theme files into the appropriate package.</p></li><li><p>Enable Compose features in the module by updating the <code>build.gradle</code> file.</p></li></ol><div><hr></div><h3>3. CompositionLocalProvider</h3><p>By default, the theme defined in the project or the plugin may have this structure.</p><pre><code>@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = true,
    content: @Composable() () -&gt; Unit
) {
  val colorScheme = when {
      dynamicColor &amp;&amp; Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.S -&gt; {
          val context = LocalContext.current
          if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
      }
      
      darkTheme -&gt; darkScheme
      else -&gt; lightScheme
  }
  val view = LocalView.current
  if (!view.isInEditMode) {
    SideEffect {
      val window = (view.context as Activity).window
      window.statusBarColor = colorScheme.primary.toArgb()
      WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
    }
  }

  MaterialTheme(
    colorScheme = colorScheme,
    typography = AppTypography,
    content = content
  )
}</code></pre><p>We may want to tweak this and place this under <a href="https://developer.android.com/develop/ui/compose/compositionlocal">CompositionLocalProvider</a> for passing data down through the Composition implicitly.</p><pre><code>@Composable
fun DesignSystemTheme(
    typography: DesignSystemTypography = DesignSystemTheme.typography,
    dimensions: DesignSystemDimension = DesignSystemTheme.dimensions,
    elevation: DesignSystemElevation = DesignSystemTheme.elevation,
    radius: DesignSystemRadius = DesignSystemTheme.radius,
    icons: DesignSystemIcon = DesignSystemTheme.icons,
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable () -&gt; Unit
) {
    val colorScheme = when {
        dynamicColor &amp;&amp; Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.S -&gt; {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }

        darkTheme -&gt; darkScheme
        else -&gt; lightScheme
    }
    val view = LocalView.current
    if (!view.isInEditMode) {
        SideEffect {
            val window = (view.context as Activity).window
            window.statusBarColor = colorScheme.primary.toArgb()
            WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
        }
    }

    CompositionLocalProvider(
        LocalColors provides colorScheme,
        LocalDimensions provides dimensions,
        LocalTypography provides typography,
        LocalElevation provides elevation,
        LocalIcon provides icons,
        LocalRadius provides radius,
        LocalIndication provides rememberRipple(),
    ) {
        content()
    }
}</code></pre><p>This approach ensures that the theme dynamically adjusts, and changes propagate from the parent node to its children.</p><div><hr></div><h3>4. Reusable Component</h3><p>Reusable components are the building blocks of a design system. Examples include:</p><ul><li><p><strong>State Indicators</strong>: Represent loading, error, or unknown states.</p></li><li><p><strong>Image Views</strong>: Standardized image handling.</p></li><li><p><strong>Text Fields</strong>: Consistent styling for user input.</p></li><li><p><strong>Media Elements</strong>: Components for videos and interactive content.</p></li></ul><h4>Accessibility Considerations:</h4><ul><li><p>Ensure proper roles and semantic definitions.</p></li><li><p>Use alt-text for media components.</p></li><li><p>Support live regions for dynamic updates.</p></li></ul><p>By focusing on these aspects, your components will be robust and accessible.</p><p>You can refer to the following posts to learn more about accessibility</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;64d107de-a98f-474a-b5e8-0196da5374ac&quot;,&quot;caption&quot;:&quot;Before implementing accessibility in Android apps, it's essential to consider a range of factors to ensure your app is inclusive and usable by people with disabilities. Here's a breakdown:&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Android Nomad #49 - Apps for Accessibility&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2161969,&quot;name&quot;:&quot;Sido&quot;,&quot;bio&quot;:&quot;Android Engineer &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdfee6a4-b881-4f20-9779-7ed909fdeea5_1032x1034.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2024-11-18T19:30:53.601Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c14f1634-cbfa-4be9-8fe3-2f55da41d709_1024x1024.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://androidnomad.substack.com/p/android-nomad-49-designing-apps-for&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:151711272,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Android Nomad&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;5215094b-97c9-4234-b689-db7193a2c025&quot;,&quot;caption&quot;:&quot;As mobile developers, it's our responsibility to ensure that our apps are accessible to all users, regardless of their abilities. With the introduction of Jetpack Compose, Google's modern toolkit for building native Android UI, we have powerful new tools at our disposal to create more accessible applications. In this post, we'll explore how Jetpack Comp&#8230;&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Android Nomad - #33 Accessibility&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2161969,&quot;name&quot;:&quot;Sido&quot;,&quot;bio&quot;:&quot;Android Engineer &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdfee6a4-b881-4f20-9779-7ed909fdeea5_1032x1034.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2024-08-19T22:24:21.404Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://androidnomad.substack.com/p/android-nomad-33-accessibility&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:147901636,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Android Nomad&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><h3>5. Testing Your Design System</h3><p>Compose makes testing easier with tools like <a href="https://developer.android.com/develop/ui/compose/tooling/previews">Compose Preview</a> and the Compose screenshot <a href="https://developer.android.com/studio/preview/compose-screenshot-testing">tool</a>.</p><h4>Testing Scenarios:</h4><ul><li><p>Light/Dark themes.</p></li><li><p>Different devices and screen sizes.</p></li><li><p>Multilingual support.</p></li><li><p>Varying font sizes and animations.</p></li></ul><p>Use these tools to visualize and validate components under diverse conditions, ensuring a polished user experience.</p><p>You can learn more about testing strategies from the developers of this tool <a href="https://www.droidcon.com/2024/11/22/scalable-testing-strategies/">here</a>. </p><div><hr></div><h3>Benefits of a Design System</h3><ul><li><p><strong>Consistency</strong>: Uniform design across the app.</p></li><li><p><strong>Scalability</strong>: Easily extendable for new features.</p></li><li><p><strong>Reusability</strong>: Shared components reduce redundancy.</p></li><li><p><strong>Testability</strong>: Easier debugging and validation.</p></li><li><p><strong>Accessibility</strong>: Inclusive design as a priority.</p></li></ul><div><hr></div><h3>Conclusion</h3><p>Jetpack Compose, combined with <a href="https://m3.material.io/">Material3</a>  guidelines, empowers developers to build design systems that are consistent, scalable, and visually appealing. By following the steps outlined here, you can create a robust foundation for your app while streamlining collaboration and enhancing user experience. Happy coding!</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #53 - Android Domain Interview]]></title><description><![CDATA[FAQs]]></description><link>https://www.androidnomad.com/p/android-nomad-53-android-domain-interview</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-53-android-domain-interview</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 09 Dec 2024 15:02:14 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3><strong>1. What is the Android operating system?</strong></h3><p>The Android operating system is a Linux-based mobile platform developed by Google, designed primarily for touchscreen devices such as smartphones and tablets. It provides a user-friendly interface, extensive app ecosystem via Google Play Store, and adaptability for diverse hardware configurations. Its architecture includes the Linux kernel, libraries, runtime, application framework, and system apps, making it powerful for both users and developers.</p><h3><strong>2. Describe the four main Android app components.</strong></h3><p>The four main Android app components are:</p><ul><li><p><strong>Activities</strong>: Represent a single screen of the app&#8217;s UI, like a login page. Activities handle user interaction.</p></li><li><p><strong>Services</strong>: Run background operations, such as playing music or syncing data, without a user interface.</p></li><li><p><strong>Broadcast Receivers</strong>: React to system-wide events, like receiving an SMS or detecting low battery.</p></li><li><p><strong>Content Providers</strong>: Manage data sharing between applications, such as accessing a contact list.</p></li></ul><p>Each component has a specific role in building a complete, interactive application.</p><h3><strong>3. What are the Intents in Android?</strong></h3><p>Intents are messaging objects used to communicate between app components. They enable activities, services, and broadcast receivers to interact seamlessly.</p><ul><li><p><strong>Explicit Intents</strong> specify the target component directly, such as starting a specific activity.</p></li><li><p><strong>Implicit Intents</strong> declare a general action, like sharing data, and allow the system to choose the best app for the task.</p></li></ul><p>For example, an app can use an implicit intent to open a web page in the browser.</p><h3><strong>4. How can you explain the Android app life cycle?</strong></h3><p>The Android app lifecycle describes the sequence of states an app goes through during its existence, managed by callback methods:</p><ul><li><p><strong>onCreate()</strong>: Initializes the activity.</p></li><li><p><strong>onStart()</strong>: Makes it visible to the user.</p></li><li><p><strong>onResume()</strong>: Brings it to the foreground.</p></li><li><p><strong>onPause()</strong>: Partially hides it.</p></li><li><p><strong>onStop()</strong>: Completely hides it.</p></li><li><p><strong>onDestroy()</strong>: Cleans up before termination.</p></li></ul><p>Understanding these states ensures efficient resource management and smooth user experiences.</p><h3><strong>5. How do you handle screen orientation changes in Android?</strong></h3><p>When a device&#8217;s orientation changes, the current activity is destroyed and recreated. To preserve the app state:</p><ul><li><p>Use <code>onSaveInstanceState()</code> to store transient data.</p></li><li><p>Restore the data using <code>onRestoreInstanceState()</code> or <code>onCreate()</code> during the new activity instance's lifecycle.</p></li></ul><p>Alternatively, handle configuration changes manually by specifying <code>configChanges</code> in the manifest.</p><h3><strong>6. What is an Android Manifest file?</strong></h3><p>The Android Manifest file is an XML file that declares essential app information to the system. It specifies components like activities, services, and broadcast receivers, along with required permissions, hardware features, and metadata. For example, it declares whether an app needs internet access or supports specific screen orientations.</p><h3><strong>7. What is the significance of the Context class in Android?</strong></h3><p>The <code>Context</code> class is a gateway to application resources and services. It facilitates access to:</p><ul><li><p><strong>System services</strong> (e.g., location, notifications).</p></li><li><p><strong>Resource files</strong> (e.g., strings, layouts).</p></li><li><p><strong>Application data</strong> (e.g., shared preferences, database).</p></li></ul><p>Activities, application instances, and services provide specific types of <code>Context</code> that developers use depending on their needs.</p><h3><strong>8. What are Layouts in Android?</strong></h3><p>Layouts define how UI elements are arranged in an Android application.</p><ul><li><p><strong>LinearLayout</strong>: Aligns elements in a single direction (horizontal or vertical).</p></li><li><p><strong>ConstraintLayout</strong>: Offers a flexible way to position UI elements using constraints.</p></li><li><p><strong>RelativeLayout</strong>: Positions elements relative to each other or the parent.</p></li></ul><p>Choosing the right layout ensures an optimized and responsive design across different screen sizes.</p><h3><strong>9. How would you explain the difference between a ScrollView and a RecyclerView?</strong></h3><ul><li><p><strong>ScrollView</strong>: Best for displaying a static, vertically scrollable list of views.</p></li><li><p><strong>RecyclerView</strong>: Designed for dynamic or large datasets, providing efficient memory management by reusing item views through a <strong>ViewHolder</strong> pattern.</p></li></ul><p>Use ScrollView for simpler needs and RecyclerView for performance-critical, interactive lists.</p><h3><strong>10. How would you create a custom view in Android?</strong></h3><p>Creating a custom view involves:</p><ol><li><p>Extending the <code>View</code> class or a subclass.</p></li><li><p>Overriding methods like <code>onDraw()</code> for custom rendering and <code>onMeasure()</code> for size handling.</p></li><li><p>Adding XML attributes if customization is required.</p></li><li><p>Using the view in your layout or adding it programmatically.</p></li></ol><p>This approach is ideal when standard widgets don&#8217;t meet your design needs.</p><h3><strong>11. What is the difference between dp, sp, pt, and px?</strong></h3><ul><li><p><strong>dp (density-independent pixels)</strong>: Ensures consistent UI element size across screens.</p></li><li><p><strong>sp (scale-independent pixels)</strong>: Similar to dp but scales with user font preferences; used for text.</p></li><li><p><strong>pt (points)</strong>: Rarely used; 1/72 inch and doesn&#8217;t adapt to density.</p></li><li><p><strong>px (pixels)</strong>: Exact screen pixels, which vary by density, making it less ideal for layouts.</p></li></ul><p>For scalable designs, use dp for layouts and sp for text.</p><h3><strong>12. How would you explain the concept of Fragments?</strong></h3><p>Fragments are modular sections of an activity&#8217;s UI. They are used for creating dynamic and flexible user interfaces. Fragments enable reuse within multiple activities and support multi-pane layouts, especially on larger screens like tablets.</p><p>For example, a messaging app can use fragments for a contact list on the left and a chat view on the right.</p><h3><strong>13. What is Shared Preferences?</strong></h3><p>Shared Preferences is a lightweight mechanism to store key-value pairs of primitive data types. It&#8217;s ideal for saving user settings or small amounts of app state data. Access it via <code>SharedPreferences</code> API, and store data securely with <code>MODE_PRIVATE</code> when needed.</p><h3><strong>14. How to use SQLite in Android?</strong></h3><p>SQLite is a lightweight relational database embedded in Android, making it an excellent choice for local data storage.</p><p>Steps to use SQLite in Android:</p><ol><li><p><strong>Create an SQLiteOpenHelper</strong>: This class simplifies database management by handling creation and versioning.</p></li><li><p><strong>Override </strong><code>onCreate()</code><strong> and </strong><code>onUpgrade()</code>: Define the schema and manage changes to it over time.</p></li><li><p><strong>Access the Database</strong>: Use <code>getReadableDatabase()</code> or <code>getWritableDatabase()</code> for read/write operations.</p></li><li><p><strong>Perform Data Operations</strong>: Use methods like <code>insert()</code>, <code>update()</code>, <code>delete()</code>, or execute raw queries using <code>query()</code> and <code>rawQuery()</code>.</p></li><li><p><strong>Close Connections</strong>: Always close the database after operations to free resources and avoid memory leaks.</p></li></ol><p>For instance, SQLite is great for storing app-specific data like user preferences or offline caches.</p><h3><strong>15. How do you perform a background task in Android?</strong></h3><p>Android offers multiple approaches to run background tasks, ensuring a responsive UI:</p><ol><li><p><strong>Services</strong>:</p><ul><li><p><strong>Foreground Services</strong>: Used for noticeable tasks like music playback, requiring a persistent notification.</p></li><li><p><strong>IntentService</strong>: Ideal for one-time tasks that stop automatically after completion.</p></li></ul></li><li><p><strong>ThreadPoolExecutor &amp; Handlers</strong>: Enable concurrent execution of lightweight tasks while maintaining communication with the main thread.</p></li><li><p><strong>WorkManager</strong>: A modern API for scheduling deferrable and guaranteed tasks that persist across app restarts. Suitable for tasks like periodic data syncing.</p></li></ol><p>Always ensure background tasks don&#8217;t overload system resources or block the UI thread.</p><h3><strong>16. What is a Loader?</strong></h3><p>A Loader simplifies asynchronous data loading in Android applications. It is often used to fetch data from a database or a server while avoiding UI thread blockage.</p><p>Key benefits:</p><ul><li><p><strong>Lifecycle Awareness</strong>: Automatically reconnects to existing data after configuration changes like rotations.</p></li><li><p><strong>Efficiency</strong>: Prevents reloading of data unnecessarily.</p></li></ul><p>Although Loaders have been largely replaced by modern libraries and patterns like LiveData and ViewModel, they are still useful in legacy applications.</p><h3><strong>17. What is the Room Persistence Library, and why is it useful?</strong></h3><p>The Room Persistence Library is part of Android's Architecture Components, offering an abstraction over SQLite to simplify database operations.</p><ul><li><p><strong>Key Features</strong>:</p><ul><li><p>Provides compile-time verification of SQL queries, reducing runtime errors.</p></li><li><p>Supports observable data with LiveData, Flow, or RxJava.</p></li><li><p>Ensures simpler and more maintainable code compared to raw SQLite.</p></li></ul></li></ul><p>Room integrates seamlessly with the Jetpack ecosystem, making it the go-to choice for database management in modern Android development.</p><h3><strong>18. How to make a network request on Android?</strong></h3><p>Making network requests in Android often involves libraries like Retrofit or OkHttp to simplify HTTP operations.</p><p>Steps to make a network request:</p><ol><li><p><strong>Set Permissions</strong>: Add the <code>INTERNET</code> permission in the manifest file.</p></li><li><p><strong>Initialize the Library</strong>: Define API endpoints and configure the library.</p></li><li><p><strong>Perform Operations</strong>: Make requests asynchronously to prevent UI thread blocking.</p></li><li><p><strong>Handle Responses</strong>: Process the results or errors appropriately.</p></li></ol><p>For example, Retrofit allows easy integration with REST APIs and supports Kotlin coroutines for asynchronous calls.</p><h3><strong>19. What libraries can be used for networking in Android?</strong></h3><p>Several libraries enhance networking in Android by abstracting complex tasks:</p><ol><li><p><strong>Retrofit</strong>: A type-safe HTTP client by Square, ideal for RESTful APIs. Its integration with Gson and Kotlin coroutines simplifies asynchronous operations.</p></li><li><p><strong>OkHttp</strong>: A foundational HTTP client for tasks like caching, connection pooling, and monitoring. Often used under the hood by other libraries.</p></li><li><p><strong>Volley</strong>: Designed for lightweight, bursty requests, providing rapid caching and streamlined response handling.</p></li></ol><p>These libraries are tailored to specific needs, with Retrofit being the most popular for modern development.</p><h3><strong>20. How do you handle caching in Android?</strong></h3><p>Caching improves app performance by reducing network calls and reusing data efficiently.</p><ul><li><p><strong>Shared Preferences</strong>: Store lightweight data like user settings.</p></li><li><p><strong>SQLite or Room</strong>: Manage structured data persistently.</p></li><li><p><strong>Glide/Picasso</strong>: Cache images for smoother loading.</p></li><li><p><strong>Retrofit with OkHttp</strong>: Cache HTTP responses for offline access.</p></li><li><p><strong>Custom Cache Implementation</strong>: Use the <code>Cache</code> class for advanced or unique requirements.</p></li></ul><p>Caching strategies depend on the type of data and app requirements.</p><h3><strong>21. How can you secure data transmission in Android?</strong></h3><p>To secure data transmission, follow best practices to protect sensitive information:</p><ol><li><p><strong>Use HTTPS</strong>: Ensures encrypted communication between the app and server.</p></li><li><p><strong>Implement SSL/TLS Pinning</strong>: Mitigates man-in-the-middle attacks.</p></li><li><p><strong>Encrypt Sensitive Data</strong>: Use AES or other robust encryption algorithms.</p></li><li><p><strong>Token-Based Authentication</strong>: Restrict API access with secure tokens.</p></li><li><p><strong>Minimize Data Exposure</strong>: Transmit only what is necessary.</p></li></ol><p>Always validate third-party libraries to avoid introducing vulnerabilities.</p><h3><strong>22. How do you find memory leaks in Android apps?</strong></h3><p>Memory leaks occur when objects are retained in memory but are no longer in use.</p><ul><li><p><strong>Memory Profiler</strong>: Available in Android Studio, it tracks memory usage, analyzes heap dumps, and identifies retained objects causing leaks.</p></li><li><p><strong>LeakCanary</strong>: A third-party library that detects and notifies about leaks during runtime.</p></li></ul><p>Proactively testing with these tools ensures better resource management and a smoother user experience.</p><h3><strong>23. How can you improve the performance of a ListView?</strong></h3><p>Enhance ListView performance with the following strategies:</p><ol><li><p><strong>ViewHolder Pattern</strong>: Reduces repeated <code>findViewById()</code> calls.</p></li><li><p><strong>Recycling Views</strong>: Use <code>convertView</code> in the adapter&#8217;s <code>getView()</code> method to reuse existing views.</p></li><li><p><strong>Lazy Loading</strong>: Load images or data incrementally for smoother scrolling.</p></li><li><p><strong>Flat Layouts</strong>: Avoid deeply nested views to reduce rendering complexity.</p></li><li><p><strong>Pagination</strong>: Load data in chunks for long lists to improve memory usage.</p></li></ol><p>Testing and profiling regularly ensures ListView remains performant.</p><h3><strong>24. How to debug ANRs and crashes in Android?</strong></h3><p>Debugging ANRs (Application Not Responding) and crashes requires a methodical approach:</p><ol><li><p><strong>ANRs</strong>:</p><ul><li><p>Analyze the <code>traces.txt</code> file to identify blocking code.</p></li><li><p>Optimize long-running tasks by moving them off the main thread.</p></li></ul></li><li><p><strong>Crashes</strong>:</p><ul><li><p>Use Logcat in Android Studio to trace the stack and identify crash causes.</p></li><li><p>Leverage Android Profiler for insights into resource usage and bottlenecks.</p></li></ul></li></ol><p>Reproducing issues in a controlled environment and stepping through code using the debugger aids in resolving these problems effectively.</p><h3><strong>25. How do you optimize the performance of an Android application?</strong></h3><p>Optimizing Android app performance involves strategies such as:</p><ul><li><p><strong>Using tools like Android Profiler</strong> to monitor CPU, memory, and network usage.</p></li><li><p><strong>Reducing APK size</strong> with ProGuard and removing unused resources.</p></li><li><p><strong>Implementing lazy loading</strong> for large datasets and images.</p></li><li><p><strong>Utilizing background threads</strong> like Coroutines for long-running tasks.</p></li><li><p><strong>Minimizing third-party libraries</strong> and testing across diverse devices for consistent performance.</p></li></ul><h3><strong>26. What are the different ways to schedule tasks in Android?</strong></h3><ul><li><p><strong>AlarmManager</strong>: For time-specific tasks, even if the app isn't active.</p></li><li><p><strong>JobScheduler</strong>: For tasks based on conditions (e.g., network connectivity).</p></li><li><p><strong>WorkManager</strong>: For guaranteed, deferrable tasks.</p></li><li><p><strong>Handler/Looper</strong>: For short, repetitive tasks on the main thread.</p></li><li><p><strong>Timer</strong>: For fixed-rate or fixed-delay execution.</p></li><li><p><strong>ScheduledThreadPoolExecutor</strong>: A Java-based alternative for generic scheduling.</p></li></ul><h3><strong>27. How can you explain the concept of Services in Android?</strong></h3><p>Services perform background tasks without a user interface. Examples include:</p><ul><li><p><strong>Bound services</strong>: Allow interaction with other app components.</p></li><li><p><strong>Unbound services</strong>: Operate independently, such as a music player continuing playback when the user navigates away.</p></li></ul><h3><strong>28. What is Data Binding?</strong></h3><p>Data Binding links UI components directly to data sources in XML layouts, enabling automatic updates when data changes. It reduces boilerplate code and improves responsiveness by automating UI updates.</p><h3><strong>29. How does ProGuard work?</strong></h3><p>ProGuard:</p><ul><li><p><strong>Shrinks code</strong> by removing unused classes and methods.</p></li><li><p><strong>Optimizes</strong> bytecode for performance.</p></li><li><p><strong>Obfuscates</strong> code by renaming classes, fields, and methods to enhance security. This process ensures smaller APKs and harder-to-reverse-engineer apps.</p></li></ul><h3><strong>30. What is Dependency Injection? Provide examples with Dagger.</strong></h3><p>Dependency Injection provides objects their dependencies externally.<br><strong>Dagger Example</strong>:</p><ul><li><p>Annotate dependencies (<code>@Inject</code>) and modules (<code>@Module</code>) to let Dagger create and provide them.<br>E.g., A <code>Car</code> class with an injected <code>Engine</code> eliminates manual instantiation, improving modularity and testability.</p></li></ul><h3><strong>31. How do you perform unit testing in Android?</strong></h3><ul><li><p>Use <strong>JUnit</strong> for unit tests.</p></li><li><p>Employ <strong>Robolectric</strong> or <strong>Espresso</strong> for Android-specific testing.</p></li><li><p>Mock dependencies with <strong>Mockito</strong>.</p></li><li><p>Automate tests with CI tools like Jenkin</p></li></ul><h3><strong>32. How would you explain the importance of the Espresso testing framework?</strong></h3><p>Espresso enables precise UI testing by simulating user interactions. It ensures:</p><ul><li><p><strong>Functional correctness</strong> of the UI.</p></li><li><p><strong>Bug-free user experiences</strong>, particularly for complex applications.</p></li><li><p>It is essential for maintaining quality in dynamic UI environments.</p></li></ul><h3><strong>33. How can you describe the Android app deployment process?</strong></h3><p>The Android app deployment process includes:</p><ol><li><p>Testing and building a release APK.</p></li><li><p><strong>Signing the APK</strong> with a private key.</p></li><li><p>Uploading to <strong>Google Play Store</strong> for review.</p></li><li><p>Post-approval, the app is available for download, with regular updates for compatibility and security.</p></li></ol><h3><strong>34. What are the different build types in an Android project?</strong></h3><ul><li><p><strong>Debug</strong>: Used during development, with debugging enabled and no optimization.</p></li><li><p><strong>Release</strong>: Optimized and secure for distribution to users, with debugging disabled.</p></li></ul><h3><strong>35. How can you Explain the difference between val and var in Kotlin?</strong></h3><ul><li><p><code>val</code>: Immutable; its value cannot change after initialization.</p></li><li><p><code>var</code>: Mutable; its value can be updated.<br>Use <code>val</code> for constants and <code>var</code> for variables requiring modifications.</p></li></ul><h3><strong>36. How does Java Garbage Collection work in Android?</strong></h3><p>Java Garbage Collection in Android uses an automatic memory management process to reclaim memory occupied by objects no longer in use.</p><p><strong>Key Points:</strong></p><ul><li><p><strong>Algorithm:</strong> It primarily uses the <em>mark-and-sweep</em> algorithm. Objects are marked as "reachable" or "unreachable", and those not reachable are swept away.</p></li><li><p><strong>Avoiding Leaks:</strong> Developers must nullify references to objects that are no longer needed, especially in contexts like activities or fragments, to avoid memory leaks.</p></li><li><p><strong>Performance:</strong> While it optimizes memory, garbage collection pauses can cause performance hitches if the app generates excessive garbage.</p></li></ul><h3><strong>37. How Null Safety is achieved in Kotlin?</strong></h3><p>Kotlin ensures null safety by making all variables non-nullable by default, eliminating the risk of null pointer exceptions (NPEs).</p><p><strong>Techniques for Null Safety:</strong></p><ol><li><p><strong>Nullable Types:</strong> Declare variables nullable using the <code>?</code> modifier (e.g., <code>val name: String?</code>).</p></li><li><p><strong>Safe Calls (</strong><code>?.</code><strong>):</strong> Use to access properties/methods on nullable objects safely.</p></li><li><p><strong>Elvis Operator (</strong><code>?:</code><strong>):</strong> Provides a fallback value if a variable is null.</p></li><li><p><strong>Null Assertion (</strong><code>!!</code><strong>):</strong> Forces access to nullable variables but throws an exception if null. Use cautiously.</p></li></ol><h3><strong>38. What are the differences between == and === in Kotlin?</strong></h3><ul><li><p><code>==</code><strong> (Structural Equality):</strong> Compares the values/contents of objects. Equivalent to <code>.equals()</code> in Java.</p></li><li><p><code>===</code><strong> (Referential Equality):</strong> Checks if two references point to the same object in memory.</p></li></ul><h3><strong>39. How do you handle exceptions in Java and Kotlin?</strong></h3><p>Exception handling ensures stability and proper error management in Android apps.</p><ul><li><p><strong>Java:</strong><br>Use <code>try-catch-finally</code> blocks. Example:</p></li></ul><pre><code>try {
    // Risky code
} catch (Exception e) {
    // Handle exception
} finally {
    // Cleanup code
}</code></pre><ul><li><p><strong>Kotlin:</strong><br>Kotlin simplifies exception handling with concise syntax and allows catching multiple exceptions using <code>when</code>. Example:</p></li></ul><pre><code>try {
    // Risky code
} catch (e: Exception) {
    when (e) {
        is IOException -&gt; handleIOException()
        is NullPointerException -&gt; handleNPE()
    }
} finally {
    // Cleanup code
}</code></pre><h3><strong>40. What is the MVP architecture?</strong></h3><p><strong>MVP (Model-View-Presenter)</strong> is an Android architectural pattern that separates application logic from UI components.</p><ul><li><p><strong>Model:</strong> Manages data and business logic (e.g., fetching data).</p></li><li><p><strong>View:</strong> Displays data to the user and sends user inputs to the Presenter.</p></li><li><p><strong>Presenter:</strong> Acts as a mediator between Model and View, handling user interactions and updating the UI.</p></li></ul><p><strong>Benefits:</strong> Decoupling UI and logic simplifies testing and enhances code maintainability.</p><h3><strong>41. How is MVVM different from MVP?</strong></h3><ul><li><p><strong>MVVM (Model-View-ViewModel):</strong> Utilizes two-way data binding, where the View automatically updates when the ViewModel data changes.</p></li><li><p><strong>MVP:</strong> Requires the Presenter to manually update the View.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kH4K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kH4K!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png 424w, https://substackcdn.com/image/fetch/$s_!kH4K!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png 848w, https://substackcdn.com/image/fetch/$s_!kH4K!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png 1272w, https://substackcdn.com/image/fetch/$s_!kH4K!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kH4K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png" width="1408" height="248" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:248,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59569,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kH4K!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png 424w, https://substackcdn.com/image/fetch/$s_!kH4K!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png 848w, https://substackcdn.com/image/fetch/$s_!kH4K!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png 1272w, https://substackcdn.com/image/fetch/$s_!kH4K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c6d9573-e4f0-4efe-9b5f-abfadf01d810_1408x248.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3><strong>42. How can you explain the Singleton design pattern?</strong></h3><p>The <strong>Singleton design pattern</strong> ensures that a class has only one instance and provides a global access point to it.</p><p><strong>Use Cases:</strong> Shared resources like databases, configuration settings, or network managers.</p><h3><strong>43. How do you handle configuration changes in Android using ViewModel?</strong></h3><ul><li><p><strong>Persistent Data:</strong> Store UI-related data in a <code>ViewModel</code>. It survives configuration changes like screen rotations.</p></li><li><p><strong>LiveData:</strong> Use LiveData in the ViewModel to observe and automatically update the UI.</p></li></ul><h3><strong>44. What is the Repository pattern, and why is it useful?</strong></h3><p>The <strong>Repository pattern</strong> separates data access logic from business logic, acting as an intermediary between the data source and the rest of the application.</p><p><strong>Benefits:</strong></p><ul><li><p>Centralizes data access logic.</p></li><li><p>Simplifies switching between data sources (e.g., API and local DB).</p></li><li><p>Improves testability by mocking repository behavior.</p></li></ul><h3><strong>45. How do you securely store sensitive information in Android?</strong></h3><ul><li><p><strong>Keystore:</strong> Use for secure cryptographic key storage.</p></li><li><p><strong>EncryptedSharedPreferences:</strong> Encrypts data stored in preferences.</p></li><li><p><strong>Avoid Hardcoding:</strong> Never store sensitive information in code.</p></li><li><p><strong>Secure Communication:</strong> Use HTTPS and TLS for data transfer.</p></li><li><p><strong>Obfuscation:</strong> Minimize reverse engineering risks with ProGuard or R8.</p></li></ul><h3><strong>46. How can you explain how to secure an Android app?</strong></h3><p><strong>Steps to Secure an Android App:</strong></p><ul><li><p>Use <strong>biometric authentication</strong> or <strong>multi-factor authentication</strong>.</p></li><li><p>Employ <strong>encryption</strong> for local storage and network communications.</p></li><li><p>Implement <strong>certificate pinning</strong> to guard against man-in-the-middle attacks.</p></li><li><p>Regularly audit the app for vulnerabilities.</p></li><li><p>Follow <strong>best practices</strong> for API security, including token-based authentication.</p></li></ul><h3><strong>47. What are Network Security Configurations?</strong></h3><p>Network Security Configurations allow developers to define custom security rules for network traffic in an app.</p><p><strong>Features:</strong></p><ul><li><p>Restrict cleartext traffic.</p></li><li><p>Enforce certificate pinning.</p></li><li><p>Define domain-specific security settings.</p></li></ul><p><strong>Implementation:</strong> Use an XML file (<code>network_security_config.xml</code>) linked in the app&#8217;s manifest.</p><h3><strong>48. How can you prevent reverse engineering of an Android app?</strong></h3><ul><li><p><strong>Obfuscation:</strong> Use tools like ProGuard or R8 to obfuscate the code.</p></li><li><p><strong>Encryption:</strong> Encrypt sensitive data and logic.</p></li><li><p><strong>Integrity Checks:</strong> Implement tamper detection.</p></li><li><p><strong>Proactive Tools:</strong> Use runtime protection mechanisms like DexGuard.</p></li><li><p><strong>Licensing:</strong> Employ license verification to deter unauthorized usage.</p></li><li><p><strong>Frequent Updates:</strong> Patch vulnerabilities regularly to stay ahead of threats.</p></li></ul><h3><strong>49. What are coroutines?</strong></h3><p>Coroutines in Android are a powerful concurrency framework provided by Kotlin to simplify asynchronous programming. They help manage tasks that might block the main thread, such as network requests, database operations, or complex computations, while keeping your code readable and concise.</p><h3>Key Features of Coroutines:</h3><ol><li><p><strong>Lightweight</strong>: Coroutines are more memory-efficient than traditional threads. You can run thousands of coroutines without significant memory overhead.</p></li><li><p><strong>Structured Concurrency</strong>: They are tied to a lifecycle (like <code>ViewModel</code> or <code>Activity</code>), ensuring that tasks are automatically canceled when the lifecycle ends.</p></li><li><p><strong>Suspend Functions</strong>: Functions marked with <code>suspend</code> allow you to write asynchronous code sequentially. You can use them with keywords like <code>await</code> or <code>withContext</code> to handle background tasks.</p></li><li><p><strong>Built-in Lifecycle Awareness</strong>: Using <code>LifecycleScope</code> or <code>ViewModelScope</code>, you can ensure coroutines are canceled automatically when their associated lifecycle ends, reducing memory leaks.</p></li><li><p><strong>Error Handling</strong>: Coroutines provide structured error handling using <code>try-catch</code> or <code>CoroutineExceptionHandler</code>.</p></li></ol><h3><strong>50. What are Flows?</strong></h3><p><strong>Flows</strong> are part of Kotlin's <strong>coroutines</strong> library, designed to handle asynchronous data streams in a more structured and efficient way. They are especially useful for emitting multiple values sequentially over time, making them a perfect fit for scenarios like observing data changes in real-time.</p><h3>Key Characteristics of Flows:</h3><ol><li><p><strong>Cold Stream</strong>:</p><ul><li><p>A flow is "cold" by default, meaning it does not emit data until it is actively collected. This helps in saving resources as it only processes when needed.</p></li></ul></li><li><p><strong>Asynchronous</strong>:</p><ul><li><p>Flows run within coroutines and can be used to handle data asynchronously, avoiding UI thread blocking.</p></li></ul></li><li><p><strong>Lifecycle-Aware Integration</strong>:</p><ul><li><p>Flows work seamlessly with Android's <code>LifecycleOwner</code> to ensure that data is collected only when the associated lifecycle is active, reducing the risk of memory leaks.</p></li></ul></li></ol><div><hr></div><h3>Basic Components of Flows:</h3><ol><li><p><code>Flow</code>: Represents a cold stream of values that can be emitted sequentially.</p></li><li><p><code>SharedFlow</code>: Represents a hot stream of values shared across multiple collectors. Useful for events.</p></li><li><p><code>StateFlow</code>: A hot flow that holds and emits the latest value. Ideal for UI state management.</p></li></ol>]]></content:encoded></item><item><title><![CDATA[Android Nomad #52 - Fast Binary Encoding]]></title><description><![CDATA[In the era of mobile development, efficient data encoding and decoding mechanisms are crucial to optimize performance, reduce app latency, and minimize resource usage.]]></description><link>https://www.androidnomad.com/p/android-nomad-51-fast-binary-encoding</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-51-fast-binary-encoding</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 02 Dec 2024 16:02:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the era of mobile development, efficient data encoding and decoding mechanisms are crucial to optimize performance, reduce app latency, and minimize resource usage. Traditional formats like JSON and XML are human-readable but can be relatively slow and bloated for high-performance scenarios. This is where <strong>fast binary encoding</strong> steps in as a game-changer.</p><h2>What is Fast Binary Encoding?</h2><p>Fast binary encoding refers to the process of converting data into compact, efficient binary formats that prioritize speed and space efficiency. Popular binary encoding formats include <strong>Protocol Buffers (Protobuf)</strong>, <strong>FlatBuffers</strong>, <strong>MessagePack</strong>, and <strong>Cap&#8217;n Proto</strong>. These formats significantly reduce payload sizes compared to text-based encodings and improve serialization and deserialization speeds.</p><h3>Benefits of Fast Binary Encoding in Android</h3><ol><li><p><strong>Performance</strong>: Faster serialization/deserialization compared to JSON or XML.</p></li><li><p><strong>Reduced Payload Size</strong>: Smaller data transmissions save bandwidth and improve network performance.</p></li><li><p><strong>Cross-Language Support</strong>: Interoperable with multiple programming languages, ideal for multi-platform systems.</p></li><li><p><strong>Scalability</strong>: Handles complex nested data structures efficiently.</p></li><li><p><strong>Battery Efficiency</strong>: Lower computational overhead helps conserve battery life.</p></li></ol><div><hr></div><h2>Popular Fast Binary Encoding Formats</h2><h3>1. <strong>Protocol Buffers (Protobuf)</strong></h3><ul><li><p>Developed by Google.</p></li><li><p>Schema-based; uses <code>.proto</code> files to define the structure.</p></li><li><p>Highly efficient and widely used in mobile and backend applications.</p></li></ul><h3>2. <strong>FlatBuffers</strong></h3><ul><li><p>Also developed by Google.</p></li><li><p>Optimized for minimal overhead in parsing; supports zero-copy deserialization.</p></li><li><p>Suitable for real-time applications like games or AR/VR.</p></li></ul><h3>3. <strong>MessagePack</strong></h3><ul><li><p>Compact and schema-less.</p></li><li><p>Easy to integrate into existing systems with minimal configuration.</p></li></ul><h3>4. <strong>Cap&#8217;n Proto</strong></h3><ul><li><p>Extremely fast, with built-in RPC support.</p></li><li><p>Focuses on serialization performance and simplicity.</p></li></ul><div><hr></div><h2>Using Fast Binary Encoding in Android Apps</h2><p>Let&#8217;s walk through the implementation of <strong>Protocol Buffers (Protobuf)</strong> as an example of fast binary encoding in an Android app.</p><h3>Step 1: Add Protobuf Dependencies</h3><p>Include the following dependencies in your <code>build.gradle</code> file:</p><pre><code>plugins {
    id 'com.android.application'
    id 'com.google.protobuf' version '0.9.3'
}

android {
    compileSdk 34
    defaultConfig {
        applicationId "com.example.binaryencoding"
        minSdk 21
        targetSdk 34
    }
}

dependencies {
    implementation 'com.google.protobuf:protobuf-javalite:3.21.12'
}

protobuf {
    protoc {
        artifact = 'com.google.protobuf:protoc:3.21.12'
    }
    generateProtoTasks {
        all().each { task -&gt;
            task.builtins {
                java {
                    option 'lite'
                }
            }
        }
    }
}</code></pre><h3>Step 2: Define the Protobuf Schema</h3><p>Create a <code>person.proto</code> file in the <code>src/main/proto</code> directory:</p><pre><code>syntax = "proto3";

message Person {
    int32 id = 1;
    string name = 2;
    string email = 3;
}</code></pre><h3>Step 3: Generate Code</h3><p>After syncing the Gradle file, Protobuf generates the required Java classes for the schema. These classes will be used for encoding and decoding.</p><h3>Step 4: Serialize and Deserialize Data</h3><p>Here&#8217;s how you can use the generated code in your app:</p><pre><code>import com.example.binaryencoding.Person

fun serializePerson(): ByteArray {
    val person = Person.newBuilder()
        .setId(1)
        .setName("John Doe")
        .setEmail("john.doe@example.com")
        .build()

    return person.toByteArray()
}

fun deserializePerson(data: ByteArray): Person {
    return Person.parseFrom(data)
}

fun main() {
    val serializedData = serializePerson()
    println("Serialized Data: ${serializedData.joinToString()}")

    val deserializedPerson = deserializePerson(serializedData)
    println("Deserialized Person: ${deserializedPerson.name}, ${deserializedPerson.email}")
}</code></pre><div><hr></div><h2>Use Cases for Fast Binary Encoding</h2><ol><li><p><strong>Networking</strong>: Reduce latency for REST or gRPC calls.</p></li><li><p><strong>Data Caching</strong>: Store compact serialized objects in databases like Room or SQLite.</p></li><li><p><strong>Real-Time Applications</strong>: Enhance performance in gaming or IoT applications.</p></li><li><p><strong>Cross-Platform Communication</strong>: Sync data between Android, iOS, and backend systems efficiently.</p></li></ol><div><hr></div><h2>Conclusion</h2><p>Fast binary encoding is an invaluable tool for developers seeking to optimize data handling in Android apps. Whether you're working on a chat app, a real-time game, or an IoT system, adopting formats like Protobuf or FlatBuffers can drastically enhance your app's efficiency and scalability.</p><p><strong>Explore, experiment, and embrace the binary revolution!</strong></p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #51 - Flow]]></title><description><![CDATA[Understanding Kotlin Flow, StateFlow, and SharedFlow: Choosing the Right Tool for Your App]]></description><link>https://www.androidnomad.com/p/android-nomad-51-flow</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-51-flow</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 25 Nov 2024 16:02:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Kotlin&#8217;s Flow API is a cornerstone of modern asynchronous programming, offering tools to manage sequential, stateful, and event-driven data streams effectively. This blog delves deep into <strong>Flow</strong>, <strong>StateFlow</strong>, and <strong>SharedFlow</strong>, their differences, and practical applications, incorporating examples and best practices.</p><h2><strong>1. Flow: The Foundation of Reactive Streams</strong></h2><h3><strong>What is Flow?</strong></h3><p><code>Flow</code> represents a <strong>cold stream of sequential values</strong> that are asynchronously generated and emitted. Unlike suspending functions (which return a single value) or collections (which hold a set of values), a <code>Flow</code> emits values on demand, only when collected.</p><h3><strong>Flow Characteristics</strong></h3><ul><li><p><strong>Cold Nature</strong>: Values are not emitted until a terminal operator (like <code>collect</code>) is applied.</p></li><li><p><strong>Operators</strong>: Intermediate operators like <code>map</code> and <code>filter</code> transform or react to emissions, while terminal operators like <code>collect</code> and <code>first</code> finalize the stream.</p></li><li><p><strong>Efficiency</strong>: A cold nature ensures resources are not wasted on unnecessary computations.</p></li></ul><h3><strong>Creating and Using a Flow</strong></h3><h4><strong>Flow Builders</strong></h4><ol><li><p><code>flow {}</code>: Use for custom logic and emitting values explicitly.</p></li><li><p><code>flowOf(...)</code>: Quickly create a flow from given values.</p></li><li><p><code>.asFlow()</code>: Convert collections or ranges into a flow.</p></li></ol><h4><strong>Example: Flow with Operators</strong></h4><pre><code>import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking

val numberFlow = flow {  
    emit(1)  
    emit(2)  
}.map { value -&gt;  
    "Transformed Value: $value"  
}  

runBlocking {  
    numberFlow.collect { value -&gt; println(value) }  
}</code></pre><h2><strong>2. StateFlow: Reactive State Management</strong></h2><h3><strong>What is StateFlow?</strong></h3><p><code>StateFlow</code> is a <strong>hot stream</strong> that always holds the latest value and emits it to new collectors. It is ideal for managing and sharing <strong>state</strong> across components like ViewModels and UI layers.</p><h3><strong>Key Features</strong></h3><ul><li><p><strong>Hot Stream</strong>: Emits the latest state to all active collectors.</p></li><li><p><strong>Atomic Updates</strong>: Safely update values using <code>.update</code> or <code>.value</code>.</p></li><li><p><strong>Thread Safety</strong>: Designed for concurrent environments.</p></li></ul><h3><strong>Creating StateFlow</strong></h3><ol><li><p><strong>From MutableStateFlow</strong></p></li></ol><pre><code>val mutableState = MutableStateFlow("Initial State")
val stateFlow = mutableState.asStateFlow()</code></pre><ol start="2"><li><p><strong>Using </strong><code>stateIn</code><br>Convert a cold <code>Flow</code> to <code>StateFlow</code> using <code>stateIn</code>.</p></li></ol><pre><code>val myFlow = flowOf(1, 2, 3)
val stateFlow = myFlow.stateIn(
    scope = coroutineScope,
    started = SharingStarted.Lazily,
    initialValue = 0
)</code></pre><h3><strong>Example: State Management with StateFlow</strong></h3><pre><code>val mutableState = MutableStateFlow("Idle")

// Update state atomically
mutableState.update { currentState -&gt;  
    "$currentState -&gt; Active"  
}  

println(mutableState.value) // Output: "Idle -&gt; Active"

// Collect state changes
mutableState.collect { state -&gt;  
    println("New State: $state")  
}</code></pre><h2><strong>3. SharedFlow: Event-Driven Programming</strong></h2><h3><strong>What is SharedFlow?</strong></h3><p><code>SharedFlow</code> is a <strong>hot stream</strong> designed for <strong>broadcasting events</strong>. Unlike <code>StateFlow</code>, it does not hold a state but supports configurable replay for event propagation.</p><h3><strong>Key Features</strong></h3><ul><li><p><strong>Hot Stream</strong>: Events are broadcast to active collectors.</p></li><li><p><strong>Replay Cache</strong>: Configurable to replay past events for new collectors.</p></li><li><p><strong>Multiple Collectors</strong>: Emit events to many subscribers.</p></li></ul><h3><strong>When to Use SharedFlow</strong></h3><ul><li><p>Broadcasting UI events like navigation or snack bars.</p></li><li><p>Decoupling event producers and consumers.</p></li></ul><h3><strong>Example: Event Broadcasting with SharedFlow</strong></h3><pre><code>import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.runBlocking

val eventFlow = MutableSharedFlow&lt;String&gt;(replay = 2) // Cache last 2 events

runBlocking {
    // Emit events
    eventFlow.emit("Event 1")
    eventFlow.emit("Event 2")

    // Collect events
    eventFlow.asSharedFlow().collect { event -&gt;  
        println("Received: $event")  
    }
}</code></pre><h3>Key Differences</h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!S-o6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S-o6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png 424w, https://substackcdn.com/image/fetch/$s_!S-o6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png 848w, https://substackcdn.com/image/fetch/$s_!S-o6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png 1272w, https://substackcdn.com/image/fetch/$s_!S-o6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S-o6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png" width="1456" height="335" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:335,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:136056,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!S-o6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png 424w, https://substackcdn.com/image/fetch/$s_!S-o6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png 848w, https://substackcdn.com/image/fetch/$s_!S-o6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png 1272w, https://substackcdn.com/image/fetch/$s_!S-o6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d6c811-eaa1-46e7-98df-c3932d73ac44_2436x560.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2><strong>Flow Operators: Intermediate and Terminal</strong></h2><h3><strong>Intermediate Operators</strong></h3><ul><li><p><strong>Transformations</strong>:</p><ul><li><p><code>map</code>: Converts data types.</p></li><li><p><code>filter</code>: Filters values based on conditions.</p></li><li><p><code>onEach</code>: Reacts to each emitted value.</p></li></ul></li><li><p><strong>Adding Emissions</strong>:</p><ul><li><p><code>onStart</code>: Emits values before the main stream starts.</p></li></ul></li></ul><h4><strong>Example: Intermediate Operators</strong></h4><pre><code>(0..10).asFlow()
    .onStart { emit(11) }
    .filter { it % 2 == 0 }
    .map { "Even Number: $it" }
    .collect { println(it) }</code></pre><h3><strong>Terminal Operators</strong></h3><ul><li><p><strong>Non-Canceling</strong>:</p><ul><li><p><code>collect</code>: Reacts to each emitted value.</p></li><li><p><code>toList</code>: Collects all values into a list.</p></li></ul></li><li><p><strong>Canceling</strong>:</p><ul><li><p><code>first</code>: Collects the first value and cancels the stream.</p></li></ul></li></ul><h4><strong>Example: Terminal Operators</strong></h4><pre><code>val firstValue = flowOf(1, 2, 3).first() // Output: 1
println(firstValue)</code></pre><h2><strong>Handling Exceptions</strong></h2><p>Use <code>catch</code> or inline <code>try-catch</code> for robust error handling.</p><h3><strong>Example: Exception Handling in Flow</strong></h3><pre><code>flow {
    emit(1)
    throw Exception("Something went wrong")
}.catch { exception -&gt;  
    emit("Error: ${exception.message}")  
}.collect { value -&gt;  
    println(value)  
}</code></pre><h2><strong>Choosing the Right Tool</strong></h2><ul><li><p><strong>Use Flow</strong> for cold, sequential data streams (e.g., paginated API results).</p></li><li><p><strong>Use StateFlow</strong> for state management (e.g., ViewModel state in Android).</p></li><li><p><strong>Use SharedFlow</strong> for event broadcasting (e.g., navigation events).</p></li></ul><p>By understanding these APIs and applying them effectively, you can build scalable and reactive Kotlin applications effortlessly.</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #50 - Android Engineer career roadmap]]></title><description><![CDATA[Structured Android Developer career roadmap with associated roles, responsibilities, success strategies, and high-level tips]]></description><link>https://www.androidnomad.com/p/android-nomad-50-android-engineer</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-50-android-engineer</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Thu, 21 Nov 2024 13:02:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3></h3><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/1euqA/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2243a87a-e3b3-43e8-9983-ffbfb2a8efc5_1260x660.png&quot;,&quot;thumbnail_url_full&quot;:&quot;&quot;,&quot;height&quot;:1296,&quot;title&quot;:&quot;Android Engineer Career Roadmap&quot;,&quot;description&quot;:&quot;&quot;}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/1euqA/1/" width="730" height="1296" frameborder="0" scrolling="no"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><h3><strong>General Success Strategies</strong></h3><ol><li><p><strong>Continuous Learning</strong>:</p><ul><li><p>Stay updated with the latest Android tools, libraries, and features (e.g., Jetpack Compose, Hilt, Kotlin Coroutines).</p></li><li><p>Explore cross-platform technologies like .NET MAUI or Flutter for versatility.</p></li></ul></li><li><p><strong>Build a Strong Portfolio</strong>:</p><ul><li><p>Develop apps showcasing various skills (e.g., animations, networking, offline support).</p></li><li><p>Contribute to open-source projects.</p></li></ul></li><li><p><strong>Master Tools</strong>:</p><ul><li><p>Learn Android Studio, Git, Gradle, and profiling tools in depth.</p></li></ul></li><li><p><strong>Network</strong>:</p><ul><li><p>Participate in Android communities, conferences, and hackathons.</p></li><li><p>Follow industry leaders to learn trends and best practices.</p></li></ul></li><li><p><strong>Soft Skills</strong>:</p><ul><li><p>Develop teamwork, communication, and leadership skills.</p></li><li><p>Practice articulating technical challenges and solutions.</p></li></ul></li><li><p><strong>Certifications</strong>:</p><ul><li><p>Obtain certifications like Google Associate Android Developer to validate skills.</p></li></ul></li></ol><h3><strong>Strategies for Role Transitions</strong></h3><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/71mgU/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/93d65728-b337-4e06-a4e2-54b3bceca537_1260x660.png&quot;,&quot;thumbnail_url_full&quot;:&quot;&quot;,&quot;height&quot;:731,&quot;title&quot;:&quot;Role Transition Roadmap&quot;,&quot;description&quot;:&quot;&quot;}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/71mgU/1/" width="730" height="731" frameborder="0" scrolling="no"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><h3></h3><p>Hope this helps :) </p><div><hr></div>]]></content:encoded></item><item><title><![CDATA[Android Nomad #49 - Apps for Accessibility]]></title><description><![CDATA[Designing accessibility for your android apps.]]></description><link>https://www.androidnomad.com/p/android-nomad-49-designing-apps-for</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-49-designing-apps-for</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 18 Nov 2024 19:30:53 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c14f1634-cbfa-4be9-8fe3-2f55da41d709_1024x1024.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Before implementing accessibility in Android apps, it's essential to consider a range of factors to ensure your app is inclusive and usable by people with disabilities. Here's a breakdown:</p><div><hr></div><h3>1. <strong>Understand Accessibility Guidelines</strong></h3><ul><li><p><strong>WCAG (Web Content Accessibility Guidelines):</strong> Familiarize yourself with <a href="https://www.w3.org/WAI/standards-guidelines/wcag/">these</a> standards, especially the ones relevant to mobile apps.</p></li><li><p><strong>Android Accessibility Guidelines:</strong> Follow Google's <a href="https://support.google.com/accessibility/android/answer/6006564?hl=en">accessibility</a> best practices for Android.</p></li></ul><div><hr></div><h3>2. <strong>Identify Your User Base</strong></h3><ul><li><p>Understand the needs of your target users, such as those with visual, auditory, physical, or cognitive impairments.</p></li><li><p>Perform a usability study to understand gaps throughout the app.</p></li></ul><div><hr></div><h3>3. Accessibility features on Android</h3><p>These are some of the features that are available under Accessibility services.</p><ul><li><p>Keyboard Access</p><ul><li><p>Control android device via external keyboard and perform actions by pressing keys on the keyboard</p></li></ul></li><li><p>Voice Access</p><ul><li><p>Control android device via voice. This is especially useful for users who have difficulty using the touchscreen.</p></li></ul></li><li><p>Talkback Access</p><ul><li><p>It is built-in screen reader that allows you to navigate through apps and perform actions. </p></li></ul></li><li><p>Switch Access</p><ul><li><p>Control android device via external switches, buttons or braille displays. This is especially useful for people with a motor disability.</p></li></ul></li></ul><div><hr></div><h3><strong>4. Plan for Accessibility from the Start</strong></h3><p>Accessibility should be a core design principle rather than an afterthought. This ensures better integration into your app's UI/UX.</p><ul><li><p>Common accessibility concepts:</p><ul><li><p>Appropriate labels for UI elements</p><ul><li><p>Add meaningful <code>contentDescription</code> to UI elements like <code>Image</code> or <code>Icon</code>.</p></li></ul></li><li><p>Proper color contrast</p><ul><li><p>Maintain a high contrast ratio between text and background.</p></li><li><p>Avoid conveying information using color alone.</p></li></ul></li><li><p>Sufficiently large touch targets</p><ul><li><p>Ensure buttons and touchable elements have a minimum size of 48x48dp.</p></li></ul></li><li><p>Dynamic Text Sizes</p><ul><li><p>Support system font scaling (use <code>sp</code> for text sizes)</p></li></ul></li></ul></li><li><p>Advanced accessibility concepts:</p><ul><li><p>Custom accessibility actions</p><ul><li><p>Ensure all functionality is accessible via keyboard or switch access.</p></li><li><p>Ensure proper navigation order and focus handling for all interactive elements.</p></li></ul></li><li><p>Implementing custom descriptions</p></li></ul></li><li><p>Testing for accessibility</p><ul><li><p>Accessibility Testing Framework</p></li><li><p>Android Studio checks</p></li><li><p>Accessibility scanner</p></li><li><p>Espresso</p></li><li><p>Roboelectric</p></li><li><p>Manual testing with Talkback and Switch Access</p></li></ul></li></ul><h3>4. How does Accessibility Services work in Android?</h3><p>An Accessibility Service is a service that runs in the background and responds to the Accessibility Events like tap, focus, tap and hold, two finger swipe, swipe right, etc. </p><p>The screen/window content is arranged in a tree and each node in the tree is represented as an <code>AccessibilityNodeInfo</code>. </p><p>Each <code>AccessibilityNodeInfo</code> contains information like <a href="https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html">Name, Role, Value, and Action</a> associated with that particular view.</p><p><code>AccessibilityNodeInfo </code>are then used by this service to facilitate accessible feature whenever the devices turns on the accessible modes.</p><p>However, there are others that doesn&#8217;t directly rely on these services such as contrast, text size which are now part of display settings but falls into the category of accessibility.</p><p>In my previous blog, I covered basics of accessibility in Compose. Feel free to read them here. </p><div class="embedded-post-wrap" data-attrs="{&quot;id&quot;:147901636,&quot;url&quot;:&quot;https://androidnomad.substack.com/p/android-nomad-33-accessibility&quot;,&quot;publication_id&quot;:1007931,&quot;publication_name&quot;:&quot;Android Nomad&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;title&quot;:&quot;Android Nomad - #33 Accessibility&quot;,&quot;truncated_body_text&quot;:&quot;As mobile developers, it's our responsibility to ensure that our apps are accessible to all users, regardless of their abilities. With the introduction of Jetpack Compose, Google's modern toolkit for building native Android UI, we have powerful new tools at our disposal to create more accessible applications. In this post, we'll explore how Jetpack Comp&#8230;&quot;,&quot;date&quot;:&quot;2024-08-19T22:24:21.404Z&quot;,&quot;like_count&quot;:0,&quot;comment_count&quot;:0,&quot;bylines&quot;:[{&quot;id&quot;:2161969,&quot;name&quot;:&quot;Sido&quot;,&quot;handle&quot;:&quot;androidnomad&quot;,&quot;previous_name&quot;:&quot;SidoPillai&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdfee6a4-b881-4f20-9779-7ed909fdeea5_1032x1034.jpeg&quot;,&quot;bio&quot;:&quot;Android Engineer &quot;,&quot;profile_set_up_at&quot;:&quot;2022-05-20T15:41:03.959Z&quot;,&quot;publicationUsers&quot;:[{&quot;id&quot;:953470,&quot;user_id&quot;:2161969,&quot;publication_id&quot;:1007931,&quot;role&quot;:&quot;admin&quot;,&quot;public&quot;:true,&quot;is_primary&quot;:false,&quot;publication&quot;:{&quot;id&quot;:1007931,&quot;name&quot;:&quot;Android Nomad&quot;,&quot;subdomain&quot;:&quot;androidnomad&quot;,&quot;custom_domain&quot;:null,&quot;custom_domain_optional&quot;:false,&quot;hero_text&quot;:&quot;Reading club for Android Developers. &quot;,&quot;logo_url&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;author_id&quot;:2161969,&quot;theme_var_background_pop&quot;:&quot;#6C0095&quot;,&quot;created_at&quot;:&quot;2022-07-19T15:24:08.766Z&quot;,&quot;rss_website_url&quot;:null,&quot;email_from_name&quot;:null,&quot;copyright&quot;:&quot;SidoPillai&quot;,&quot;founding_plan_name&quot;:null,&quot;community_enabled&quot;:true,&quot;invite_only&quot;:false,&quot;payments_state&quot;:&quot;disabled&quot;,&quot;language&quot;:null,&quot;explicit&quot;:false,&quot;is_personal_mode&quot;:false}}],&quot;twitter_screen_name&quot;:&quot;SidoPillai&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;utm_campaign&quot;:null,&quot;belowTheFold&quot;:true,&quot;type&quot;:&quot;newsletter&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="EmbeddedPostToDOM"><a class="embedded-post" native="true" href="https://androidnomad.substack.com/p/android-nomad-33-accessibility?utm_source=substack&amp;utm_campaign=post_embed&amp;utm_medium=web"><div class="embedded-post-header"><img class="embedded-post-publication-logo" src="https://substackcdn.com/image/fetch/$s_!FQnk!,w_56,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" loading="lazy"><span class="embedded-post-publication-name">Android Nomad</span></div><div class="embedded-post-title-wrapper"><div class="embedded-post-title">Android Nomad - #33 Accessibility</div></div><div class="embedded-post-body">As mobile developers, it's our responsibility to ensure that our apps are accessible to all users, regardless of their abilities. With the introduction of Jetpack Compose, Google's modern toolkit for building native Android UI, we have powerful new tools at our disposal to create more accessible applications. In this post, we'll explore how Jetpack Comp&#8230;</div><div class="embedded-post-cta-wrapper"><span class="embedded-post-cta">Read more</span></div><div class="embedded-post-meta">2 years ago &#183; Sido</div></a></div><h3>5. Accessibility Role</h3><p>An accessibility role helps users of assistive technologies to understand the purpose of elements on the screen.</p><p><strong>Here are some accessibility roles in Jetpack Compose:</strong></p><ul><li><p><strong>Button</strong>: Indicates that an element is a button</p></li><li><p><strong>Checkbox</strong>: Indicates that an element is a checkbox with two states (checked/unchecked)</p></li><li><p><strong>DropdownList</strong>: Indicates that an element is a drop-down menu</p></li><li><p><strong>Image</strong>: Indicates that an element is an image</p></li><li><p><strong>RadioButton</strong>: Indicates that an element is a radio button</p></li><li><p><strong>Switch</strong>: Indicates that an element is a switch</p></li><li><p><strong>Tab</strong>: Indicates that an element is a tab that represents a single page of content</p></li><li><p><strong>ValuePicker</strong>: Indicates that an element is a value picker that should support accessibility scroll events&nbsp;</p></li></ul><div><hr></div><h3>6. <strong>Semantic Markup</strong></h3><p>In compose, you can provide semantic markups via a <code>Modifier </code>to a UI element, this provision makes a UI element compatible with assistive features</p><ul><li><p><code>contentDescription </code>to provide a meaningful description for the screen reader for a particular UI element for e.g. <code>Button, Icon, Box etc. </code></p><p>In Jetpack Compose, you can use the <code>contentDescription</code> or <code>text</code> properties to set an accessibility label. <code>ContentDescription</code> is used for more visual elements, like icons and images. <code>Text</code> is used for text elements.</p></li></ul><pre><code><strong>// set contentDescription
Box(modifier = Modifier.semantics {
    contentDescription = "Appt"
}) {
    // Box content...
}

// set text
Box(modifier = Modifier.semantics {
    text = AnnotatedString("Appt")
}) {
    // Box content...
}</strong></code></pre><ul><li><p><code>liveRegion </code>to add awareness to assisted screen readers of important changes in content through accessibility announcements, for e.g. a state change of an UI element</p><pre><code>var isFavorite = remember <strong>{ </strong><em>mutableStateOf</em>(false) <strong>}

</strong>Icon(
    modifier = Modifier
        .<em>wrapContentSize</em>()
        .<em>semantics </em><strong>{
            </strong><em>liveRegion </em>= LiveRegionMode.Polite
        <strong>}
        </strong>.<em>clickable </em><strong>{
            </strong>// Handle on Click
        <strong>}</strong>,
    imageVector = if (isFavorite.value) {
        Icons.Rounded.<em>Favorite
    </em>} else {
        Icons.Rounded.<em>FavoriteBorder
    </em>},
    contentDescription = if (isFavorite.value) {
        "Favorite"
    } else {
        "Not favorite"
    }
)</code></pre><p><strong>LiveRegionMode.Polite</strong> - waits for the speech announcement in progress to complete</p><p><strong>LiveRegionMode.Assertive</strong> - interrupts ongoing speech to immediately announce changes</p></li><li><p><code>mergeDescendants - </code>group all elements within this parent element and they will be focused and read out together.</p></li></ul><pre><code>// Merge all semantics of box elements
Box(modifier = Modifier
    .semantics(mergeDescendants = true) { }
) {
    // Box content ...
}</code></pre><ul><li><p><code>customActions - </code>provide a custom action for a UI element</p></li></ul><pre><code>Button(
    onClick = { /* Your click handler */ },
    modifier = Modifier
        .wrapContentSize()
        .semantics {
            customActions = listOf(
                // your custom action
                CustomAccessibilityAction(label = "Add bookmark") {
                    // Bookmark logic
                    true
                }
            )
        }
) {
    Text("Bookmark button")
}</code></pre><p>Furthermore, there are several ways to override labels for default actions. For <code>Composables</code> that expose the <code>onClick</code> parameter, specify the <code>label</code> inside the <code>semantics</code> block modifier. The <code>SemanticsActions</code> object provides a list of all predefined accessibility actions.</p><pre><code>// override label for button
val buttonClickHandler: () -&gt; Unit = { /* Your click handler */ }
Button(
    onClick = buttonClickHandler,
    modifier = Modifier
        .semantics {
            onClick(label = "Add bookmark") {
                buttonClickHandler.invoke()
                true
            }
        }
) {
    // Button content
}</code></pre><ul><li><p>Managing Focus</p><p></p><p>In Jetpack Compose, you can use the <code>contentDescription</code> to hide an element (like an image) from assistive technologies by setting it to null.</p><pre><code>// Set contentDescription to null
Image(
    painter = /* your Painter */,
    contentDescription = null,
)</code></pre><p>If <code>Composable</code> doesn't expose the <code>contentDescription</code> property, you can use the <code>invisibleToUser</code> property inside the <code>semantics</code> block modifier, to hide an element from assistive technologies.</p><p></p><pre><code>// Make element invisible for assistive technologies
Text(
    text = "",
    modifier = Modifier.semantics {
        invisibleToUser()
    }
)</code></pre><p>If, on the contrary, you want to make a certain element focusable, use the <code>focusable</code> modifier. By design, some <code>Composables</code> are focusable, such as a <code>Button</code> or a <code>Composable</code> with the <code>clickable</code> modifier attached to it.</p></li></ul><pre><code>// Make element focusable
Box(modifier = Modifier.focusable()) {
    // Box content...
}</code></pre><p>In Jetpack Compose, you can use the <code>onFocusChanged</code> modifier to listen for focus changes. It contains a property such, as <code>isFocused</code>, to check if composable currently has focus.</p><pre><code>Button(
    onClick = { /* Your click handling */ },
    modifier = Modifier
        .onFocusChanged {
            if (it.isFocused) {
                // logic in case view is focused
            }
        }
) {
    // Button content ...
}</code></pre><ul><li><p><code>panelTitle</code></p></li></ul><p>A modal overlays the screen with additional content. Modals are usually indicated visually, e.g. by throwing a shadow on the content below it. Users of assistive technologies also need to know when content is part of a modal.</p><p>In Jetpack Compose, to set a title for an accessibility pane, you can use <code>paneTitle</code> inside <code>semantics</code> block modifier.</p><p>It's recommended to set the pane title for high-level layouts, such as <code>Scaffold</code>.</p><pre><code>Scaffold(modifier = Modifier.semantics {
    paneTitle = "Appt pane"
}) { paddingValues -&gt;
    // Scaffold content...
}</code></pre><ul><li><p><code>traversalGroup - </code>This will let assistive technologies know that this section is grouped and should be traversed, before moving on to a next section. Then add <code>traversalIndex</code> to elements in this group to fix any issues with the focus order.</p><p></p><p>In Jetpack Compose, you can use the <code>traversalIndex</code> to alter the focus order of the screen. The <code>traversalIndex</code> will give assistive technologies an explicit order of traversing. When a section of a screen is read out in an incorrect order, start by adding <code>isTraversalGroup</code> to the parent <code>Column</code>, <code>Row</code> or <code>Box</code>. </p><p></p><p>It is possible to use <code>isTraversalGroup</code> and <code>traversalIndex</code> on the same element.</p></li></ul><pre><code>Box(modifier = Modifier.semantics {
    isTraversalGroup = true
    traversalIndex = -1f
}) {
    // Box content...
}</code></pre><ul><li><p><code>stateDescription - </code>An accessibility state helps users of assistive technologies to understand the state of elements on the screen. </p></li></ul><pre><code>Box(
    modifier = Modifier.semantics {
        // Custom state
        stateDescription = "Expanded"

        // Selected
        selected = true
    }
)</code></pre><h3>7. Keyboard Navigation</h3><p>By adjusting the keyboard order, you can provide a great experience for users that control their device using a hardware keyboard.</p><pre><code>@Composable
fun CustomFocusTraversal() {
    // Create set of references for each Composable
    val (first, second, third, fourth) = remember { FocusRequester.createRefs() }

    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        // First Button
        Button(
            onClick = { },
            modifier = Modifier
                .focusRequester(first)
                .focusProperties {
                    down = second
                }
        ) {
            Text("Button 1")
        }

        Spacer(modifier = Modifier.height(16.dp))

        // Second Button
        Button(
            onClick = { },
            modifier = Modifier
                .focusRequester(second)
                .focusProperties {
                    up = first
                    down = third
                }
        ) {
            Text("Button 2")
        }

        Spacer(modifier = Modifier.height(16.dp))

        // Third Button
        Button(
            onClick = { },
            modifier = Modifier
                .focusRequester(third)
                .focusProperties {
                    up = second
                    down = fourth
                }
        ) {
            Text("Button 3")
        }

        Spacer(modifier = Modifier.height(16.dp))

        // Fourth Button
        Button(
            onClick = { },
            modifier = Modifier
                .focusRequester(fourth)
                .focusProperties {
                    up = third
                    right = first
                }
        ) {
            Text("Button 4")
        }
    }

    // Set initial focus
    LaunchedEffect(Unit) {
        first.requestFocus()
    }
}
</code></pre><div><hr></div><h3>8. <strong>Localization and Language</strong></h3><ul><li><p>Support multiple languages.</p></li><li><p>Ensure text directions (LTR or RTL) are handled appropriately.</p></li><li><p>Assistive technologies, such as the screen reader, use the locale for the pronunciation of utterances. It is important to explicitly set a locale for your app. An incorrect locale leads to unclear pronunciation. Also, setting a locale can help with displaying characters correctly.</p><pre><code>val context = LocalContext.current
val localizedContext = remember {
    val locales = LocaleList.forLanguageTags("nl-NL")
    val configuration = context.resources.configuration
    configuration.setLocales(locales)
    context.createConfigurationContext(configuration)
}
val localizedString = localizedContext.resources.getString(R.string.appt)

Text(text = localizedString)</code></pre></li></ul><div><hr></div><p>By considering these aspects, you can create a more inclusive experience, ensuring your app is accessible to all users.</p><div><hr></div><h3>Resources:</h3><ol><li><p><a href="https://developer.android.com/develop/ui/compose/accessibility">Accessibility for Compose</a></p></li><li><p><a href="https://developer.android.com/guide/topics/ui/accessibility/principles">Principles for improving app&#8217;s accessibility</a></p></li><li><p><a href="https://developer.android.com/guide/topics/ui/accessibility/testing">Testing for accessibility</a></p></li><li><p><a href="https://appt.org/en/docs/jetpack-compose">Appt</a></p></li></ol>]]></content:encoded></item><item><title><![CDATA[Android Nomad #48 - Android Interview ]]></title><description><![CDATA[Tips for preparing for android interview]]></description><link>https://www.androidnomad.com/p/android-nomad-48-android-interview</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-48-android-interview</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Tue, 12 Nov 2024 15:01:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3><strong>Android domain interview</strong>: </h3><p>This interview comprises questions related to Android specific development. I used this Github repository to prepare for this interview <strong><a href="https://github.com/amitshekhariitbhu/android-interview-questions">https://github.com/amitshekhariitbhu/android-interview-questions</a></strong></p><h4><strong>System Design:</strong></h4><p>Following are the resources and guidelines that I used to prepare for Meta Android Design interview</p><ul><li><p>General guidelines:</p><ul><li><p><strong><a href="https://github.com/weeeBox/mobile-system-design">https://github.com/weeeBox/mobile-system-design</a></strong></p></li><li><p><strong><a href="https://proandroiddev.com/a-simple-framework-for-mobile-system-design-interviews-89f6f4134b84">https://proandroiddev.com/a-simple-framework-for-mobile-system-design-interviews-89f6f4134b84</a></strong></p></li></ul></li><li><p>Mock System Design Interviews:&nbsp;<strong><a href="https://www.youtube.com/@alementuev">https://www.youtube.com/@alementuev</a></strong>&nbsp;The feedback part at the end is enlightening.</p></li><li><p>Instagram Android Engineering:&nbsp;<strong><a href="https://instagram-engineering.com/tagged/android">https://instagram-engineering.com/tagged/android</a></strong></p></li><li><p>Uber Android System Design:&nbsp;</p></li></ul><div id="youtube2-SbXDhAKgWek" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;SbXDhAKgWek&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/SbXDhAKgWek?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><ul><li><p>Modern Android App Architecture (Very important):&nbsp;<strong><a href="https://developer.android.com/courses/pathways/android-architecture">https://developer.android.com/courses/pathways/android-architecture</a></strong></p></li></ul><p>Here are topic specific links that I found very useful</p><ul><li><p>Pagination:&nbsp;<strong><a href="https://nordicapis.com/everything-you-need-to-know-about-api-pagination/">https://nordicapis.com/everything-you-need-to-know-about-api-pagination/</a></strong>&nbsp;<strong><a href="https://uxdesign.cc/why-facebook-says-cursor-pagination-is-the-greatest-d6b98d86b6c0">https://uxdesign.cc/why-facebook-says-cursor-pagination-is-the-greatest-d6b98d86b6c0</a></strong></p></li><li><p>Web Sockets:&nbsp;<strong><a href="https://ably.com/topic/websockets">https://ably.com/topic/websockets</a></strong></p></li><li><p>Build for Billions (very important if you are interviewing for a big tech company):&nbsp;<strong><a href="https://developer.android.com/docs/quality-guidelines/build-for-billions">https://developer.android.com/docs/quality-guidelines/build-for-billions</a></strong></p></li><li><p>Memory:&nbsp;<strong><a href="https://developer.android.com/topic/performance/memory-overview">https://developer.android.com/topic/performance/memory-overview</a></strong></p></li></ul><p>To ace a Mobile System Design interview, these are the topics I recommend studying and finding examples for:</p><ul><li><p>Networking and communication protocols (REST, WebSockets, Server-Sent Events, Push Notifications, etc.)</p></li><li><p>Storage (Relational Database, File storage, Key-value based)</p></li><li><p>Data Management (Offline storage, Caching, Pre-fetching data, Persistent/In-memory storage, Services/Work Manager)</p></li><li><p>Feature Development (Modularization, Dependency Injection, Testing, Accessibility, Localization)</p></li></ul><p>Try to find pros and cons for the following and come up with why would you go with the one over the other</p><ul><li><p>MVVM vs MVC vs MVP vs MVI design pattern</p></li><li><p>Single Activity vs Multi-Activity architecture pattern</p></li><li><p>Different Modularization techniques</p></li><li><p>Push-based vs pull-based database synchronization</p></li><li><p>In-memory storage vs persistent storage</p></li><li><p>XML vs Compose</p></li></ul><p>App scalability topics (Important for apps with billions of users)</p><ul><li><p>Battery consumption</p></li><li><p>Building for different network bandwidth</p></li><li><p>Accessibility</p></li><li><p>Localization</p></li><li><p>App profiling (CPU, Network and Battery profiling)</p></li></ul><p>This is my approach while tacking an Android System Design problem:</p><ul><li><p>What are we building (Client app, API, BE or both (client plus BE))?</p></li><li><p>Whom are we building this for (target users, target market, etc)?</p></li><li><p>Functional Requirements</p></li><li><p>Non-functional requirements</p></li><li><p>Defining the network communication protocol on how the app would get data and then defining the APIs</p></li><li><p>Defining the app architecture</p></li><li><p>Deep diving in any of the feature</p></li><li><p>Scalability</p></li><li><p>Privacy and Security</p></li></ul><p><strong>Mindset that help me build an end to end system</strong>: Thinking as if I am using the app and coming up with different scenarios and then discussing possible solution like</p><ul><li><p>What happens when the user goes on an airplane mode?</p></li><li><p>What happens when the user removes the app from the background and then starts it again?</p></li><li><p>What happens when the user uninstalls the app and re-installs it?</p></li><li><p>What happens when a call or an alarm interrupts the app?</p></li></ul><p><strong>Behavioral Interview</strong>: I kept ready multiple work-related stories from my past work experiences and did few mock interviews to answer questions in STAR (Situation Task Action Result) format. I used the guidelines from the educative behavioral course.<br></p><p>You can also read the interview blueprint to help you plan for an interview. </p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;f201ff84-3d66-47b3-a914-5cb429d40181&quot;,&quot;caption&quot;:&quot;Developer Interview Loop:&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Android Nomad #45 - Developer Interview Loop&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2161969,&quot;name&quot;:&quot;Sido&quot;,&quot;bio&quot;:&quot;Android Engineer &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdfee6a4-b881-4f20-9779-7ed909fdeea5_1032x1034.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2024-10-31T16:01:12.051Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://androidnomad.substack.com/p/android-nomad-45-developer-interview&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:150842111,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Android Nomad&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Hope this helps. Thank you!</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #47 - Threading Models Advanced]]></title><description><![CDATA[Multithreading concepts in Kotlin]]></description><link>https://www.androidnomad.com/p/android-nomad-48-threading-models</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-48-threading-models</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Thu, 07 Nov 2024 17:01:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In modern Kotlin development, understanding multithreading is essential for creating high-performance applications that can handle concurrent tasks efficiently. Multithreading enables the execution of multiple tasks simultaneously, making your application more responsive and able to take full advantage of modern multicore processors. In this guide, we'll dive deep into key multithreading concepts in Kotlin and provide code examples to illustrate how each concept works.</p><p></p><h3>1. Atomic Assignments</h3><p>Atomic assignments ensure that a variable update is indivisible, meaning it cannot be interrupted mid-operation by another thread.</p><p>In Kotlin, atomic variables are available in the <code>Atomic*</code> classes from <code>kotlinx.atomicfu</code> or Java&#8217;s <code>java.util.concurrent.atomic</code> package:</p><pre><code>import java.util.concurrent.atomic.AtomicInteger

fun main() {
    val atomicCounter = AtomicInteger(0)

    // Atomic increment
    val newValue = atomicCounter.incrementAndGet()
    println("Atomic Incremented Value: $newValue")
}</code></pre><p>This ensures that incrementing <code>atomicCounter</code> is thread-safe and can be used safely in concurrent environments.</p><p></p><h3>2. Thread Safety &amp; Synchronized</h3><p>When multiple threads access shared data, thread safety must be ensured to avoid unpredictable behavior. The <code>synchronized</code> block in Kotlin helps achieve this by allowing only one thread to access a critical section at a time.</p><pre><code>val lock = Any()
var counter = 0

fun increment() {
    synchronized(lock) {
        counter++
    }
}</code></pre><p>Here, only one thread can increment the <code>counter</code> at a time, preventing race conditions.</p><p></p><h3>3. Wait &amp; Notify</h3><p>In Kotlin, <code>wait</code> and <code>notify</code> can be used with the <code>synchronized</code> block for inter-thread communication. <code>wait</code> pauses a thread until another thread calls <code>notify</code>, signaling it to resume.</p><pre><code>val lock = Object()

fun waitingThread() {
    synchronized(lock) {
        println("Waiting...")
        lock.wait() // Release lock and wait
        println("Resumed after notification")
    }
}

fun notifyingThread() {
    synchronized(lock) {
        println("Notifying...")
        lock.notify() // Notify the waiting thread
    }
}</code></pre><p>This is useful when one thread must wait for a certain condition to be met by another thread.</p><h3>4. Interrupting Threads</h3><p>Threads can be interrupted to stop or redirect them to other tasks. Kotlin provides an <code>isInterrupted</code> flag and the <code>interrupt()</code> method for this.</p><pre><code>val thread = Thread {
    while (!Thread.currentThread().isInterrupted) {
        println("Working...")
        Thread.sleep(1000)
    }
}

fun main() {
    thread.start()
    Thread.sleep(3000)
    thread.interrupt() // Interrupt the thread
}</code></pre><p>This stops the <code>thread</code> gracefully after 3 seconds.</p><h3>5. Volatile</h3><p>The <code>volatile</code> keyword ensures that updates to a variable are immediately visible to all threads, preventing caching issues across CPU cores.</p><pre><code>@Volatile
var isRunning = true

val thread = Thread {
    while (isRunning) {
        println("Running...")
    }
}

fun main() {
    thread.start()
    Thread.sleep(2000)
    isRunning = false // Stop the thread
}</code></pre><p>Without <code>volatile</code>, changes to <code>isRunning</code> may not be visible immediately to the running thread due to caching.</p><div><hr></div><h3>6. Reentrant Locks &amp; Conditional Variables</h3><p>Reentrant locks allow a thread to re-enter a lock it already holds. Condition variables (<code>newCondition()</code>) help manage threads waiting for specific conditions.</p><pre><code>import java.util.concurrent.locks.ReentrantLock

val lock = ReentrantLock()
val condition = lock.newCondition()

fun waitingTask() {
    lock.lock()
    try {
        println("Waiting for condition...")
        condition.await() // Wait until signaled
        println("Condition met, resuming...")
    } finally {
        lock.unlock()
    }
}

fun signalingTask() {
    lock.lock()
    try {
        println("Signaling condition...")
        condition.signal() // Signal waiting threads
    } finally {
        lock.unlock()
    }
}</code></pre><p>This setup allows greater flexibility for complex waiting conditions.</p><h3>7. Missed Signals</h3><p>Missed signals happen when <code>notify</code> or <code>signal</code> is called before a thread begins waiting. Avoid this by controlling timing, ensuring that waiting threads start before signaling threads.</p><p></p><h3>8. Semaphores</h3><p>Semaphores control the number of threads that can access a resource at the same time. <code>Semaphore</code> from <code>java.util.concurrent</code> can limit concurrent access.</p><pre><code>import java.util.concurrent.Semaphore

val semaphore = Semaphore(3) // Allows 3 threads concurrently

fun accessResource() {
    semaphore.acquire()
    try {
        println("Resource accessed by ${Thread.currentThread().name}")
    } finally {
        semaphore.release()
    }
}</code></pre><p>This limits resource access to three threads at any time.</p><p></p><h3>9. Spurious Wakeups</h3><p>Spurious wakeups occur when a thread unexpectedly wakes up without receiving a notification. To handle these, place <code>wait()</code> calls in a loop that checks the condition repeatedly.</p><pre><code>val lock = Object()
var condition = false

fun safeWait() {
    synchronized(lock) {
        while (!condition) {
            lock.wait()
        }
        println("Condition met!")
    }
}</code></pre><p>This loop ensures the thread only proceeds if <code>condition</code> is actually met.</p><div><hr></div><h3>10. Atomic Classes</h3><p>Kotlin provides atomic classes (like <code>AtomicInteger</code>, <code>AtomicReference</code>) that perform atomic operations on variables without needing synchronization.</p><pre><code>import java.util.concurrent.atomic.AtomicReference

data class Person(val name: String, val age: Int)

val atomicPerson = AtomicReference(Person("Alice", 25))

fun updatePerson() {
    atomicPerson.updateAndGet { person -&gt; person.copy(age = person.age + 1) }
    println("Updated person: ${atomicPerson.get()}")
}</code></pre><p>This is thread-safe and doesn&#8217;t require explicit locks.</p><div><hr></div><h3>11. Non-Blocking Synchronizations</h3><p>Non-blocking synchronization avoids traditional locks by using atomic classes or <code>compareAndSet</code> operations. This allows threads to retry operations without blocking.</p><pre><code>val atomicCounter = AtomicInteger(0)

fun incrementCounter() {
    var current: Int
    do {
        current = atomicCounter.get()
    } while (!atomicCounter.compareAndSet(current, current + 1))
    println("Non-blocking increment to: ${atomicCounter.get()}")
}</code></pre><p>This pattern, known as "optimistic locking," retries until successful, ensuring thread safety without traditional locks.</p><p></p><p>Understanding multithreading in Kotlin is key for high-performance application development. Concepts like atomic assignments, synchronization mechanisms, <code>wait</code>/<code>notify</code>, <code>volatile</code>, and non-blocking synchronization can help you design more efficient and safer multithreaded applications. Leveraging Kotlin&#8217;s atomic classes and structured concurrency can simplify complex concurrent code, making it easier to manage and debug. Armed with these concepts, you&#8217;re ready to build responsive, concurrent Kotlin applications!</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #46 - Threading Models in Kotlin]]></title><description><![CDATA[Basics of threading concepts handy for developers.]]></description><link>https://www.androidnomad.com/p/android-nomad-46-threading-models</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-46-threading-models</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 04 Nov 2024 17:01:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the world of Kotlin, efficient handling of multithreading and concurrency is essential for creating responsive, high-performing applications. Whether you&#8217;re developing mobile apps, backend services, or high-performance software, understanding Kotlin&#8217;s threading models and concurrency mechanisms is critical. This article will cover core concepts, such as processes and threads, concurrency vs. parallelism, multitasking types, synchronization strategies, and more.</p><p></p><h3>Process vs. Threads</h3><p>In operating systems, <strong>processes</strong> and <strong>threads</strong> are basic units of execution:</p><ul><li><p><strong>Process</strong>: A process is an independent program in execution, with its own allocated memory space and system resources. Processes can have multiple threads but do not share memory with other processes. Switching between processes is typically more costly because each process has its own memory space.</p></li><li><p><strong>Thread</strong>: A thread is the smallest unit of execution within a process. Multiple threads can exist within a single process, sharing the same memory space. This sharing enables faster communication and context switching, allowing threads to perform tasks simultaneously within the same application.</p></li></ul><p>In Kotlin, the concept of threads is used extensively, especially in concurrency programming with coroutines, where lightweight, efficient threads simplify concurrency.</p><p></p><h3>Concurrency vs. Parallelism</h3><p>Though often used interchangeably, <strong>concurrency</strong> and <strong>parallelism</strong> have distinct meanings:</p><ul><li><p><strong>Concurrency</strong>: This refers to tasks that are in progress at the same time, although not necessarily being executed simultaneously. Concurrency is about managing multiple tasks at once and can involve task switching on a single core.</p></li><li><p><strong>Parallelism</strong>: Parallelism involves executing multiple tasks simultaneously on multiple cores. While concurrency focuses on structure and task management, parallelism leverages hardware capabilities to perform tasks simultaneously, speeding up overall execution.</p></li></ul><p>In Kotlin, coroutines provide a structured way to manage concurrency without the complexity of traditional thread management. They can switch between tasks efficiently, making it easier to achieve concurrent programming.</p><p></p><h3>Cooperative Multitasking vs. Preemptive Multitasking</h3><p>Multitasking involves managing multiple tasks on a single CPU core. There are two primary types:</p><ul><li><p><strong>Cooperative Multitasking</strong>: In this model, tasks voluntarily yield control, allowing other tasks to run. The system relies on tasks to cooperate by relinquishing control regularly. While simpler, it can lead to inefficiencies if one task doesn&#8217;t yield.</p></li><li><p><strong>Preemptive Multitasking</strong>: The operating system controls task execution, switching between tasks at regular intervals, or preemptively. This approach is more complex but efficient for handling uncooperative or long-running tasks.</p></li></ul><p>Kotlin&#8217;s coroutines support both types, with structured concurrency allowing efficient task switching that resembles preemptive multitasking without directly involving OS-level threads.</p><p></p><h3>Synchronous vs. Asynchronous</h3><p>In programming, <strong>synchronous</strong> and <strong>asynchronous</strong> execution define how tasks are scheduled and executed.</p><ul><li><p><strong>Synchronous</strong>: Tasks execute in a strict sequence, where each task waits for the previous one to finish. This approach is straightforward but can cause delays if a task takes time to complete.</p></li><li><p><strong>Asynchronous</strong>: Tasks can execute independently, without waiting for others to complete, allowing for more responsive applications. Asynchronous programming is essential for non-blocking operations, especially in Kotlin where <code>suspend</code> functions and coroutines make it easier to write non-blocking code.</p></li></ul><p></p><h3>I/O Bound vs. CPU Bound</h3><p>Tasks in programming are often categorized as either <strong>I/O-bound</strong> or <strong>CPU-bound</strong>:</p><ul><li><p><strong>I/O Bound</strong>: These tasks involve waiting on external resources like network requests or file I/O. Such tasks benefit from asynchronous processing, allowing other tasks to proceed without blocking.</p></li><li><p><strong>CPU Bound</strong>: These tasks require significant computation, utilizing the CPU intensively. CPU-bound tasks benefit from parallel processing on multiple cores to reduce execution time.</p></li></ul><p>Using Kotlin&#8217;s <code>Dispatchers.IO</code> for I/O-bound tasks and <code>Dispatchers.Default</code> for CPU-bound tasks is an effective way to manage these tasks efficiently.</p><p></p><h3>Throughput vs. Latency</h3><p>When evaluating performance, <strong>throughput</strong> and <strong>latency</strong> provide insights into the efficiency of task processing:</p><ul><li><p><strong>Throughput</strong>: This measures the number of tasks completed over a given time period. Higher throughput indicates better efficiency and task management.</p></li><li><p><strong>Latency</strong>: Latency refers to the time taken to complete a single task. Lower latency is essential for real-time systems where response time is crucial.</p></li></ul><p>Kotlin&#8217;s structured concurrency and coroutines can help balance throughput and latency by optimizing how tasks are managed and executed.</p><p></p><h3>Critical Sections vs. Race Conditions</h3><p>In multi-threaded programs, <strong>critical sections</strong> and <strong>race conditions</strong> are essential concepts to ensure data consistency and avoid conflicts.</p><ul><li><p><strong>Critical Section</strong>: This is a part of the code where shared resources are accessed by multiple threads. To avoid data inconsistency, only one thread should access the critical section at a time.</p></li><li><p><strong>Race Condition</strong>: This occurs when multiple threads try to modify shared resources simultaneously, leading to unpredictable outcomes. Synchronization mechanisms like locks are used to prevent race conditions.</p></li></ul><p>In Kotlin, <code>Mutex</code> and <code>Atomic*</code> variables help manage critical sections safely, allowing for thread-safe operations.</p><p></p><h3>Deadlocks, Liveness, and Reentrant Locks</h3><p>When dealing with locks and concurrency, understanding <strong>deadlocks</strong>, <strong>liveness</strong>, and <strong>reentrant locks</strong> is crucial.</p><ul><li><p><strong>Deadlock</strong>: This occurs when two or more threads wait indefinitely for resources held by each other, resulting in a standstill.</p></li><li><p><strong>Liveness</strong>: Liveness refers to the system&#8217;s ability to continue making progress. Deadlock prevention and liveness checks are necessary to keep the system running efficiently.</p></li><li><p><strong>Reentrant Lock</strong>: A reentrant lock allows a thread to reacquire a lock it already holds, preventing self-deadlocking. In Kotlin, reentrant locks (<code>ReentrantLock</code>) provide this functionality, allowing for better control in recursive methods or nested functions.</p></li></ul><p></p><h3>Mutex and Semaphore</h3><p><strong>Mutexes</strong> and <strong>semaphores</strong> are key synchronization tools:</p><ul><li><p><strong>Mutex (Mutual Exclusion)</strong>: A mutex allows only one thread to access a resource at a time, providing a simple mechanism to protect critical sections.</p></li><li><p><strong>Semaphore</strong>: A semaphore controls access to a resource pool with a defined limit on concurrent access. For example, a semaphore with a count of 5 would allow up to 5 threads to access the resource simultaneously.</p></li></ul><p>In Kotlin, <code>Mutex</code> and <code>Semaphore</code> are essential for handling concurrency, particularly when managing resources shared across multiple coroutines.</p><p></p><p>Threading models in Kotlin offer a comprehensive toolkit for building efficient, high-performance applications. By understanding concepts like processes and threads, cooperative and preemptive multitasking, synchronization mechanisms, and concurrency tools like coroutines, you can write code that scales well and performs optimally under various workloads. Whether handling I/O-bound tasks or CPU-intensive calculations, Kotlin&#8217;s concurrency model simplifies writing effective multithreaded applications, making it a powerful choice for modern development.</p><div><hr></div><p>With these foundations, you&#8217;re well-equipped to dive deeper into advanced concurrency patterns, optimizing both resource usage and performance in your Kotlin applications. Happy coding!</p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #45 - Developer Interview Loop]]></title><description><![CDATA[Interview Structure]]></description><link>https://www.androidnomad.com/p/android-nomad-45-developer-interview</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-45-developer-interview</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Thu, 31 Oct 2024 16:01:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Developer Interview Loop:</h3><ol><li><p>Coding Interview </p><p>&#8226; 60-90 minutes </p><p>&#8226; Focus: Data structures and algorithms</p></li><li><p>System Design Interview </p><p>&#8226; 45-60 minutes</p><p>&#8226; Focus: System design, API/product design, or object-oriented design</p></li><li><p>Behavioral Interview </p><p>&#8226; 45-60 minutes</p><p>&#8226; Focus: Cultural fit</p><p></p></li></ol><h3>Signals for each round:</h3><ol><li><p>Coding Interview</p><ol><li><p>Can code</p></li><li><p>Pseudo code - Can think through a problem (not necessarily solve them)</p></li><li><p>Can communicate clearly</p></li></ol></li><li><p>System Design Interview</p><ol><li><p>Methodical</p></li><li><p>Getting Functional and Non Functional Requirements right!!! </p></li><li><p>Go deep in critical areas - Decides your leveling </p></li><li><p>Provide more than one solution and talk through pros and cons (senior level)</p></li></ol></li><li><p>Behavioral Interview</p><ol><li><p>This is infact a reverse interview round, you get to ask a lot of questions.</p></li><li><p>You are talking to your future teammates, therefore, I emphasize on asking meaningful questions about work and not generic ones that you can find on the web.</p></li><li><p>This is the most important round, unless you get a yes in this round, it doesn&#8217;t matter how you performed in previous rounds.</p></li></ol></li></ol><p></p><h3>Preparation:</h3><ol><li><p>DSA </p><ol><li><p>Leetcode</p></li><li><p>Neetcode</p></li></ol></li><li><p>System Design</p></li><li><p>Behavioral</p><ol><li><p>Cultivate string mental models</p></li><li><p>Improve speech</p></li></ol></li></ol><p></p><h3>Speech</h3><p>This is the most difficult but of all the above, this is often easiest to master as it only requires practice and developing good muscle memory.</p><p>I have created a framework named CDC</p><ol><li><p><strong>C</strong>onfidence</p></li><li><p><strong>D</strong>elivery</p></li><li><p><strong>C</strong>larity</p></li></ol><p>I see many native and non-native English speaker struggle with clarity, this often comes from one of the two things, not having deep understanding of a subject or unable to distill thoughts into words. Trust me the latter is much difficult and most struggle with.</p><p>Here is an exercise, you can start with and will notice results in just 10 days as you muster DSA and System Design prep.</p><p>Every day for 3 mins record yourself on webcam and talk about </p><ol><li><p>Your favorite product</p></li><li><p>Explain your CV</p></li><li><p>Explain your vision</p></li><li><p>A value you would share with a child as a basis of a good life</p></li></ol><p>Confidence comes from preparation &#8594; Delivery leads to clarity</p><p>All the best :) </p><p></p>]]></content:encoded></item><item><title><![CDATA[Android Nomad #44 - FAQ's for Mobile System Design]]></title><description><![CDATA[10 common areas of system design]]></description><link>https://www.androidnomad.com/p/android-nomad-44-faqs-for-mobile</link><guid isPermaLink="false">https://www.androidnomad.com/p/android-nomad-44-faqs-for-mobile</guid><dc:creator><![CDATA[Sido]]></dc:creator><pubDate>Mon, 28 Oct 2024 16:01:30 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/dc852cad-eac9-4cde-aacc-6f4195123d07_1792x1024.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While every system is unique, you can use this as a blueprint as a starting point for most commonly asked system design interviews. </p><ol><li><p>Communication (Text/Audio/Video/Email)</p><ol><li><p>Snapchat</p></li><li><p>WhatsApp (audio or video calling/real-time chat)</p></li><li><p>Gmail</p></li></ol></li><li><p>Streaming</p><ol><li><p>Instagram/Facebook Live (real-time)</p></li><li><p>Netflix/Hulu etc..</p></li><li><p>Spotify</p></li></ol></li><li><p>Ride-share/Maps</p><ol><li><p>Uber/UberEats</p></li><li><p>GoogleMaps</p></li></ol></li><li><p>Library management</p><ol><li><p>Image Loader</p></li><li><p>Compression/Decompression</p></li><li><p>Resource Loader</p></li><li><p>Networking</p></li><li><p>WorkManager (Threading)</p></li><li><p>Analytics</p></li><li><p>Caching</p><ol><li><p>Network</p></li><li><p>Memory</p></li><li><p>Disk</p></li></ol></li></ol></li><li><p>POS/ Payment/Banking services</p><ol><li><p>PayPal</p></li><li><p>Square</p></li><li><p>Banking app</p></li></ol></li><li><p>Booking/Live Events/Content</p><ol><li><p>Booking</p></li><li><p>TicketMaster</p></li><li><p>LiveNation</p></li><li><p>Zillow/Redfin</p></li></ol></li><li><p>Tracking/Notification</p><ol><li><p>Flight Tracker</p></li><li><p>Stock Manager (Robinhood)</p></li><li><p>Sport Score app</p></li><li><p>Betting apps</p></li><li><p>Okta Verification</p></li></ol></li><li><p>Device connectivity</p><ol><li><p>Garmin/Coros watch app</p></li><li><p>Mobile key (car/doors/etc..)</p></li><li><p>Smart home (Google Home)</p></li><li><p>All Trails/Strava</p></li></ol></li><li><p>Experience</p><ol><li><p>Disney</p></li><li><p>AirBnb</p></li><li><p>StoryMaps</p></li></ol></li><li><p>Health</p><ol><li><p>CVS</p></li><li><p>Apple Health</p></li><li><p>MyChart</p></li></ol></li></ol><p></p><p>For mobile/web/client engineers, this should cover pretty much 90% use cases for apps out there. </p><p>Meanwhile you can read system design for Instagram Stories, </p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;02287e76-ebad-4f5c-967f-7445b1df08ec&quot;,&quot;caption&quot;:&quot;In my previous System Design post, I covered how to approach a system design interview from an end to end system standpoint, a lot of readers reached out to me to create one similar for mobile system design a.k.a. client app design. This is applicable for both iOS and Android engineers.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Android Nomad - #36 Mobile System Design&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:2161969,&quot;name&quot;:&quot;Sido&quot;,&quot;bio&quot;:&quot;Android Engineer &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdfee6a4-b881-4f20-9779-7ed909fdeea5_1032x1034.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2024-09-19T16:00:39.001Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://androidnomad.substack.com/p/android-nomad-36-mobile-system-design&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:149077473,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;Android Nomad&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F70cc0f46-9225-4376-9496-ee3bfcc5b45b_1280x1280.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p></p><p>Hope this helps you to prepare for your interviews :) </p><div><hr></div><p>If you have any specific request or feedback feel free to drop me a message. </p><div class="directMessage button" data-attrs="{&quot;userId&quot;:2161969,&quot;userName&quot;:&quot;Sido&quot;,&quot;canDm&quot;:null,&quot;dmUpgradeOptions&quot;:null,&quot;isEditorNode&quot;:true}" data-component-name="DirectMessageToDOM"></div>]]></content:encoded></item></channel></rss>