<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://kgurgul.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://kgurgul.com/" rel="alternate" type="text/html" /><updated>2025-08-05T03:52:06+00:00</updated><id>https://kgurgul.com/feed.xml</id><title type="html">Personal blog</title><subtitle>Blog about technologies related to Kotlin and mobile</subtitle><author><name>Kamil Gurgul</name></author><entry><title type="html">Journey from Java to Kotlin Multiplatform</title><link href="https://kgurgul.com/cpu-info-migration/" rel="alternate" type="text/html" title="Journey from Java to Kotlin Multiplatform" /><published>2024-10-22T00:00:00+00:00</published><updated>2024-10-22T00:00:00+00:00</updated><id>https://kgurgul.com/cpu-info-migration</id><content type="html" xml:base="https://kgurgul.com/cpu-info-migration/"><![CDATA[<h2 id="cpu-info-a-personal-journey-of-my-project-its-migrations-and-its-architectures">CPU Info: A Personal Journey of My Project, Its Migrations, and Its Architectures</h2>

<p><strong><em>Overview</em></strong></p>

<p>Welcome to my personal retrospective on <strong>CPU Info</strong>, an open-source tool I created to provide in-depth hardware and software details on a range of devices—from smartphones and wearables to desktops and TVs, even the web via WASM. In this blog post, I’d like to share how I started the project, the significant migrations it underwent, and the choice of technologies and architectural patterns I introduced along the way.</p>

<hr />

<h3 id="1-early-history--origins">1. Early History &amp; Origins</h3>

<p>I launched <strong>CPU Info</strong> as a simple Android application to detect both hardware and software information on Android devices during development. My motivation came from wanting a deeper, hands-on understanding of how hardware and OS layers interact. Over time, I added features for GPU details, RAM usage, and temperature monitoring—turning the app into a go-to tool for enthusiasts who needed more capabilities than the default system settings.</p>

<p>These initial successes pushed me to expand the app beyond a single device type. While the early codebase was purely Android and reliant on Java, I began to imagine how CPU Info could serve different form factors and operating systems if the foundation was sufficiently modular.</p>

<hr />

<h3 id="2-tools-and-key-architectural-shifts">2. Tools and key architectural shifts</h3>

<p>TODO</p>

<hr />

<h3 id="3-expansion-to-additional-platforms">3. Expansion to Additional Platforms</h3>

<p>With CPU Info’s Android base solidified, I decided it was time to bring the same functionality to other devices. My solution was to leverage <strong>Kotlin Multiplatform (KMP)</strong> and reorganize the project into shared modules:</p>

<ul>
  <li><strong>Data Interactors</strong>: Shared logic for OS, memory, CPU, GPU, and sensor queries.</li>
  <li><strong>State Management</strong>: AndroidX Lifecycle ViewModels and StateFlow for consistent patterns across platforms.</li>
  <li><strong>Common UI</strong>: Compose usage for Android, Android TV, WearOS, iOS, Desktop, and WASM.</li>
</ul>

<h4 id="desktop-linux-macos-windows"><strong>Desktop (Linux, macOS, Windows)</strong></h4>
<ul>
  <li>By using Compose Multiplatform, I wrote a single UI layer that could run on desktop operating systems.</li>
  <li>For low-level system data, I turned to <a href="https://github.com/oshi/oshi">OSHI</a> to handle hardware calls.</li>
</ul>

<h4 id="ios"><strong>iOS</strong></h4>
<ul>
  <li>I developed an iOS module with CPU metrics, battery info, and orientation data, bridging the gap between iOS-specific APIs and my shared KMP modules.</li>
</ul>

<h4 id="wasmweb"><strong>WASM/Web</strong></h4>
<ul>
  <li>Deploying a web target required Compose support for WASM. Though hardware access is restricted in a browser, I still provided essential info like screen resolution, language data, and browser storage usage.</li>
</ul>

<h4 id="wear-os--android-tv"><strong>Wear OS &amp; Android TV</strong></h4>
<ul>
  <li>I created specialized UI flows for Wear OS and Android TV so users could quickly fetch device data on tiny watch displays or large TV screens.</li>
  <li>Shared logic among watch, TV, and phone versions minimized duplication and ensured consistent feature sets.</li>
</ul>

<hr />

<h3 id="4-challenges">4. Challenges</h3>

<ol>
  <li><strong>Feature Parity Across Platforms</strong>: Creating a smooth user experience on iOS, Android, desktop, and Web forced me to write platform-specific providers for certain system-level details.</li>
  <li><strong>Evolving Toolchains</strong>: Early adopters of Compose Multiplatform faced frequent changes; I had to adapt quickly whenever APIs shifted.</li>
  <li><strong>Full-Fledged CI</strong>: Maintaining test coverage and build pipelines for so many targets was complex, but the end result was a stable, well-tested product.</li>
  <li>TODO</li>
</ol>

<hr />

<h3 id="5-looking-ahead">5. Looking Ahead</h3>

<p>I’m excited about what’s next:</p>

<ul>
  <li>Deeper hardware detection.</li>
  <li>Possible additions like performance profiling and CPU benchmarking.</li>
  <li>Additional hardware targets (like Android Auto) or unusual form factors that can benefit from CPU Info’s robust detection capabilities.</li>
</ul>

<hr />

<h3 id="6-conclusion">6. Conclusion</h3>

<p>TODO</p>]]></content><author><name>Kamil Gurgul</name></author><category term="android" /><category term="kmp" /><category term="java" /><category term="kotlin" /><category term="compose" /><summary type="html"><![CDATA[The story of the migration of the CPU Info application, which over the years has used most Android architectures and patterns.]]></summary></entry><entry><title type="html">Understanding Android SavedState and SavedStateHandle</title><link href="https://kgurgul.com/saved-instance-state/" rel="alternate" type="text/html" title="Understanding Android SavedState and SavedStateHandle" /><published>2019-03-01T00:00:00+00:00</published><updated>2019-03-01T00:00:00+00:00</updated><id>https://kgurgul.com/saved-instance-state</id><content type="html" xml:base="https://kgurgul.com/saved-instance-state/"><![CDATA[<p>Building robust Android applications often requires careful handling of app states, especially when the lifecycle of activities or fragments comes into play. The Android platform offers several tools to manage state across configuration changes or process death, with two of the most common being <code class="language-plaintext highlighter-rouge">SavedState</code> and <code class="language-plaintext highlighter-rouge">SavedStateHandle</code>. In this blog post, we’ll take a closer look at these two concepts, explaining how they work and when you should use them.</p>

<h2 id="what-is-savedstate">What is SavedState?</h2>

<p>In Android, <code class="language-plaintext highlighter-rouge">SavedState</code> refers to the mechanism that allows you to preserve critical information when an activity or fragment is recreated. This typically happens during configuration changes such as screen rotation, or when the app is killed by the system and needs to be restored later.</p>

<p>Traditionally, Android developers relied on the <code class="language-plaintext highlighter-rouge">onSaveInstanceState</code> method to store information in a <code class="language-plaintext highlighter-rouge">Bundle</code>, and then used that <code class="language-plaintext highlighter-rouge">Bundle</code> to restore the state in <code class="language-plaintext highlighter-rouge">onCreate</code> or <code class="language-plaintext highlighter-rouge">onViewStateRestored</code>. However, this approach can get cumbersome, especially when dealing with complex data or larger-scale applications.</p>

<h2 id="enter-savedstatehandle">Enter SavedStateHandle</h2>

<p>The <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> is a more modern and convenient approach introduced as part of Android’s Jetpack components. It’s particularly useful when working with <code class="language-plaintext highlighter-rouge">ViewModel</code> and <code class="language-plaintext highlighter-rouge">Fragment</code> components, providing a seamless way to save and retrieve UI-related data even after process death.</p>

<h3 id="how-savedstatehandle-works">How SavedStateHandle Works</h3>

<p><code class="language-plaintext highlighter-rouge">SavedStateHandle</code> is essentially a key-value map that allows you to store small pieces of data that should survive configuration changes or process death. It works closely with the <code class="language-plaintext highlighter-rouge">ViewModel</code> to ensure the state persists beyond the usual lifecycle of a fragment or activity. Instead of manually handling <code class="language-plaintext highlighter-rouge">Bundle</code> objects, you can interact with <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> in a more straightforward way.</p>

<p>Under the hood, <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> relies on the <code class="language-plaintext highlighter-rouge">SavedStateRegistry</code> mechanism provided by the Android framework. When a <code class="language-plaintext highlighter-rouge">ViewModel</code> is created, it is given a reference to a <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> that is linked to the <code class="language-plaintext highlighter-rouge">SavedStateRegistryOwner</code> (typically an activity or fragment). The <code class="language-plaintext highlighter-rouge">SavedStateRegistry</code> manages the state by storing key-value pairs in a <code class="language-plaintext highlighter-rouge">Bundle</code> when the lifecycle owner is stopped, and then restores those values when it is recreated. This ensures that the data is retained across configuration changes and even process death, making it a powerful tool for state management.</p>

<p>For example, consider the following usage in a <code class="language-plaintext highlighter-rouge">ViewModel</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyViewModel</span><span class="p">(</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">savedStateHandle</span><span class="p">:</span> <span class="nc">SavedStateHandle</span><span class="p">,</span>
<span class="p">)</span> <span class="p">:</span> <span class="nc">ViewModel</span><span class="p">()</span> <span class="p">{</span>

    <span class="k">fun</span> <span class="nf">setUserName</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">savedStateHandle</span><span class="p">[</span><span class="nc">KEY_USER_NAME</span><span class="p">]</span> <span class="p">=</span> <span class="n">name</span>
    <span class="p">}</span>

    <span class="k">companion</span> <span class="k">object</span> <span class="p">{</span>
        <span class="k">private</span> <span class="k">const</span> <span class="kd">val</span> <span class="py">KEY_USER_NAME</span> <span class="p">=</span> <span class="s">"user_name"</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here, <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> allows you to save the user name with a specific key, and retrieve it later, simplifying the process of managing UI state in a configuration-safe manner.</p>

<h3 id="benefits-of-using-savedstatehandle">Benefits of Using SavedStateHandle</h3>

<ol>
  <li>
    <p><strong>Integration with ViewModel:</strong> Unlike traditional <code class="language-plaintext highlighter-rouge">onSaveInstanceState</code>, <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> is integrated with <code class="language-plaintext highlighter-rouge">ViewModel</code>. This means it benefits from the <code class="language-plaintext highlighter-rouge">ViewModel</code>’s lifecycle awareness, making it easier to maintain state during configuration changes.</p>
  </li>
  <li>
    <p><strong>Simplified Code:</strong> No more manual handling of <code class="language-plaintext highlighter-rouge">Bundles</code> and lifecycle methods. The API is intuitive, allowing you to focus on the logic rather than lifecycle intricacies.</p>
  </li>
  <li>
    <p><strong>Automatic Process Death Handling:</strong> <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> automatically manages saving and restoring data in cases of process death, which can be complex to handle manually.</p>
  </li>
</ol>

<h3 id="when-to-use-savedstatehandle">When to Use SavedStateHandle</h3>

<ul>
  <li><strong>Fragments with ViewModel:</strong> When you have a fragment that uses a <code class="language-plaintext highlighter-rouge">ViewModel</code>, <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> is ideal for preserving UI-related state between fragment recreations.</li>
  <li><strong>Configuration Changes:</strong> If you need to persist small amounts of data, like user input or filter criteria, during configuration changes, <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> can simplify your implementation.</li>
  <li><strong>State Restoration After Process Death:</strong> If you want your application to restore its state seamlessly after the OS terminates your app to free up resources, <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> can be your go-to tool.</li>
</ul>

<h2 id="practical-example-saving-user-input">Practical Example: Saving User Input</h2>

<p>Let’s consider a fragment where a user types their name into a text field. You can easily manage this state with <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> and <code class="language-plaintext highlighter-rouge">StateFlow</code> to ensure the data persists across configuration changes.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyViewModel</span><span class="p">(</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">savedStateHandle</span><span class="p">:</span> <span class="nc">SavedStateHandle</span><span class="p">,</span>
<span class="p">)</span> <span class="p">:</span> <span class="nc">ViewModel</span><span class="p">()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">_uiStateFlow</span> <span class="p">=</span> <span class="nc">MutableStateFlow</span><span class="p">(</span>
        <span class="nc">UiState</span><span class="p">(</span>
            <span class="n">userInput</span> <span class="p">=</span> <span class="n">savedStateHandle</span><span class="p">[</span><span class="s">"user_input"</span><span class="p">]</span> <span class="p">=</span> <span class="n">input</span>
        <span class="p">)</span>
    <span class="p">)</span>
    <span class="kd">val</span> <span class="py">uiStateFlow</span> <span class="p">=</span> <span class="n">_uiStateFlow</span><span class="p">.</span><span class="nf">asStateFlow</span><span class="p">()</span>

    <span class="k">fun</span> <span class="nf">saveUserInput</span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">savedStateHandle</span><span class="p">[</span><span class="s">"user_input"</span><span class="p">]</span> <span class="p">=</span> <span class="n">input</span>
        <span class="n">_uiStateFlow</span><span class="p">.</span><span class="nf">update</span> <span class="p">{</span>
            <span class="n">it</span><span class="p">.</span><span class="nf">copy</span><span class="p">(</span><span class="n">userInput</span> <span class="p">=</span> <span class="n">input</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">data class</span> <span class="nc">UiState</span><span class="p">(</span>
        <span class="kd">val</span> <span class="py">userInput</span><span class="p">:</span> <span class="nc">String</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span><span class="p">,</span>
    <span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In your fragment, you would observe this data and update the UI accordingly:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyFragment</span> <span class="p">:</span> <span class="nc">Fragment</span><span class="p">()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">viewModel</span><span class="p">:</span> <span class="nc">MyViewModel</span> <span class="k">by</span> <span class="nf">viewModels</span><span class="p">()</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">onViewCreated</span><span class="p">(</span><span class="n">view</span><span class="p">:</span> <span class="nc">View</span><span class="p">,</span> <span class="n">savedInstanceState</span><span class="p">:</span> <span class="nc">Bundle</span><span class="p">?)</span> <span class="p">{</span>
        <span class="k">super</span><span class="p">.</span><span class="nf">onViewCreated</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">savedInstanceState</span><span class="p">)</span>

        <span class="kd">val</span> <span class="py">userInputEditText</span> <span class="p">=</span> <span class="n">view</span><span class="p">.</span><span class="n">findViewById</span><span class="p">&lt;</span><span class="nc">EditText</span><span class="p">&gt;(</span><span class="nc">R</span><span class="p">.</span><span class="n">id</span><span class="p">.</span><span class="n">userInputEditText</span><span class="p">)</span>

        <span class="c1">// Observe the saved input</span>
        <span class="n">viewLifecycleOwner</span><span class="p">.</span><span class="n">lifecycleScope</span><span class="p">.</span><span class="nf">launch</span> <span class="p">{</span>
            <span class="n">viewLifecycleOwner</span><span class="p">.</span><span class="nf">repeatOnLifecycle</span><span class="p">(</span><span class="nc">Lifecycle</span><span class="p">.</span><span class="nc">State</span><span class="p">.</span><span class="nc">STARTED</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">viewModel</span><span class="p">.</span><span class="n">uiStateFlow</span><span class="p">.</span><span class="nf">collect</span> <span class="p">{</span> <span class="n">uiState</span> <span class="p">-&gt;</span>
                    <span class="n">userInputEditText</span><span class="p">.</span><span class="nf">setText</span><span class="p">(</span><span class="n">uiState</span><span class="p">.</span><span class="n">userInput</span> <span class="o">?:</span> <span class="s">""</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this approach, user input is automatically saved and restored, creating a smooth user experience without manually managing bundles and lifecycle intricacies.</p>

<h2 id="conclusion">Conclusion</h2>

<p><code class="language-plaintext highlighter-rouge">SavedState</code> and <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> are essential tools for Android developers aiming to create more resilient and lifecycle-aware applications. While <code class="language-plaintext highlighter-rouge">SavedState</code> using <code class="language-plaintext highlighter-rouge">onSaveInstanceState</code> is still a valid approach, <code class="language-plaintext highlighter-rouge">SavedStateHandle</code> provides a cleaner and more integrated solution, especially when working with Jetpack’s <code class="language-plaintext highlighter-rouge">ViewModel</code> and <code class="language-plaintext highlighter-rouge">Fragment</code> components. By leveraging these tools, you can ensure that your app handles configuration changes and process death smoothly, leading to a better user experience.</p>]]></content><author><name>Kamil Gurgul</name></author><category term="android" /><category term="internals" /><summary type="html"><![CDATA[Managing app state effectively is key to building robust Android applications, especially during lifecycle events like configuration changes or process death. In this post, we explore SavedState and SavedStateHandle, two important tools that simplify state management. We'll cover how SavedStateHandle integrates with ViewModel, making it easier to preserve UI-related data seamlessly. With practical examples involving StateFlow, you'll learn how to maintain user input and create a smoother user experience without the hassle of managing Bundles manually.]]></summary></entry></feed>