<?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>Forem: ZhiBo Geng</title>
    <description>The latest articles on Forem by ZhiBo Geng (@clwater).</description>
    <link>https://forem.com/clwater</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%2F1030645%2Fbbef443b-4961-498a-9ecb-b04020a3df89.jpeg</url>
      <title>Forem: ZhiBo Geng</title>
      <link>https://forem.com/clwater</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/clwater"/>
    <language>en</language>
    <item>
      <title>What did I gain when I first coded a Canvas using Kotlin and Compose?</title>
      <dc:creator>ZhiBo Geng</dc:creator>
      <pubDate>Thu, 27 Apr 2023 06:02:27 +0000</pubDate>
      <link>https://forem.com/clwater/what-did-i-gain-when-i-first-coded-a-canvas-using-kotlin-and-compose-1lcm</link>
      <guid>https://forem.com/clwater/what-did-i-gain-when-i-first-coded-a-canvas-using-kotlin-and-compose-1lcm</guid>
      <description>&lt;h1&gt;
  
  
  When I first coding a Canvas using Kotlin and Compose, what did I gain?
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Since Google recommended Kotlin as the preferred language for Android development in 2019, nearly four years have passed, Compose 1.0 version has been released for almost two years now. However, Kotlin + Compose is not yet the preferred choice in Android development. Should we still consider trying this combination? What benefits will this choice bring us?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For me, I am both conservative and open to new things. Since early 2018, I have experimented with using Kotlin to develop some Android projects (&lt;a href="https://www.clwater.top/2018/01/26/%E8%B4%9D%E5%A1%9E%E5%B0%94%E6%9B%B2%E7%BA%BF/"&gt;Android For Bezier&lt;/a&gt;), but it has never been my preferred language for Android development. However, with the growing Kotlin and Compose community, more and more people trying this combination and contributing to its ecosystem. Thanks to seamless integration of Kotlin and Java, more developers, including myself,  are experimenting with Kotlin or Kotlin+Compose to existing development projects. In this article, I will share some of these gains and thoughts I have encountered while using this combination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Staring with Custom Canvas
&lt;/h2&gt;

&lt;p&gt;I would like to show you a Gif first. The animation design is not created by me, but I redesigned it with some modifications to better suit the actual functional requirements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nUPs9-Lc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/ezgif.com-crop.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nUPs9-Lc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/ezgif.com-crop.gif" alt="Canavs" width="596" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am quite satisfied with the final result as I added many other animation effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  First to show my gains and thoughts.
&lt;/h2&gt;

&lt;p&gt;Usually, I explain my design-to-code progress step by step. However, this time I would like to first share the insights and reflections I gained from this project because the overall process of designing and coding has no fundamental difference from my past Java development. Of course, I will still provide a detailed explanation of this Canvas code later on.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Kotlin and Java&lt;br&gt;
I won't go into much detail about the comparison and relationship between Kotlin and Java, but through coding this canvas, I gained a much deeper understanding of Kotlin's usage and concepts. While the two languages have many similarities, it is important to avoid bringing habits from one language into the other when using them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Compose&lt;br&gt;
Initially, I attempted to build a desktop tool for Gist using Compose, But I encountered various issues and couldn't complete the project due to a lack of resources. It wasn't until I actually used Compose in a real project that I fully understand how it can be applied.  It's clear that this experience is common among developers who have focused on Android development for a long time. Simply relying on others' explanations is not enough to fully grasp the benefits of Compose.&lt;/p&gt;

&lt;p&gt;One of the biggest differences is the reactive layout approach that Compose uses, which is very different from the habits we develop when working with traditional XML layouts.&lt;/p&gt;

&lt;p&gt;When we use XML to build our layout(or even build directly using &lt;strong&gt;View.add()&lt;/strong&gt;), we first create a view and hold its "handle". We can use this handle to make any modifications to the view at any time and in any place.&lt;/p&gt;

&lt;p&gt;When we use Compose for layout, we first define the Composables(which are on the same level as Views) and tell them what should do in certain situations. This can be compared to the YuTi moon explorer car, where you can add various tools(such as tracks and cameras) while it is on the ground. However, once it's launched to the moon, even a small change to its surface pattern is no longer possible. Similarly, Composables in Compose are defined upfront and can't be modified on the fly like Views.&lt;/p&gt;

&lt;p&gt;During the initial stages of Coding, I had a traditional way of thinking where I would implement functionalities first and gradually add related features while checking their effects. However, when I returned to some of the functionalities, I have to make significant changes to the existing code in a few cases. I realized that this issue is often hidden when we work with Views and have their handles. Here is my biggest mindset shift that took place during this project: "Don't implement something if you haven't designed it yet."&lt;/p&gt;

&lt;p&gt;To put it differently, using Compose is similar to using other Builders. We can freely manipulate Composeables before calling the build() method. However, after we call it, we can no longer control them as we please. This is similar to the behavior of  &lt;strong&gt;AlertDialog.Builder().create()&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kotlin Compose and Java XML&lt;br&gt;
I attempted to use Kotlin+Compose throughout the entire codebase to implement various functionalities seamlessly, much like how Kotlin and Java can easily interoperate with each other. Compose and View are also easy to integrate with each other. However, due to my lack of experience and unfamiliarity with Kotlin Compose, I encountered difficulties in implementing some seemingly simple features. In some cases, I had to resort to using older methods to achieve certain effects. Despite this, I still ended up using &lt;code&gt;ValueAnimator&lt;/code&gt; in some parts rather than &lt;code&gt;rememberInfiniteTransition&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Will make me use more Kotlin and Compose in this experience?&lt;br&gt;
After using it for some time(not just in this demo, but also in actual development), I believe I will continue to experiment with writing more Kotlin code and using Compose to build layouts. Kotlin generally results in less code than Java, but with the various development tools available today(such as Copilot and ChatGpt), the emphasis on "writing less code" may not be as attractive as before. However, as the saying goes &lt;em&gt;"纸上得来终觉浅,绝知此事要躬行"&lt;/em&gt;(Chinese proverb, means "Reading books is like scraping the surface of knowledge, real understanding comes from practicing and experiencing."). After using Kotlin and Compose for some time, I have found that writing less code not only means less code, but also less complexity in understanding, modifying, and maintaining it. Of course, this is all based on a solid foundation are requires an understanding of certain features and conventions. Otherwise, we may only as questions such as "What does this place of code do?" and "Why it is designed this way?", (Sure? coroutines).&lt;/p&gt;

&lt;p&gt;If we were to compare the learning difficulty of Java to a linear curve, then I think the difficulty curve of learning Kotlin would resemble a parabolic shape. Kotlin is easier to get started with Java, but as you dive deep into it. its many conversions and features can make it more challenging than Java in the intermediate stage. Of course, it requires continuous learning and practice to achieve mastery in the advanced stage.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basic knowledge pitfalls&lt;br&gt;
When I first started learning Kotlin, I thought of it as just another implementation of Java and figured that it would be straightforward to use. However, just like the relationship between View and Compose, although both can easily interact with each other, View is still View and Compose is still Compose. Kotlin is Kotlin adn Java is Java. They both provide different approaches to accomplish the same goals. So when using Kotlin and Compose, it's important to let go of old ways of thinking and understand that Kotlin is not a replacement for Java, but rather a different path to achieving similar outcomes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you don't want to keep modifying your code repeatedly, complete your design first.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Look us Canvas
&lt;/h2&gt;

&lt;p&gt;It is the background, Sun cloud, Night star, Sun, and Moon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u8jGLzSs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/background_1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u8jGLzSs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/background_1.gif" alt="Background" width="600" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To clearly present the design of the canvas, the background is not restricted in terms of the display range. Here we have used four circles with different radii but centered at the same point to simulate the effect of light at different levels.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dmvq-xOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/background_2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dmvq-xOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/background_2.gif" alt="SunCloud" width="600" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For ease of visibility, the sun clouds have been changed to blue. The cloud consists of 7 2-level circles of different sizes and positions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JvKci6xH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/background_3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JvKci6xH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/background_3.gif" alt="NightStar" width="600" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Night stars are also simple, and can be displayed as a diamond shape at random positions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V4rab2YO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/%25E5%25B1%258F%25E5%25B9%2595%25E6%2588%25AA%25E5%259B%25BE%25202023-04-24%2520114532.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V4rab2YO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/%25E5%25B1%258F%25E5%25B9%2595%25E6%2588%25AA%25E5%259B%25BE%25202023-04-24%2520114532.png" alt="Sun" width="454" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The density of the sun is very simple, with just a color-changing effect added.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LZgwbTGE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/background_4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LZgwbTGE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/background_4.gif" alt="Moon" width="556" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The design of the moon is also simple, with just a  rotating effect added.&lt;/p&gt;

&lt;p&gt;The code is in &lt;a href="https://github.com/clwater/AndroidComposeCanvas"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>compos</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>What different between drawable and mipmap in Android</title>
      <dc:creator>ZhiBo Geng</dc:creator>
      <pubDate>Thu, 23 Feb 2023 10:11:42 +0000</pubDate>
      <link>https://forem.com/clwater/what-different-between-drawable-and-mipmap-in-android-h98</link>
      <guid>https://forem.com/clwater/what-different-between-drawable-and-mipmap-in-android-h98</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In old project code, some images were use in the 'drawable' folder, and other in the 'mipmap' folder. Occasionally, I had doubts and searched for articles. But I an not  understand enough. Recently, I try to find it. so I checked the content of the article one by one and found that the actual results were far from the conclusion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Several common conclusions
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Case 1 drawable will remove other densities, mipmap will retain all (the final conclusion is related to this)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;When a xhdpi density phone loads this apk, Google has an optimization that will remove the other density files in the drawable folder, Just leaving only a basic drawable and the drawable-xhdpi folder, But  mipmap will retain this all.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The detection method is also relatively simple. Put the same type of image (with image text for tag) in the folders to different densities folder of drawable and mipmap, Then check this installation package and application size&lt;/p&gt;

&lt;p&gt;Case1.1 Installation package and application size&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Installation package size&lt;/th&gt;
&lt;th&gt;Application size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;drawable&lt;/td&gt;
&lt;td&gt;13.3 MB (14,016,841 bytes)&lt;/td&gt;
&lt;td&gt;14.04MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mipmap&lt;/td&gt;
&lt;td&gt;13.3 MB (14,017,191 bytes)&lt;/td&gt;
&lt;td&gt;14.04MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Conclusion1.1
&lt;/h5&gt;

&lt;p&gt;From this, although the size of two installation packages is slightly different, considering the size of image itself (each image is more than 1MB), it can be considered that there is no difference between the images placed in the drawable and mipmap folders after the installation of the application.&lt;/p&gt;

&lt;p&gt;Case1.2 In-app performance&lt;/p&gt;

&lt;p&gt;Excluding the installation package situation, let's take a look at the performance in the app (use &lt;code&gt;adb shell wm density&lt;/code&gt; to ensure density without change other info).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;100&lt;/th&gt;
&lt;th&gt;420&lt;/th&gt;
&lt;th&gt;800&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;drawable&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vG7k4FHk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192034141.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9jgC5D9a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192034231.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gN64NZHH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192034624.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mipmap&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XvxzVXMB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192032115.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y_TBCwHN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192031816.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iIVKRDzA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192033995.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Conclusion1.2
&lt;/h5&gt;

&lt;p&gt;From this, it can be seen that no matter which folder the file is placed in, it will be correctly displayed as its matching image resource on the phone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 1.3 In-app scaling
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;If an imageView have scaling animation, using the image under drawable, one image will be continuously used to scale the image to achieve imageView scaling animation.&lt;/p&gt;

&lt;p&gt;If you use the image under mipmap, the image with the closest resolution to the current resolution and larger than the current resolution will be automatically selected for scaling according.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This conclusions may be not is an common conclusions, but since there is shuch a saying, let is test it.&lt;/p&gt;

&lt;h6&gt;
  
  
  drawable
&lt;/h6&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Small Scaling Ratio&lt;/th&gt;
&lt;th&gt;Large Scaling Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uJ_4XEqs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192118872.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CDYca6qK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192118141.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  mipmap
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Small Scaling Ratio&lt;/th&gt;
&lt;th&gt;Large Scaling Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m4zV0NN6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192119332.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DMQicts2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212192119909.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Conclusion 1.3
&lt;/h5&gt;

&lt;p&gt;It can be seen that the same animation is displayed throughout the scaling animation process.&lt;/p&gt;

&lt;p&gt;Case 2 Application Performance&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Google has optimized the performance of mipmap images to make them perform better.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h6&gt;
  
  
  drawable
&lt;/h6&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Performance Overview&lt;/th&gt;
&lt;th&gt;MEMORY&lt;/th&gt;
&lt;th&gt;Average time for loading 10 images&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--36MRjy3k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201029065.png" width="880" height="242"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vw8Sqvx3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201030206.png" width="284" height="371"&gt;&lt;/td&gt;
&lt;td&gt;146&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  mipmap
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Performance Overview&lt;/th&gt;
&lt;th&gt;MEMORY&lt;/th&gt;
&lt;th&gt;Average time for loading 10 images&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--07sNvuFj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201030747.png" width="880" height="185"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7FMtQTzw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201030493.png" width="246" height="301"&gt;&lt;/td&gt;
&lt;td&gt;151&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h6&gt;
  
  
  Conclusion 2
&lt;/h6&gt;

&lt;p&gt;It can be seen that in the case when system loading a single image, their performance is basically the same, except for the case where this image is too small or too few to optimize the performance obviously. However, even in the case of repeated loading of images, the performance is still similar. Perhaps the optimization is only for specific types of images? If you know more details, feel free to communicate with me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case3 Launcher icons
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;While researching, it was found that the minmap folder is often recommended to only include the launcher icon of the application, this way can make performance optimize better.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;100dpi&lt;/th&gt;
&lt;th&gt;420dpi&lt;/th&gt;
&lt;th&gt;800dpi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CnyjyUpW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201828162.png" width="109" height="60"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I3BTsrGg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201829421.png" width="198" height="111"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I3BTsrGg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201829421.png" width="198" height="111"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Conclusion 3
&lt;/h5&gt;

&lt;p&gt;It can be seen the display of application icon is consistent across different dpi settings, and the boundary value for switching the application icon is also consistent. As for the situation why this display effect is same for 420dpi and 800dpi, because app icon need to be enlarged by about 25% when selecting image resources for various reasons. &lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At this point, you may have the same doubts as me. Since the performance of the images under drawable and mipmap is the same, whether it is in installation package or in  application itself, and even the official documentation says so, why do various test results show that the performance of the two is basically consistent?&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The Culprit: Bundle (.aab)
&lt;/h1&gt;

&lt;p&gt;If you do not use Google Play to pushing you apk(like me), you will unfamiliar adbout this. and even many who have previously published app on Google Play are not very familiar with it. This actually appears every time we manually package an app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s6-R92fn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201633188.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s6-R92fn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201633188.png" alt="image" width="675" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simply put, the .aab package is generally used for the Google Play store. When you download an app from the Google Play store, it will download resources from different drawables based on your phone's actual usage to reduce the size of the installation package. (In general, the phone's dpi does not change, and other resources densities will not be used until the app is uninstalled.)&lt;/p&gt;

&lt;p&gt;The tool used in the following test is bundletool &lt;sup id="fnref2"&gt;2&lt;/sup&gt;, which simulates the process of downloading and installing an app from Google Play.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison of installation packages
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Installation Package (apks) Size&lt;/th&gt;
&lt;th&gt;Application Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;drawable&lt;/td&gt;
&lt;td&gt;5.91 MB (6,201,543 字节)&lt;/td&gt;
&lt;td&gt;6.22MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mipmap&lt;/td&gt;
&lt;td&gt;12.6 MB (13,230,670 字节)&lt;/td&gt;
&lt;td&gt;13.26MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  In-app performance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;100&lt;/th&gt;
&lt;th&gt;420&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;drawable&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yw_1Scy9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201832564.png" width="880" height="1956"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FC9VzeXF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201831242.jpg" width="880" height="1956"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mipmap&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AHIUaWVJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201832837.jpg" width="880" height="1956"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ly_0468N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202212201832319.jpg" width="880" height="1956"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It can be seen that when the image is used on the drawable  folder, the application installed through the .aab package will be  smaller than the one used under minmap folder, and when changing the DPI in the application, it not can  longer automatically select the corresponding image according to the current DPI.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Based on the above tests, we can get the following conclusions.&lt;br&gt;
&lt;strong&gt;The following conclusions do not involve performance optimization related to mipmap (mainly because we have not yet designed a clear comparison test)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;The test phone model is Pixel 7, and the tested Android version is 13&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When the application is built as an .apk, there is no difference between drawable or mipmap folders, whether it is in the application or in the launcher (application icon).&lt;/li&gt;
&lt;li&gt;When the application is built as an .aab, the resources in the drawable folder will look for matching device density to keep, and unmatched resources will be deleted to ensure the size of the .apk. However, all resources files in the mipmap folder will be kept.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Can we place the images used in the application in any directory?
&lt;/h2&gt;

&lt;p&gt;If your application is distributed and installed through .apk, there is no difference in principle. However, Google also has recommended instructions for the related directories, as shown below:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QqmYxtOH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202302231534338.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QqmYxtOH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://clwater-obsidian.oss-cn-beijing.aliyuncs.com/img/202302231534338.png" alt="image" width="880" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we can see, mipmap directory can only save application icons in principle. Similarly, their &lt;a href="https://github.com/orgs/android/repositories?type=all"&gt;official projects&lt;/a&gt; and &lt;a href="https://github.com/android/architecture-samples/tree/main/app/src/main/res"&gt;single density resource projects&lt;/a&gt; also use these two folders in this way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the retention mechanism of the mipmap folder in the .aab package only applicable to application icons?
&lt;/h2&gt;

&lt;p&gt;After testing, it can be found that the retention mechanism of mipmap is applicable to all image resources under mipmap, whether they are application icons or not.&lt;/p&gt;

&lt;p&gt;Related code can be accessed on my &lt;a href="https://github.com/clwater/drawable_mipmap"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://developer.android.com/training/multiscreen/screendensities#mipmap"&gt;https://developer.android.com/training/multiscreen/screendensities#mipmap&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://github.com/google/bundletool/"&gt;https://github.com/google/bundletool/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>android</category>
      <category>google</category>
    </item>
  </channel>
</rss>
