<?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: Basheer</title>
    <description>The latest articles on DEV Community by Basheer (@blue_alien).</description>
    <link>https://dev.to/blue_alien</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3976226%2Fe85d5a10-3c15-4e18-9dea-8fc991831bef.jpg</url>
      <title>DEV Community: Basheer</title>
      <link>https://dev.to/blue_alien</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/blue_alien"/>
    <language>en</language>
    <item>
      <title>Your Android image compressor might crash on Android 15 — so I built a pure-JVM one</title>
      <dc:creator>Basheer</dc:creator>
      <pubDate>Tue, 09 Jun 2026 17:54:18 +0000</pubDate>
      <link>https://dev.to/blue_alien/your-android-image-compressor-might-crash-on-android-15-so-i-built-a-pure-jvm-one-2c9p</link>
      <guid>https://dev.to/blue_alien/your-android-image-compressor-might-crash-on-android-15-so-i-built-a-pure-jvm-one-2c9p</guid>
      <description>&lt;p&gt;Android 15 quietly changed the rules for native code. Since November 2025, apps targeting API 35 on&lt;br&gt;
Google Play must support &lt;strong&gt;16 KB memory pages&lt;/strong&gt;, and every bundled &lt;code&gt;.so&lt;/code&gt; has to be 16 KB-aligned —&lt;br&gt;
including the ones inside your third-party libraries. An unaligned native lib doesn't warn; it&lt;br&gt;
crashes on 16 KB-page devices.&lt;/p&gt;

&lt;p&gt;That sent me looking at how my apps compress images. The most popular option, &lt;strong&gt;Luban 2&lt;/strong&gt;, is&lt;br&gt;
excellent — but it ships &lt;strong&gt;libjpeg-turbo&lt;/strong&gt;, a native encoder. The original &lt;strong&gt;Luban&lt;/strong&gt; and its fork&lt;br&gt;
&lt;strong&gt;AdvancedLuban&lt;/strong&gt; were pure-JVM… and abandoned.&lt;/p&gt;

&lt;p&gt;So I revived that lineage as &lt;strong&gt;PixelDiet&lt;/strong&gt;: a pure-JVM, zero-native Android image compressor.&lt;/p&gt;
&lt;h4&gt;
  
  
  What "pure-JVM" buys you
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No &lt;code&gt;.so&lt;/code&gt; → no 16 KB page-size risk.&lt;/strong&gt; Nothing to align, no NDK, no per-ABI builds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiny footprint.&lt;/strong&gt; The release AAR is ~44 KB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest guarantee.&lt;/strong&gt; A CI step unzips the AAR and fails the build if it ever contains a &lt;code&gt;.so&lt;/code&gt;. The claim is enforced, not promised.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trade-off, stated plainly: you give up libjpeg-turbo's raw encode speed. In exchange you get a&lt;br&gt;
dependency that can't break on a platform page-size change and won't bloat your APK.&lt;/p&gt;
&lt;h4&gt;
  
  
  What I kept, and what I modernized
&lt;/h4&gt;

&lt;p&gt;The valuable part of Luban is its WeChat-Moments-style &lt;strong&gt;"gear" sizing strategy&lt;/strong&gt; — I ported that&lt;br&gt;
math faithfully (and unit-tested it). Around it, everything is new:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin coroutines&lt;/strong&gt; core: &lt;code&gt;suspend get()&lt;/code&gt;, &lt;code&gt;getFirst()&lt;/code&gt;, and &lt;code&gt;asFlow()&lt;/code&gt; for progress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java-friendly&lt;/strong&gt; &lt;code&gt;OnCompressListener&lt;/code&gt; path backed by &lt;code&gt;Dispatchers.IO&lt;/code&gt; — &lt;strong&gt;no RxJava&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoped-storage safe&lt;/strong&gt; inputs: &lt;code&gt;File&lt;/code&gt;, &lt;code&gt;content://&lt;/code&gt; &lt;code&gt;Uri&lt;/code&gt;, &lt;code&gt;InputStream&lt;/code&gt;, &lt;code&gt;Bitmap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;androidx ExifInterface&lt;/strong&gt; orientation handling read from streams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebP&lt;/strong&gt; lossy/lossless output (smaller than JPEG), plus JPEG/PNG.
&lt;/li&gt;
&lt;/ul&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;out&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PixelDiet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&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;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OutputFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WEBP_LOSSY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFirst&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  The feature Luban 2 removed
&lt;/h4&gt;

&lt;p&gt;Luban 2 dropped &lt;code&gt;setMaxSize&lt;/code&gt;. If your code relied on "compress to under N KB," upgrading silently&lt;br&gt;
breaks it. PixelDiet brings it back as &lt;code&gt;hardCap(kb)&lt;/code&gt; — and makes it a real guarantee with a&lt;br&gt;
two-phase &lt;strong&gt;quality-loop + resize-fallback&lt;/strong&gt;, so the output is &lt;em&gt;actually&lt;/em&gt; ≤ your target on any gear:&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="nc"&gt;PixelDiet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&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="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hardCap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getFirst&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// ≤ 200 KB, guaranteed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Does it actually compress?
&lt;/h4&gt;

&lt;p&gt;From the sample app: an 863 KB photo → &lt;strong&gt;78 KB (−91%)&lt;/strong&gt; in ~223 ms (Custom gear → PNG). With Smart&lt;br&gt;
gear + WebP + a 300 KB cap: 863 KB → 250.8 KB (−71%).&lt;/p&gt;
&lt;h4&gt;
  
  
  The part nobody writes about: licensing
&lt;/h4&gt;

&lt;p&gt;PixelDiet is a &lt;strong&gt;fork&lt;/strong&gt;, and I'm explicit about that. Luban and AdvancedLuban are Apache-2.0, which&lt;br&gt;
permits forking, renaming, and redistribution — but it has conditions: keep the original copyright&lt;br&gt;
headers, ship &lt;code&gt;LICENSE&lt;/code&gt; + a &lt;code&gt;NOTICE&lt;/code&gt; stating your changes, and don't imply endorsement. The&lt;br&gt;
compression &lt;em&gt;strategy&lt;/em&gt; is Curzibn's and shaohui's work; my contribution is the modern API, the&lt;br&gt;
scoped-storage/EXIF fixes, the native-free guarantee, and ongoing maintenance. Presenting that&lt;br&gt;
honestly is both the legal requirement and the more credible story.&lt;/p&gt;
&lt;h4&gt;
  
  
  Try it
&lt;/h4&gt;

&lt;p&gt;JitPack:&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="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.github.basheerpaliyathu.PixelDiet:pixeldiet:0.1.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code, sample app, and the full comparison table: &lt;a href="https://github.com/basheerpaliyathu/PixelDiet" rel="noopener noreferrer"&gt;https://github.com/basheerpaliyathu/PixelDiet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you maintain an Android app targeting API 35, it's worth auditing your dependencies for bundled&lt;br&gt;
&lt;code&gt;.so&lt;/code&gt; files this week. You might be one transitive dependency away from a 16 KB crash.&lt;/p&gt;

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