<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David Rufai</title>
    <description>The latest articles on DEV Community by David Rufai (@xheghun).</description>
    <link>https://dev.to/xheghun</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1943256%2Ff0dbd668-869e-466f-afe3-01c35247cc17.jpg</url>
      <title>DEV Community: David Rufai</title>
      <link>https://dev.to/xheghun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xheghun"/>
    <language>en</language>
    <item>
      <title>Yet Another Article on Media Streaming: Manifest parsing, Buffer management, Demuxing, Decoding.</title>
      <dc:creator>David Rufai</dc:creator>
      <pubDate>Tue, 21 Jul 2026 00:49:43 +0000</pubDate>
      <link>https://dev.to/xheghun/yet-another-article-on-media-streaming-part-3-5d7n</link>
      <guid>https://dev.to/xheghun/yet-another-article-on-media-streaming-part-3-5d7n</guid>
      <description>&lt;p&gt;Hey there! In &lt;a href="https://dev.to/xheghun/introduction-to-media-streaming-part-1-2lbc"&gt;Part 1&lt;/a&gt; and &lt;a href="https://dev.to/xheghun/introduction-to-media-streaming-part-2-473p"&gt;Part 2&lt;/a&gt;, we covered the network side of streaming: why chunked delivery beats downloading, how manifests and ABR work, and how DRM protects content in transit. That's the "getting the bytes to the device" story.&lt;/p&gt;

&lt;p&gt;This part picks up right where the bytes land. A segment has arrived on the device, encrypted or not, and now something has to turn it into pixels and sound coming out of your speakers within milliseconds. That's the playback pipeline, and it's where a lot of my day to day work as an Android engineer actually lives. I'll be leaning on Media3/ExoPlayer for the code examples since that's the stack I know best, but the concepts apply broadly to any modern player(AVFoundation/ShakaPlayer).&lt;/p&gt;

&lt;p&gt;Here's the rough shape of what we're covering, in the order it actually happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Manifest → Segment Fetch → Buffer → Demux → Decode → Sync → Render
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's Dive into them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manifest Parsing
&lt;/h2&gt;

&lt;p&gt;Before anything can play, the player needs to know what it's dealing with, how many renditions exist, where the segments live, what codecs are in use, whether there's DRM involved. That's what the manifest (&lt;code&gt;.m3u8&lt;/code&gt; for HLS, &lt;code&gt;.mpd&lt;/code&gt; for DASH) tells it.&lt;/p&gt;

&lt;p&gt;Parsing this isn't just abount reading a file, the player builds an internal representation of the whole stream structure: available tracks (video, audio, subtitles), the rendition ladder for each, segment URLs or byte ranges, and timing information. In Media3, this is handled by a &lt;code&gt;MediaSource&lt;/code&gt; for adaptive content specifically, &lt;code&gt;HlsMediaSource&lt;/code&gt; or &lt;code&gt;DashMediaSource&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;mediaItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MediaItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/stream.mpd"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMimeType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MimeTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;APPLICATION_MPD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;mediaSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DashMediaSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataSourceFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createMediaSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mediaItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;exoPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMediaSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mediaSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;exoPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;prepare()&lt;/code&gt; call is doing more than it looks like, under the hood, ExoPlayer fetches the manifest, parses it, and builds out the track structure before it can fetch a single video segment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Segment Fetching
&lt;/h2&gt;

&lt;p&gt;Once the player knows what's available, it needs to actually pull segments over the network and this is where the ABR decision from Part 2 gets executed in practice. For every segment, the player's track selector picks a rendition, and a &lt;code&gt;DataSource&lt;/code&gt; fetches the corresponding chunk.&lt;/p&gt;

&lt;p&gt;The Player prefetches chunks ahead of the playback position to keep the buffer healthy, and the &lt;code&gt;LoadControl&lt;/code&gt; in ExoPlayer is what governs how aggressively that happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;loadControl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultLoadControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBufferDurationsMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="cm"&gt;/* minBufferMs = */&lt;/span&gt; &lt;span class="mi"&gt;15_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="cm"&gt;/* maxBufferMs = */&lt;/span&gt; &lt;span class="mi"&gt;50_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="cm"&gt;/* bufferForPlaybackMs = */&lt;/span&gt; &lt;span class="mi"&gt;2_500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="cm"&gt;/* bufferForPlaybackAfterRebufferMs = */&lt;/span&gt; &lt;span class="mi"&gt;5_000&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;exoPlayer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ExoPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLoadControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loadControl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;bufferForPlaybackMs&lt;/code&gt; is worth calling out specifically as it's the minimum amount of buffered media required before playback starts at all, which is a direct trade off between "start playing fast" and "don't stall two seconds in."&lt;/p&gt;

&lt;h2&gt;
  
  
  Buffer Management
&lt;/h2&gt;

&lt;p&gt;Fetched segments don't go straight to the decoder. They land in a buffer first, which exists for one reason: to absorb network variability so playback doesn't stall every time throughput dips for a moment.&lt;/p&gt;

&lt;p&gt;The buffer isn't one undifferentiated pool, either. There's a distinction worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Media buffer&lt;/strong&gt; compressed segment data that's been downloaded but not yet decoded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoded/output buffer&lt;/strong&gt; frames that have already been decoded and are waiting to be rendered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;minBufferMs&lt;/code&gt;/&lt;code&gt;maxBufferMs&lt;/code&gt; values above are what keep the media buffer in a healthy range too little, and any network hiccup causes a stall; too much, and you're wasting memory and bandwidth on segments that might get abandoned anyway if the ABR algorithm decides to switch renditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demuxing
&lt;/h2&gt;

&lt;p&gt;Here's a term from Part 1 worth revisiting: a container (like an &lt;code&gt;.mp4&lt;/code&gt; or &lt;code&gt;.ts&lt;/code&gt; segment) holds multiple streams bundled together — video, audio, sometimes subtitles. Before any of those can be decoded, they need to be pulled apart. That's demuxing (short for demultiplexing).&lt;/p&gt;

&lt;p&gt;A demuxer reads the container format, identifies each embedded stream, and routes the compressed video frames to a video decoder and the compressed audio frames to an audio decoder, in parallel. This matters because video and audio are decoded completely independently at this stage they only get reunited later, at the sync stage.&lt;/p&gt;

&lt;p&gt;In ExoPlayer, this is handled by an &lt;code&gt;Extractor&lt;/code&gt; (there's one per container format — &lt;code&gt;Mp4Extractor&lt;/code&gt;, &lt;code&gt;TsExtractor&lt;/code&gt;, &lt;code&gt;FragmentedMp4Extractor&lt;/code&gt;, and so on), and it's mostly invisible to us as developers unless you're working with a custom or unusual container format(we'll talk about this another article).&lt;/p&gt;

&lt;h2&gt;
  
  
  Decoding
&lt;/h2&gt;

&lt;p&gt;This is where compressed data actually becomes raw, playable frames, and it's also the most hardware-dependent stage in the whole pipeline.&lt;/p&gt;

&lt;p&gt;On Android, this is &lt;code&gt;MediaCodec&lt;/code&gt;'s job. Every device ships with a set of hardware decoders (fast, power-efficient, but fixed at the chip level) for common codecs like H.264/AVC and often H.265/HEVC. When a hardware decoder is available for the stream's codec, the player uses it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;trackSelector&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultTrackSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;trackSelector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trackSelector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildUponParameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTunnelingEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Tunneling here lets the hardware decoder pipe decoded frames directly toward the display in supported cases, cutting out extra copies, a small optimization, but a good example of how much is happening below the API surface you normally touch.)&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens when the device can't decode a codec?
&lt;/h3&gt;

&lt;p&gt;This is worth its own section, because it's a real problem, not a theoretical edge case. Say your stream was encoded in AV1, but you're playing it on a mid-range device from a few years back with no AV1 hardware decoder. What now?&lt;/p&gt;

&lt;p&gt;we've got a few options, roughly in order of preference:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Serve a different rendition.&lt;/strong&gt; If the manifest includes a fallback codec (say, H.264 alongside AV1), the player can just pick a rendition it can actually decode. This is the cleanest fix and why most large platforms encode in multiple codecs, not just the newest/most efficient one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fall back to software decoding.&lt;/strong&gt; Every codec also has a software decoder implementation that runs on the CPU instead of dedicated hardware. It works everywhere, but it's slower and burns significantly more battery fine for short clips, rough for a two-hour movie on a phone at 30% battery(faced a similar issue earlier in my career with FFmpeg).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Register a custom decoder extension.&lt;/strong&gt; Media3 supports this directly, you can plug in a software codec implementation (Google publishes extensions for AV1 and others) that ExoPlayer will fall back to when no hardware decoder matches.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;renderersFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultRenderersFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setExtensionRendererMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;DefaultRenderersFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EXTENSION_RENDERER_MODE_ON&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;exoPlayer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ExoPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;renderersFactory&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;EXTENSION_RENDERER_MODE_ON&lt;/code&gt; tells ExoPlayer to consider extension decoders (like the AV1 software extension) as a fallback if the platform's built-in &lt;code&gt;MediaCodec&lt;/code&gt; decoders can't handle the stream — it tries hardware/platform decoders first, and only falls back to the extension if nothing else works. There's also &lt;code&gt;EXTENSION_RENDERER_MODE_PREFER&lt;/code&gt;, which flips that priority, though that's a less common choice since hardware decoding is almost always the better default.&lt;/p&gt;

&lt;p&gt;This is a genuinely practical thing to get right: pick the wrong strategy and you either get playback failures on real devices in the field, or you get playback that "works" but tanks battery life and generates support tickets you can't easily explain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audio/Video Synchronization
&lt;/h2&gt;

&lt;p&gt;Remember how demuxing split audio and video into separate decode paths? They now need to come back together — and this is trickier than it sounds, because audio and video decode at different rates and have different latency characteristics.&lt;/p&gt;

&lt;p&gt;The mechanism that ties them back together is the &lt;strong&gt;presentation timestamp (PTS)&lt;/strong&gt; every decoded frame, audio or video, carries a timestamp saying exactly when it should be presented to the viewer. The player picks one clock as the reference (almost always the audio clock, since audio glitches are far more noticeable to human ears than a dropped video frame) and continuously adjusts video rendering to match it — dropping a video frame if it's fallen behind, or holding one briefly if it's ahead.&lt;/p&gt;

&lt;p&gt;This is also why you'll occasionally see a video frame get skipped entirely under heavy load rather than the whole playback stalling — the player is prioritizing staying in sync over rendering every single frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering to the Screen
&lt;/h2&gt;

&lt;p&gt;The final stage: decoded frames need to actually reach the display, and decoded audio needs to reach the speakers.&lt;/p&gt;

&lt;p&gt;For video, decoded frames are typically handed off to a &lt;code&gt;Surface&lt;/code&gt; — on Android, this is usually backed by a &lt;code&gt;SurfaceView&lt;/code&gt; or &lt;code&gt;TextureView&lt;/code&gt;, which composites with the rest of the UI and gets pushed to the display at the device's refresh rate. For audio, decoded PCM data goes to an &lt;code&gt;AudioTrack&lt;/code&gt;, which queues it for playback through the device's audio pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;playerView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exoPlayer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one line is doing a lot of quiet work — &lt;code&gt;PlayerView&lt;/code&gt; wires up the &lt;code&gt;Surface&lt;/code&gt; the video renderer writes to, alongside playback controls, subtitle rendering, and the various render-state callbacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ExoPlayer Orchestrates All of This
&lt;/h2&gt;

&lt;p&gt;Pulling it together: &lt;code&gt;ExoPlayer&lt;/code&gt; itself doesn't do the demuxing, decoding, or rendering directly. It's an orchestrator — it owns the playback timeline and state machine, and delegates the actual work to a set of collaborating components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MediaSource&lt;/code&gt;&lt;/strong&gt; manifest parsing and segment/track structure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;LoadControl&lt;/code&gt;&lt;/strong&gt; buffer thresholds and prefetch behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;TrackSelector&lt;/code&gt;&lt;/strong&gt; which rendition and codec to use, including ABR decisions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Renderer&lt;/code&gt;&lt;/strong&gt; (one per track type, video, audio, text) demuxing, decoding, and rendering for that track&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MediaClock&lt;/code&gt;&lt;/strong&gt; the timing authority that keeps renderers in sync&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each &lt;code&gt;Renderer&lt;/code&gt; runs its own decode loop, pulling samples from the &lt;code&gt;MediaSource&lt;/code&gt;, feeding them to a decoder (&lt;code&gt;MediaCodec&lt;/code&gt; or an extension), and pushing output toward the display or audio track all while checking in against the shared clock to stay synchronized.&lt;/p&gt;

&lt;p&gt;If you take one thing away from this article, it's this: everything from "the video looks blurry for a second" to "the app crashed trying to play this file" traces back to one of these seven stages. Knowing which stage you're debugging is most of the battle when something goes wrong in a media player.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Quick recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manifest parsing builds the player's internal picture of the stream — tracks, renditions, timing&lt;/li&gt;
&lt;li&gt;Segment fetching executes ABR decisions and prefetches ahead to keep the buffer healthy&lt;/li&gt;
&lt;li&gt;Buffering absorbs network variability so playback doesn't stall on every dip&lt;/li&gt;
&lt;li&gt;Demuxing splits a container into independent audio and video streams&lt;/li&gt;
&lt;li&gt;Decoding turns compressed data into raw frames — hardware-first, with software fallback or custom extensions when the device can't handle the codec natively&lt;/li&gt;
&lt;li&gt;A/V sync uses presentation timestamps, anchored to the audio clock, to keep everything aligned&lt;/li&gt;
&lt;li&gt;Rendering pushes decoded frames and audio to the screen and speakers&lt;/li&gt;
&lt;li&gt;ExoPlayer orchestrates all of it through &lt;code&gt;MediaSource&lt;/code&gt;, &lt;code&gt;LoadControl&lt;/code&gt;, &lt;code&gt;TrackSelector&lt;/code&gt;, and per-track &lt;code&gt;Renderer&lt;/code&gt;s, all checking in against a shared clock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the full pipeline from segment arrival to pixels on screen. In Part 4, I want to go deeper into a couple of these stages specifically — likely &lt;code&gt;MediaCodec&lt;/code&gt; internals and buffer/memory management under real-world constraints, since those are the areas with the least beginner-friendly documentation out there.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is Part 3 of a multi-part series on media streaming internals. Catch &lt;a href="https://dev.to/xheghun/introduction-to-media-streaming-part-1-2lbc"&gt;Part 1&lt;/a&gt; and &lt;a href="https://dev.to/xheghun/introduction-to-media-streaming-part-2-473p"&gt;Part 2&lt;/a&gt; here.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>streaming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Introduction to media streaming: Bitrate, Resolution, ABR, &amp; DRM</title>
      <dc:creator>David Rufai</dc:creator>
      <pubDate>Sun, 12 Jul 2026 22:38:00 +0000</pubDate>
      <link>https://dev.to/xheghun/introduction-to-media-streaming-part-2-473p</link>
      <guid>https://dev.to/xheghun/introduction-to-media-streaming-part-2-473p</guid>
      <description>&lt;p&gt;Hey there! Alright so in &lt;a href="https://dev.to/xheghun/introduction-to-media-streaming-part-1-2lbc"&gt;Part 1&lt;/a&gt;, we covered why streaming beats downloading, how chunked delivery and manifest files work together, and the difference between containers and codecs. If you haven't read that yet, it's probably worth a quick look before proceeding with this one, we'll be building directly on top of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bitrate vs Resolution
&lt;/h2&gt;

&lt;p&gt;Bitrate and resolution correlate to the amount of data in a media file, it's easy to mix these two up so let's clarify how they differ. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution&lt;/strong&gt; measures the detail in a single video frame by counting the pixels across its width and height. For instance, a 1920 by 1080 display is made up of 1,080 stacked rows, with each row stretching 1,920 pixels wide. More pixels means more detail, and then a bigger picture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bitrate&lt;/strong&gt;, think of it as the amount of digital information your audio or video is allowed to use per second. Giving it more information to work with creates a crisper, more detailed, media experience, usually measured in kbps or Mbps. This is where the actual compression trade off lives.&lt;/p&gt;

&lt;p&gt;The thing is you can't judge video quality by resolution by itself. A 1080p file can look terrible, blurry, pixelated, and washed out if it's encoded in a very low bitrate. On the flip side, a lower 720p resolution with a healthy bitrate can produce a much, cleaner image than a poorly compressed 1080p video. &lt;/p&gt;

&lt;p&gt;There's another piece of the puzzle too: the codec being used. Remember from &lt;a href="https://dev.to/xheghun/introduction-to-media-streaming-part-1-2lbc"&gt;Part 1&lt;/a&gt; that codecs are responsible for compressing and decompressing video. Newer codecs are simply better at squeezing the same visual quality into fewer bits.&lt;/p&gt;

&lt;p&gt;For example, a video encoded with H.265 (HEVC) or AV1 can often look just as good as an older H.264 (AVC) stream while using significantly less bandwidth. In other words, two videos with exactly the same resolution and bitrate don't necessarily look identical, the codec plays a huge role in how efficiently those bits are used.&lt;/p&gt;

&lt;p&gt;Now you must be thinking why not just use the highest bitrate for everything? Hmm...well because bitrate is directly tied to file size and bandwidth. A higher bitrate means more data has to be transmitted  every second, which affects latency, and if the viewer's connection can't keep up, you get the thing nobody wants, buffering.&lt;/p&gt;

&lt;p&gt;Just like the issue with downloading, adaptive bitrate streaming (ABR) was invented as the direct solution to, finding the perfect balance between great video quality and a viewer's internet speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adaptive Bitrate Streaming (ABR)
&lt;/h2&gt;

&lt;p&gt;Remember the manifest files we talked about in Part 1 &lt;code&gt;.m3u8&lt;/code&gt; for HLS, &lt;code&gt;.mpd&lt;/code&gt; for DASH? This is where they truly prove their value.&lt;/p&gt;

&lt;p&gt;Preparing video for streaming involves generating multiple encodings instead of a single output file, this strategy is vital because it allows the stream to work seamlessly on the massive, ever increasing variety of screens, devices and browsers available today. The media is processed into various resolutions, bitrate, and container combinations called &lt;strong&gt;renditions&lt;/strong&gt; or &lt;strong&gt;variants&lt;/strong&gt;, typically structured into a ladder such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;240p @ 400 kbps&lt;/li&gt;
&lt;li&gt;480p @ 1000 kbps&lt;/li&gt;
&lt;li&gt;720p @ 2500 kbps&lt;/li&gt;
&lt;li&gt;1080p @ 5000 kbps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these get chunked up, and the manifest lists every rendition as an option. The player is the one that decides, chunk by chunk, which rendition to pull next based on the device's current network speed, buffer health, and sometimes device screen size.&lt;/p&gt;

&lt;p&gt;Have you ever noticed a video go blurry when your network drops, only to clear up a moment later? Well this isn't a bug in the player, to prevent the video from freezing up, The player immediately requests the next segment at a lower quality, as you watch, it measures your real-time download speed and automatically steps up to a clearer picture as soon as it’s confident your connection can handle it.&lt;/p&gt;

&lt;p&gt;Let's make that a little more concrete.&lt;/p&gt;

&lt;p&gt;Imagine you're watching a football match in 1080p over your home Wi-Fi. Everything looks sharp because the player has determined your connection can comfortably sustain that bitrate. Halfway through the match, someone in your house starts downloading a large game update. Suddenly, your available bandwidth drops.&lt;/p&gt;

&lt;p&gt;The player notices that the next few video segments are taking longer to download than before. Rather than waiting until playback stalls, it proactively requests the next segment at 720p, or even 480p if necessary. The picture becomes a little softer, but the video keeps playing without interruption.&lt;/p&gt;

&lt;p&gt;A few minutes later, the download finishes. Segment downloads become faster again, the player's confidence in the available bandwidth increases, and it gradually moves back up through higher quality renditions until you're watching in 1080p again all without restarting the stream or you having to do anything manually.&lt;/p&gt;

&lt;p&gt;The paragraph above overly simplifies what happens under the hood with ABR. What is the player actually measuring, and how does it decide? We need to go a level deeper.&lt;/p&gt;

&lt;h3&gt;
  
  
  How ABR algorithms actually decide
&lt;/h3&gt;

&lt;p&gt;Broadly, these algorithms fall into a few categories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throughput-based,&lt;/strong&gt; which is the simplest approach: you measure how fast recent segments downloaded, estimate the device's available bandwidth from that, and then pick the highest rendition whose bitrate comfortably fits under that estimate. The catch is that throughput is rather unstable, an increase in latency on a single segment doesn't necessarily mean your connection became poor, it might just be a momentary dip, or an issue with the CDN serving that segment.&lt;/p&gt;

&lt;p&gt;Switching too aggressively on every fluctuation and you get &lt;strong&gt;quality flapping&lt;/strong&gt;, where the video visibly jumps between resolutions every few seconds, which is arguably worse for viewer experience than just staying at a slightly lower quality. Instead of panicking and reacting to a single slow download, most players use a moving average to smoothly track network speed, once that average confirms your connection is stable, the player then steps back up to a clearer quality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buffer-based.&lt;/strong&gt; Instead of just staring at download speeds, this approach looks at the actual runway the player has left, the amount of segments currently sitting in the buffer. The logic is simple: if the buffer is healthy and growing, you can afford to take a risk and bump up the quality, but if the buffer starts shrinking toward empty, then the need to hit the brakes and drop to a lower resolution immediately, even if your download speed looks fine.&lt;/p&gt;

&lt;p&gt;A viewer can tolerate a blurry video for a few seconds, but they absolutely hate it when the video freezes completely to reload. Because a frozen screen is the ultimate mood killer, real world video players are built to keep the video moving, even if it means dropping the quality for a bit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hybrid approaches.&lt;/strong&gt; In practice, most modern players (ExoPlayer, AVFoundation, Shaka Player) combine both signals along with a few extras: device screen size, past switching history to avoid flapping, and sometimes prediction models trying to anticipate network drops before they happen. In practice, most modern players build on some combination of throughput and buffer health.&lt;/p&gt;

&lt;p&gt;So what is the player actually trying to optimise? Every ABR algorithm is ultimately balancing three competing goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Quality&lt;/strong&gt; give the viewer the best resolution their connection can handle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stability&lt;/strong&gt; don't switch renditions so often that it's distracting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rebuffer risk&lt;/strong&gt; never let the buffer run dry&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The player can optimize for any one of these, but trade off against the other two. Tuning this balance well is most of what makes one player's ABR implementation "feel" better than another's, even when they're pulling from the same manifest.&lt;/p&gt;

&lt;p&gt;Next let's look at how to protect these files when transmitting them over the internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protecting Premium Content: DRM
&lt;/h2&gt;

&lt;p&gt;ABR solves the problem of keeping video playing smoothly. But it doesn't solve a completely different problem that studios and streaming platforms care about a lot: how to stop people from just copying and stealing the video the second it reaches their device.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;Digital Rights Management (DRM)&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;At a high level, DRM encrypts the media so that even though the chunks are sitting on the device, or passing through the network, they're useless without a decryption key. That key is only handed over by a &lt;strong&gt;license server&lt;/strong&gt; after checking that the request is legitimate, the user has a valid subscription, the device is authorized, that kind of thing.&lt;/p&gt;

&lt;p&gt;The three major DRM systems you'll run into are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Widevine&lt;/strong&gt;: Google, used across Android, Chrome, and most smart TVs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FairPlay&lt;/strong&gt;: Apple, used across iOS, Safari, and Apple devices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PlayReady&lt;/strong&gt;: Developed by Microsoft, used across Windows and some smart TVs/consoles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because no single DRM system covers every platform, most large streaming services encrypt their content with &lt;strong&gt;multiple DRM systems at once&lt;/strong&gt;, and the player picks whichever one matches the device it's running on. This is usually paired with &lt;strong&gt;Common Encryption (CENC)&lt;/strong&gt;, a standard that lets the same encrypted media file be decrypted by different DRM systems, so you're not re-encoding your entire catalog three times over.&lt;/p&gt;

&lt;p&gt;It’s worth noting that encoding/decoding a video is totally different from encrypting it with DRM. Encoding is just a way of compressing a video file so it’s small enough to travel smoothly over the internet. Encryption, on the other hand, is all about locking the file down to protect it from being stolen.&lt;/p&gt;

&lt;h3&gt;
  
  
  What actually happens when a license gets requested
&lt;/h3&gt;

&lt;p&gt;"Gate the key behind a license server" is easy to say. Here's what that exchange actually looks like step by step, using Widevine as the running example (FairPlay and PlayReady follow the same general shape, with system-specific details):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The player requests encrypted content.&lt;/strong&gt; The video chunks arrive encrypted the player can download them fine, but can't decode them yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The player extracts protection info.&lt;/strong&gt; Encrypted content includes metadata (in Widevine's case, called &lt;code&gt;PSSH&lt;/code&gt; data) that tells the player's Content Decryption Module (CDM) which DRM system is in use and which key is needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CDM generates a license request.&lt;/strong&gt; This is a device-specific, cryptographically signed request, it proves the request is in fact coming from a legitimate, unmodified player/device combination, not something spoofing it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The request goes to the license server.&lt;/strong&gt; This is a server the streaming platform controls, separate from the server hosting the video segments. It checks things like if the user's subscription is active, if the device is authorised, if the account has rights to the particular title, and if the region is licensed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The license server responds with an encrypted license.&lt;/strong&gt; If everything checks out, the server sends back a license containing the decryption key, but the key itself is encrypted so that only the specific CDM that made the request can unlock it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CDM decrypts content locally.&lt;/strong&gt; From here, decryption happens inside a protected, isolated part of the device - ideally hardware backed, so the key never becomes accessible to the OS, the app, or anything that could extract it, then the decoded frames get handed to the video player for playback.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is way more complicated than just checking a password. With normal auth, the app only needs to ask: "Is this the right user?" But with video security, the question is: "Can we let this exact video play on this exact device without letting the user, or any unauthorised software on their machine ever touch the actual video file or the secret key?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A note on security levels.&lt;/strong&gt; Not all decryption happens with the same level of protection. DRM systems support different tiers of security,  Widevine, for example, ranges from L1 - which is hardware backed, keys and decryption happen in a trusted execution environment down, to L3 which is software only with no TEE backup. Premium content (think 4K, HDR) is often gated to only play on devices that support the higher security level, because software only decryption is more exposed to extraction attacks. This is part of why a 4K stream might quietly downgrade to a lower resolution on an older or rooted device because the device doesn't meet the security requirement for that content tier, so the platform serves a rendition it's comfortable protecting less strictly.&lt;/p&gt;

&lt;p&gt;Tools like Shaka Packager (mentioned back in &lt;a href="https://dev.to/xheghun/introduction-to-media-streaming-part-1-2lbc"&gt;Part 1&lt;/a&gt; for encoding), and FFMpeg also handle the encryption step, tagging the encrypted chunks with the right metadata so the player knows which license server to talk to and which key to ask for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Quick recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resolution determines how many pixels are displayed, bitrate determines how much data is available each second, and the codec determines how efficiently those bits are used.&lt;/li&gt;
&lt;li&gt;ABR works by encoding multiple quality renditions and letting the player switch between them, usually driven by a combination of throughput and buffer level signals, balanced against quality, stability, and rebuffer risk&lt;/li&gt;
&lt;li&gt;DRM protects content by encrypting it and gating decryption keys behind a license server via a signed request/response handshake, with Widevine, FairPlay, and PlayReady covering the major platforms, and security levels (like Widevine's L1/L3) determining what quality tiers a device is even allowed to play&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That covers the client-side fundamentals I set out to write about from the basic "why streaming exists" question all the way down to what happens in a DRM handshake. If you're building or debugging a media player and want to go even further, the ExoPlayer and Shaka Player source/docs are genuinely excellent references a lot of what's above is drawn from spending time reading and understanding them and on &lt;a href="https://ottverse.com/" rel="noopener noreferrer"&gt;OTTVerse&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is Part 2 of a multi-part series on media streaming internals. Catch &lt;a href="https://dev.to/xheghun/introduction-to-media-streaming-part-1-2lbc"&gt;Part 1 here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>software</category>
      <category>security</category>
    </item>
    <item>
      <title>Introduction to media streaming: Download, Containers &amp; Codecs.</title>
      <dc:creator>David Rufai</dc:creator>
      <pubDate>Tue, 07 Jul 2026 20:51:52 +0000</pubDate>
      <link>https://dev.to/xheghun/introduction-to-media-streaming-part-1-2lbc</link>
      <guid>https://dev.to/xheghun/introduction-to-media-streaming-part-1-2lbc</guid>
      <description>&lt;p&gt;I recently interviewed with a popular streaming platform for a Senior Software Engineer role focused on low-level media streaming. As someone with prior experience in this space, I used the prep process to go deeper into the internals of how streaming actually works. The interview went well, but the role wasn't quite the right fit at the time. In an effort not let that knowledge go to waste, I thought I'd write an article to share what I learned during this period.&lt;/p&gt;

&lt;p&gt;This article is the first in a series introducing how media streaming works on the client side: the steps involved in getting a piece of media from a server onto a client device over the internet, so off we go!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Downloading
&lt;/h2&gt;

&lt;p&gt;I believe at some point you've consume some sort of media on the internet today with either your phone, your laptop, maybe even a pager if you're reading this from 1998. Whether it's a short clip on Instagram or X(formerly Twitter), or a two-hour film on Netflix or Amazon Prime, or maybe even listening to your favourite music on Spotify that content had to get to your device somehow.&lt;/p&gt;

&lt;p&gt;These media content can be delivered in various ways but we'd be looking at two of them. In the early days of the internet, there was really just one go-to option, downloading the entire file before playback actually starts. Yeah that's works fine for a few second clip, but where the problem lies is when a long form media content is being served to a user. Downloading an hour of 720p video over a 4G connection can take several minutes depending on your connection quality, now imagine how that scales when the media resolution is larger with a longer duration. Nobody wants to stare at a loading bar for ten minutes before a movie starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enters Streaming
&lt;/h2&gt;

&lt;p&gt;The solution to this was streaming. In context media, streaming is the process of continually delivering multimedia content, like video,  audio, or subtitles over the internet in near realtime.&lt;/p&gt;

&lt;p&gt;Instead of waiting for the entire file to download, streaming breaks the media into small chunks (typically a few seconds each) that are delivered and buffered continuously. Playback can start as soon as the first chunk of the media arrives, rather than waiting for the whole file. See how that solves the download problem?...now let's dive a bit deeper into how these streaming technologies work.&lt;/p&gt;

&lt;p&gt;Before playback can start with streaming, the player(client) needs to know what to fetch and in what order. That's the job of a manifest file, a kind of playlist that lists the available chunks and quality levels. depending on the streaming protocol, this manifest file can be formatted differently. HLS uses a &lt;code&gt;.m3u8&lt;/code&gt; manifests, while MPEG-DASH uses &lt;code&gt;.mpd&lt;/code&gt; manifests. We won't go deep on these yet, but it's worth knowing they exist: they're the reason a player can jump between quality levels mid-playback without you noticing something we'll cover properly in Part 2 when we get into adaptive bitrate streaming.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Two Big Streaming Technologies
&lt;/h2&gt;

&lt;p&gt;Since streaming's early days, several technologies have emerged, but two dominate today RIP Adobe Flash:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Live Streaming (HLS)&lt;/strong&gt; — developed by Apple&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Adaptive Streaming over HTTP&lt;/strong&gt; (DASH), also known as MPEG-DASH&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both work on the same core idea described above: chunked delivery guided by a manifest. Before any video can be delivered this way, though, it needs to be prepared, encoded, packaged, and often encrypted. Tools like FFmpeg and Shaka Packager handle this: taking a raw, hour long video file and turning it into the chunks and manifests a streaming player can actually use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Containers and Codecs
&lt;/h2&gt;

&lt;p&gt;To support an the vast array of browsers, OS, and devices available today you can't rely on just a single media format. This is where containers and codecs come in, two terms that get used interchangeably but mean different things.&lt;/p&gt;

&lt;p&gt;think of container as  just that, a shell for housing multiple data streams maybe an audio, video and or subtitle stream, bundled into one file. An MP4 file, for example, is a container that might hold an H.264 video stream and an AAC audio stream inside it.&lt;/p&gt;

&lt;p&gt;Each of those individual streams is compressed using a codec(encoder/decoder), an algorithm designed to shrink raw, usually oversized media data into something efficient to store and transmit over the internet. some of the common video codecs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;H.264 / AVC (Advanced Video Coding)&lt;/li&gt;
&lt;li&gt;H.265 / HEVC (High Efficiency Video Coding), the successor to H.264&lt;/li&gt;
&lt;li&gt;AV1, a newer, royalty-free codec&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And common audio codecs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AAC (Advanced Audio Coding)&lt;/li&gt;
&lt;li&gt;Opus&lt;/li&gt;
&lt;li&gt;FLAC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The codec is what does the heavy lifting of compression; the container is just the box everything ships in ;)&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next
&lt;/h3&gt;

&lt;p&gt;To keep this article from running longer than the Dead Sea Scrolls, I'll stop here. Quick recap of what we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why downloading doesn't scale for long-form video, and how streaming solves it&lt;/li&gt;
&lt;li&gt;How chunked delivery and manifest files work together&lt;/li&gt;
&lt;li&gt;HLS and DASH as the two dominant streaming protocols&lt;/li&gt;
&lt;li&gt;The difference between containers (the shell) and codecs (the compression)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 2, we'll dig into bitrate vs. resolution and how they affect playback quality, how adaptive bitrate streaming actually decides what to switch to and when, and how premium content gets protected with DRM and how the client platform handle securing these files.&lt;/p&gt;

&lt;p&gt;Part 2 available &lt;a href="https://dev.to/xheghun/introduction-to-media-streaming-part-2-473p"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>software</category>
    </item>
    <item>
      <title>Jetpack Compose Optimization - Making Your App Run Like a Well-Oiled Machine</title>
      <dc:creator>David Rufai</dc:creator>
      <pubDate>Wed, 30 Oct 2024 18:56:55 +0000</pubDate>
      <link>https://dev.to/xheghun/jetpack-compose-optimization-making-your-app-run-like-a-well-oiled-machine-12lo</link>
      <guid>https://dev.to/xheghun/jetpack-compose-optimization-making-your-app-run-like-a-well-oiled-machine-12lo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzi809lbwc0bblme4vkr8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzi809lbwc0bblme4vkr8.jpg" alt="tuning" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
I think we all know how easy it has become when it comes to building the UI in our Android apps since the introduction of &lt;a href="https://android-developers.googleblog.com/2021/07/jetpack-compose-announcement.html" rel="noopener noreferrer"&gt;Jetpack Compose&lt;/a&gt;, we've gone from the days of requiring 3 separate components to display a simple list(no shades to RecyclerView), to achieving the same with just a simple composable - 😍 the &lt;code&gt;LazyColumn&lt;/code&gt;. But the thing with new technologies is that they often introduce a new level of complexity, in the case of Compose minimizing the number of re-compositions can be a very huge performance booster. This article aims at providing tips/techniques on how to do just that.&lt;/p&gt;

&lt;p&gt;Before jumping right in we first need to understand how a composable is rendered on screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp85uulgi5yisusaal5dv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp85uulgi5yisusaal5dv.png" alt="compose phases" width="800" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Composition:&lt;/strong&gt; In this initial phase, Compose constructs a UI tree, a hierarchical representation of your UI. It executes your composable functions and reading state and builds the structure of your UI. This phase determines what UI elements to display and their relationships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layout:&lt;/strong&gt; Once the UI tree is established, the Layout phase takes over. It measures and positions each UI element within the tree, calculating its size and placement on the screen. This phase ensures that elements are arranged correctly and don't overlap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drawing:&lt;/strong&gt; Finally, the Drawing phase renders the UI to the screen. It takes the layout information generated in the previous phase and translates it into pixels, visually displaying your app's interface. This phase uses the device's graphics hardware to optimize performance.&lt;/p&gt;

&lt;p&gt;When the device configuration(e.g, a screen rotation) or a state that the Composable depends on changes, the Composable is "re-composed". meaning it goes through these three phases again, updating the UI if necessary. This ensures that the UI remains consistent with the current state and configurations.&lt;/p&gt;

&lt;p&gt;Now that we understand how Compose renders its UI components, let's jump into optimizing our apps.&lt;/p&gt;
&lt;h3&gt;
  
  
  Writing stable Composables
&lt;/h3&gt;

&lt;p&gt;One way of reducing unnecessary recomposition in your app is by writing stable composables, but what do I mean by that? Stable composables are composables whose parameters are immutable or can be determined to have not changed between recompositions, on the other hand, Unstable composables have parameters that are mutable or cannot be determined to have changed, requiring full recomposition even if only a portion of the data has changed.&lt;/p&gt;

&lt;p&gt;take the code block below for example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;ItemList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage with un-stable state&lt;/span&gt;
&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;MainScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;  &lt;span class="nf"&gt;mutableListOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Banana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cherry"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;DisplayItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while this seems normal, this code would always cause DisplayItems to recompose even when the &lt;code&gt;items&lt;/code&gt; hasn't particularly changed, this is because &lt;code&gt;items&lt;/code&gt; is of a mutable data type, and the compose compiler doesn't know when it actually changes, so to err on the side of caution re-composition is always triggered. To fix this we can use the &lt;code&gt;DisplayItems&lt;/code&gt; composable this way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;MainScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Immutable list is stable&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;remember&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Banana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cherry"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="nc"&gt;DisplayItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;items&lt;/code&gt; is stable because it’s created as an immutable list and wrapped in &lt;code&gt;remember&lt;/code&gt;, meaning Compose will treat it as stable and won’t trigger recompositions unless items actually changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use derivedStateOf for Computed States
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;derivedStateOf&lt;/code&gt; in Compose is helpful when you need to create a computed state that depends on other states, especially if the computation is expensive or the UI doesn’t need constant updates. With using this method computed values could trigger recompositions anytime the dependent state changes, even if the actual computed value doesn’t change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;SearchScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;query&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;remember&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;mutableStateOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Banana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cherry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// Filtering directly without derivedStateOf&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;filteredItems&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ignoreCase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;Column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;onValueChange&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;filteredItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, &lt;code&gt;filteredItems&lt;/code&gt; is recomputed every time the user types in the search bar, even if the filtered list remains unchanged, which isn't really efficient. With &lt;code&gt;derivedStateOf&lt;/code&gt;, we ensure that filteredItems only updates when the query value actually changes, minimizing recompositions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;SearchScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;query&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;remember&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;mutableStateOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Banana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cherry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// Using derivedStateOf for optimized filtering&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;filteredItems&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;derivedStateOf&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ignoreCase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;Column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;onValueChange&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;filteredItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using Lazy Composables Effectively
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;LazyColumn&lt;/code&gt; and &lt;code&gt;LazyRow&lt;/code&gt; components in Compose are handy, but they’re only as efficient as your use of them. Avoid nesting too many Lazy components, and use &lt;code&gt;item&lt;/code&gt; scopes smartly to minimize view updates. Use the &lt;code&gt;LazyListState&lt;/code&gt; to manage scroll positions and track updates efficiently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the Profiler
&lt;/h3&gt;

&lt;p&gt;One of the most reliable ways to ensure an optimized app is by measuring its performance. The Android Studio Profiler is like a diagnostic tool, showing you where bottlenecks are in your app. Keep an eye on the CPU and memory usage of composables, particularly the frequency of recomposition events. You can also consider using the &lt;strong&gt;'Profile GPU Rendering' *&lt;em&gt;or *&lt;/em&gt;'Profile HWUI Rendering'&lt;/strong&gt; tool in your device's developer options settings screen, to view frame rendering times and ensure smooth animations. For Compose, a smooth operation is usually 60 frames per second or better.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Jetpack Compose offers plenty of ways to write clean, fast, and stunning apps. And just like a car needs regular maintenance, your app needs its optimizations to stay slick and speedy. In my experience, a few small adjustments can prevent a lot of user frustration and keep them coming back to your app. Keep building &amp;amp; write clean code :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dependency Injection in Jetpack Compose with Koin</title>
      <dc:creator>David Rufai</dc:creator>
      <pubDate>Tue, 10 Sep 2024 13:02:31 +0000</pubDate>
      <link>https://dev.to/xheghun/dependency-injection-in-jetpack-compose-with-koin-a-koincidence-134a</link>
      <guid>https://dev.to/xheghun/dependency-injection-in-jetpack-compose-with-koin-a-koincidence-134a</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxripgzj6hlwuvi7hx6ie.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxripgzj6hlwuvi7hx6ie.jpg" alt="post image" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you ever felt like your code is drowning in dependencies, tangled like some Italian spaghetti? Fear not! Dependency Injection (DI) is here to save the day, and this time with an elder wand &lt;strong&gt;Koin&lt;/strong&gt; 😊, seriously, it’s as simple as flipping a coin 🌚! Let's dive into how you can inject dependencies in your Jetpack Compose app and give your code some much-needed structure—without breaking the spacetime continuum.&lt;/p&gt;

&lt;h3&gt;
  
  
  Well, what's the Deal with Koin?
&lt;/h3&gt;

&lt;p&gt;Koin is a lightweight DI framework built in Kotlin. Think of it as a friendly neighborhood library that helps you manage your dependencies without all the boilerplate code. It’s like hiring an assistant who knows where everything is and hands it to you when you need it. Ready to make your code cleaner? Let’s get started!&lt;/p&gt;

&lt;h4&gt;
  
  
  1: Add Koin to your project
&lt;/h4&gt;

&lt;p&gt;First, you’ll need to add Koin to your project. Head to your app module build.gradle file and include the following lines in the dependencies section and sync gradle files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="k"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"io.insert-koin:koin-android:3.1.6"&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"io.insert-koin:koin-androidx-compose:3.1.6"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;that's it, we've just summon our very own Elder wand. &lt;/p&gt;

&lt;h4&gt;
  
  
  2: Define Your Modules
&lt;/h4&gt;

&lt;p&gt;Think of a Koin module as your favorite meal prep service—it organizes all your dependencies so you don’t have to scramble at the last minute. Here’s how you can define a module to serve up some dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;appModule&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;module&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;single&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// Singleton pattern&lt;/span&gt;
    &lt;span class="nf"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// Factory pattern&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this module:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have a Repository that’s served as a single instance (singleton).&lt;/li&gt;
&lt;li&gt;The ViewModel gets created fresh every time, with the Repository injected like magic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3: Start Koin in Your App
&lt;/h4&gt;

&lt;p&gt;Before you can start injecting stuff, you need to initialize Koin in your app’s Application class. Don't worry, it’s a one-time setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;startKoin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;androidLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;androidContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="nd"&gt;@MyApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: Don't forget to define your application class in your app's &lt;code&gt;AndroidManifest.xml&lt;/code&gt; file using the &lt;code&gt;android:name&lt;/code&gt; attribute in the &lt;code&gt;application&lt;/code&gt; tag.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4: Inject in Jetpack Compose
&lt;/h4&gt;

&lt;p&gt;Alright! How do we inject dependencies into our Jetpack Compose UI? It’s Surprisingly easy, no rituals, sermons, or incantations needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;MyScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Data: $data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic here is get()—it tells Koin to inject the necessary dependencies. And just like that, you’ve turned a complicated mess into a clean, organized flow. You’re a magician at this point. 🎩✨&lt;/p&gt;




&lt;p&gt;With Koin, you can wave goodbye to manual dependency management and say hello to clean, testable, and maintainable code. Your codebase &amp;amp; anyone &lt;br&gt;
 else who works on it will thank you, and you’ll wonder why you ever did it the hard way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion 🏁
&lt;/h3&gt;

&lt;p&gt;Koin makes dependency injection in Jetpack Compose a walk in the park. It’s lightweight, easy to use, and saves you from writing boilerplate code—like a pocket-size superhero for your app. So, go ahead, flip the "Koin", and let it work its magic in your next project! Just remember, with great power comes great injectability.&lt;/p&gt;

&lt;p&gt;Happy coding, and may your dependencies always be well-injected! 😌&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Understanding Dependency Injection</title>
      <dc:creator>David Rufai</dc:creator>
      <pubDate>Sun, 18 Aug 2024 18:07:08 +0000</pubDate>
      <link>https://dev.to/xheghun/a-guide-to-dependency-injection-2cf5</link>
      <guid>https://dev.to/xheghun/a-guide-to-dependency-injection-2cf5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftldf6axiok24yaouf5z3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftldf6axiok24yaouf5z3.png" alt="Dependency injection image" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine you're working on an app that requires various components to interact seamlessly. You’ve written a class to handle user authentication, but it directly creates instances of several dependencies network services, data storage, and logging utilities. It works well at first, but as the project grows, testing becomes a nightmare. Every time you make a change, you must modify multiple classes, and mocking these dependencies for unit tests feels like a battle. You start to realize that your tightly coupled code is dragging down the entire project.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Dependency Injection&lt;/strong&gt; comes to the rescue.&lt;/p&gt;




&lt;h3&gt;
  
  
  Dependency Injection? What the heck is that?
&lt;/h3&gt;

&lt;p&gt;Dependency Injection (DI) is a software engineering technique often used in object-oriented software development to handle object relationships. Rather than having an object create or manage its required components (dependencies), DI involves providing these components from an external source. This approach is like giving an object the tools it needs instead of letting it gather them. By implementing DI, developers can create systems where different parts are less tightly interconnected. This results in easier to modify, test, and maintain applications over time. It also allows seamless flexibility in swapping out components without affecting the overall system structure.&lt;/p&gt;

&lt;p&gt;Here's a very basic example in &lt;a href="https://kotlinlang.org/" rel="noopener noreferrer"&gt;Kotlin&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GenericEngine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"engine started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;engine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenericEngine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

   &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the Car is responsible for creating its engine, which seems pretty normal for now, right? But what happens when we want to build a car with a different type of engine? Do we create a different car class and then define our custom engine in it, or just change the GenericEngine instance to a different engine instance? This approach has some issues: the former being having to write more code for just a slightly different behavior, and the latter being having to modify a field which could cause breaking changes. This could go south quickly in large-scale applications. Fortunately for us, we have a magic trick up our sleeves... drumroll 🥁 ...Dependency Injection!!! 🙌&lt;/p&gt;

&lt;p&gt;We could avoid all of that unnecessary modification by just requiring the engine to be passed as a dependency. This allows us to easily modify the engine type on our class object (talk about an upgrade 😉). Enough talk, here's how we do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// Engine is injected via the constructor&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Car is driving"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;engine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// engine dependency is created outside the Car class&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;car&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// dependency is injected into the Car class&lt;/span&gt;
    &lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could take this a step further by defining an Engine &lt;a href="https://kotlinlang.org/docs/interfaces.html" rel="noopener noreferrer"&gt;interface&lt;/a&gt;. This provides more flexibility, enabling us to swap out different implementations of the dependency without changing the dependent class.&lt;/p&gt;

&lt;p&gt;First, we define our interface and also two contracts (start &amp;amp; stop) that all engines must follow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now let's create two implementations of the &lt;code&gt;Engine&lt;/code&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PetrolEngine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"engine started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"engine stopped"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ElectricEngine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Electric engine started, battery on 12% please find a charging station soon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"electric engine stopped"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now our &lt;code&gt;Car&lt;/code&gt; class would depend on the &lt;code&gt;Engine&lt;/code&gt; interface and not a specific implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Car is moving"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Car stopped turning off engine"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;finally here's how we can use both implementations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;petrolEngine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PetrolEngine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;electricEngine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ElectricEngine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;carWithPetrolEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;petrolEngine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;carWithPetrolEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;carWithElectricEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;electricEngine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;carWithElectricEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach using an interface is more scalable and is a very common practice in software design, especially when building apps that need to be easily modified. &lt;/p&gt;

&lt;p&gt;I guess now we could easily solve the world's energy crisis by implementing an &lt;code&gt;Engine&lt;/code&gt; that runs on water 😅. &lt;/p&gt;

</description>
      <category>android</category>
      <category>mobile</category>
      <category>kotlin</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
