Jump to content

How I halved apk size

Ruslan Sibgatullin

1782 views

1*u_5yDFEpoTD3ST_bn0dgfQ.jpegOriginally posted on Medium

You coded your game so hard for several months (or even years), your artist made a lot of high-quality assets, and the game is finally ready to be launched. Congratulation! You did a great job. Now take a look at the apk size and be prepared to be scared. What is the size — 60, 70 or even 80 megabytes? As it might be sounds strange to hear (in the era of 128GB smartphones) but I have some bad news — the size it too big.

That’s exactly what happened to me after I’ve finished the game Totem Spirits. In this article I want to share several advises about how to reduce the size of a release apk file and yet not lose the quality.

Please, note, that for development I used quite popular game development engine Libgdx, but tips below should be applicable for other frameworks as well. Moreover, my case is about rather simple 2D game with a lot of sprites (i.e. images), so it might be not that useful for large 3D products.

 

 

 

To keep you motivated to read this article further I want to share the final result:

I managed to halve the apk size — from 64MB to 32.36MB.

Memory management

The very first thing that needs to be done properly is a memory management. You should always have only necessary objects loaded into the memory and release resources once they are not in use. This topic requires a lot of details, so I’d rather cover it in a separate article.

Next, I want to analyze the size of current apk file. As for my game I have four different types of game resources:

1. Intro — the resources for intro screen.

1*sC1w5idpfpfhzGSIL9_E8Q.jpeg

Intro background

Loaded before the game starts, disposed immediately after the loading is done. (~0.5MB)

2. In menu resources — used in menu only (location backgrounds, buttons, etc). Loaded during the intro stage and when a player exits a game level. Disposed during “in game resources” loading. (~7.5MB images + ~5.4MB music)

3. In game resources — used on game levels only (objects, game backgrounds, etc.). Loaded during a game level loading, disposed when a player exits the game level. Note, that those resources are not disposed when a player navigates between levels (~4.5MB images + ~10MB music)

4. Common — used in all three above. Loaded during the intro stage, disposed only once the game is closed. This one also includes fonts. (~1.5MB).

The summed size of all resources is ~30MB, so we can conclude that the size of apk is basically the size of all its assets. The code base is only ~3MB. That’s why I want to focus on the assets in the first place (still, the code will be discussed too).

Images optimization

The first thing to do is to make the size of images smaller while not harming the quality. Fortunately, there are plenty services that offer exactly this. I used this one.

This resulted in 18MB reduction already! Compare the two images below:

1*9A23phO-TVVQN11IBzJYhA.png

Not optimized

1*kmzB4p8v9MNSujGpX1gEPg.png

Optimized

the sizes are 312KB and 76KB respectively, so the optimized image is 4 times smaller! But a human eye can’t notice the difference.

Images combination

You should combine the same images programmatically rather than having almost the same images (especially if they are quite big). Consider the following example:

1*vn0LQGYxKlyEv6GWENs18A.jpeg

Before

1*7q6CTo6Gr1z6CWHvkU_f2A.png

After

1*wqQS_j0RgQbpT7QqzHldNw.png

God of Fire

1*GC097jZ5LGWJIyzscAV8bA.png

God of Water

Rather than having four full-size images with different Gods but same background I have only one big background image and four smaller images of Gods that are then combined programmatically into one image. Although, the reduction is not so big (~2MB) for some cases it can make a difference.

Images format

I consider this as my biggest mistake so far. I had several images without transparency saved in PNG format. The JPG version of those images is 6 times more lightweight! Once I transformed all images without transparency into JPG the apk size became 5MB smaller.

Music optimization

At first the music quality was 256 kbps. Then I reduced it to 128 kbps and saved 5MB more. Still think that tracks can be compressed even more. Please, share in comments if you ever used 64 kbps in your games.

Texture Packs

This item might be a bit Libgdx-specific, although I think similar functionality should exist in other engines as well. Texture pack is a way to organize a bunch of images into one big pack. Then, in code you treat each pack as one unit, so it’s quite handy for memory management. But you should combine images wisely. As for my game, at first I had resources packed quite badly. Then, I separated all transparent and non-transparent images and gained about 5MB more.

Dependencies and Optimal code base

Now let’s see the other side of development process — coding. I will not dive into too many details about the code-writing here (since it deserves separate article as well). But still want to share some general rules that I believe could be applied to any project. The most important thing is to reduce the quantity of 3d party dependencies in the project. Do you really need to add Apache Commons if you use only one method from StringUtils? Or gson if you just don’t like the built-in json functionality? Well, you do not. I used Libgdx as a game development engine and quite happy with it. Quite sure that for the next game I’ll use this engine again. Oh, do I need to say that you should have the code to be written the most optimal way? :) Well, I mentioned it.

Although, the most of the tips I’ve shared here can be applied at the late development stage, some of them (especially, optimization of memory management) should be designed right from the very beginning of a project.

Stay tuned for more programming articles!



4 Comments


Recommended Comments

Always interesting to hear experiences! :) I guess the runtime footprint and the APK size are quite interrelated, did you have any problems on devices from runtime memory use which encouraged you to load resources only when required? Or was it more a pre-emptive measure to prevent problems on smaller devices (difficult to test there are so many!)?

Another good trick with the multiple similar images is to do a 'diff' on them, and then instead of saving the whole image each time, just save the difference. If a lot of the values are the same, the diff will be zero and this will compress well.

Share this comment


Link to comment

Hi lawnjelly! Actually, that way to organize resources loading is one of the "best practices" suggested by libgdx devs. So it is more a pre-emptive measure indeed. But I even tested the game on my old Acer Liquid MT (released 7 years ago) and everything works smoothly :) 

The trick about images diff sounds complicated, have you ever used it in a real project? 

Share this comment


Link to comment

All the time, saving the difference between values rather than an absolute value is one of the most fundamental compression methods. I've used in it images, video, audio, animation data, both as part of my own compression, or as a pre-process before applying other compression (like png). And nearly all compression formats you use everyday will use the idea. :)

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By CommanderLake
      I've been experimenting with my own n-body simulation for some time and I recently discovered how to optimize it for efficient multithreading and vectorization with the Intel compiler. It did exactly the same thing after making it multithreaded and scaled very well on my ancient i7 3820 (4.3GHz). Then I changed the interleaved xy coordinates to separate arrays for x and y to eliminate the strided loads to improve AVX scaling and copy the coordinates to an interleaved array for OpenTK to render as points. Now the physics is all wrong, the points form clumps that interact with each other but they are unusually dense and accelerate faster than they decelerate causing the clumps to randomly fly off into the distance and after several seconds I get a NaN where 2 points somehow occupy exactly the same x and y float coordinates. This is the C++ DLL:
      #include "PPC.h" #include <thread> static const float G = 0.0000001F; const int count = 4096; __declspec(align(64)) float pointsx[count]; __declspec(align(64)) float pointsy[count]; void SetData(float* x, float* y){ memcpy(pointsx, x, count * sizeof(float)); memcpy(pointsy, y, count * sizeof(float)); } void Compute(float* points, float* velx, float* vely, long pcount, float aspect, float zoom) { #pragma omp parallel for for (auto i = 0; i < count; ++i) { auto forcex = 0.0F; auto forcey = 0.0F; for (auto j = 0; j < count; ++j) { if(j == i)continue; const auto distx = pointsx[i] - pointsx[j]; const auto disty = pointsy[i] - pointsy[j]; //if(px != px) continue; //most efficient way to avoid a NaN failure const auto force = G / (distx * distx + disty * disty); forcex += distx * force; forcey += disty * force; } pointsx[i] += velx[i] -= forcex; pointsy[i] += vely[i] -= forcey; if (zoom != 1) { points[i * 2] = pointsx[i] * zoom / aspect; points[i * 2 + 1] = pointsy[i] * zoom; } else { points[i * 2] = pointsx[i] / aspect; points[i * 2 + 1] = pointsy[i]; } /*points[i * 2] = pointsx[i]; points[i * 2 + 1] = pointsy[i];*/ } } This is the relevant part of the C# OpenTK GameWindow:
      private void PhysicsLoop(){ while(true){ if(stop){ for(var i = 0; i < pcount; ++i) { velx[i] = vely[i] = 0F; } } if(reset){ reset = false; var r = new Random(); for(var i = 0; i < Startcount; ++i){ do{ pointsx[i] = (float)(r.NextDouble()*2.0F - 1.0F); pointsy[i] = (float)(r.NextDouble()*2.0F - 1.0F); } while(pointsx[i]*pointsx[i] + pointsy[i]*pointsy[i] > 1.0F); velx[i] = vely[i] = 0.0F; } NativeMethods.SetData(pointsx, pointsy); pcount = Startcount; buffersize = (IntPtr)(pcount*8); } are.WaitOne(); NativeMethods.Compute(points0, velx, vely, pcount, aspect, zoom); var pointstemp = points0; points0 = points1; points1 = pointstemp; are1.Set(); } } protected override void OnRenderFrame(FrameEventArgs e){ GL.Clear(ClearBufferMask.ColorBufferBit); GL.EnableVertexAttribArray(0); GL.BindBuffer(BufferTarget.ArrayBuffer, vbo); mre1.Wait(); are1.WaitOne(); GL.BufferData(BufferTarget.ArrayBuffer, buffersize, points1, BufferUsageHint.StaticDraw); are.Set(); GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, 0); GL.DrawArrays(PrimitiveType.Points, 0, pcount); GL.DisableVertexAttribArray(0); SwapBuffers(); } These are the array declarations:
      private const int Startcount = 4096; private readonly float[] pointsx = new float[Startcount]; private readonly float[] pointsy = new float[Startcount]; private float[] points0 = new float[Startcount*2]; private float[] points1 = new float[Startcount*2]; private readonly float[] velx = new float[Startcount]; private readonly float[] vely = new float[Startcount]; The compiled x64 AVX versions can be downloaded from my server here:
      How it should work:
      Single thread: http://commanderlake.net/PPsingle.7z Multithread: http://commanderlake.net/PPmulti.7z   Current broken version: http://commanderlake.net/PP.7z   Edit 0: It seems that adding 3 zeros to G increases the accuracy of the simulation but I'm at a loss as to why its different without interleaved coordinates. Edit 1: I somehow achieved an 8.3x performance increase with AVX over scalar with the new code above!
    • By Gafami
      Dear community,
      I would like inform you that "War IOM" v2 have been re-work graphics and added more feature to serve user experience.
      Here is my banner screenshot: 

      You can access to the website and play it directly on your phone (Chrome mobile, Safari iPhone, iPad) or your Laptop
      Link to play: https://www.iomgame.com/wariom/
      ++++ Game description: 
      The rule to win the game is very simple: buy the soldiers, defeat the enemy army and then destroys the flag. Try to use fireball to wipe out the enemy. 
      The game had the shop to buy new soldier, upgrade soldier and choose the team out. 
      Game data be stored on both user's device and server so never lost data again.
       
      ++++ Here is the look and feel of War IOM icon on your Home-screen phone

       
      Rumor: You can get double gem receive if you beat the mini boss at level 3. Also win level 3 will give you a lot of Gem, use it to buy new solider and upgrade your army!
      My facebook page: https://www.facebook.com/Iomgame-245553622715070/
       
      /******** Change log update 08/26 ********/
      -- Add new game play for level 6.

      -- Improve camera.
      -- Improve game performance.
      /******** Change log update 08/31 ********/
      -- Improve sound load time. No painful for waiting sound loading any more!
      /******** Change log update 19/09 ********/
      -- Add level 7 with new game play.
      Regards,
      Gafami
    • By 3dmodelerguy
      For reference I am use Unity as my game engine and the A* Pathfinding Project for path finding as there is no chance I would be able to create anything close to as performant as that in any reasonable amount of time.
      So I am looking to build a game that is going to have a very similar style as Prison Architect / Rim World / SimAirport / etc. One of the things that I assume is going to effect performance is path finding. Decisions about the game I have already made that I think relate to this are:
      1. While I am going to be using Colliders, all of them will be trigger colliders so everything can pass through each other and I will not be use physics for anything else as it has no relevance for my game
      2. I am going to want to have a soft cap at the map size being 300x300 (90,000 tiles), I might allow bigger sizes but do something like Rim World does in warning the player about possible side effect (whether it be performance or gameplay)
      3. The map will be somewhat dynamic in that the user will be able to build / gather stuff from the map but outside of that, it should not change very much
      Now I am going to build my game around the idea that users would be in control of no more than 50 pawns at any given time (which is something I can probably enforce through the game play) but I am also going to want to have number other pawns that are AI controlled on the map (NPCs, animals, etc.) that would also need path finding enabled. Now I did a basic test in which I have X number of pawns pick a random location in the 300 x 300 map. move towards it, and then change the location every 3-5 seconds. My initial test was pretty slow (not surprising as I was calculating the path every frame for each pawn) so I decided to cache the calculated path results and only update it ever 2 seconds which got me:
      100 pawns: 250 - 450 FPS
      150 pawns: 160 - 300 FPS
      200 pawns: 90 - 150 FPS
      250 pawns: 50 - 100 FPS
      There is very little extra happening in the game outside of rendering the tilemap.
      I would imagine the most pawns on the map at a given time that need path finding might be a 1000 (and I would probably be able to make due with like 500 - 600). Now obviously I would not need all the pawn to be calculation paths every 2 seconds nor would they need to be calculating paths that are so long but even at a 5 second path refresh rate and paths that are up to 10 tiles long, I am still only able to get to about 400 pawns before I start to see some big performance issues. The issue with reducing the refresh rate is that there are going to be cases where maybe a wall is built before the pawns path is refreshed having them walk through the wall but not sure if there is a clean way to update the path only when needed.
      I am sure when I don't run the game in the Unity editor I will see increase performance but I am just trying to figure out what things I could be doing to make sure path finding is as smaller of a performance hit as possible as there is a lot of other simulation stuff I am going to want to run on top of the path finding.
    • By Ales Velek
      Hi, this is my first finished game ever.
      It took my 2 years because Im little bit lazy.
      Its simple game where you defend the castle agains random spawned enemies. Lots of flying physics objects included :). 
      What do you think?
      Video
      Google play link



    • By Gerhart
      Hi,
      I want to learn to program games for PC and maybe Android. Sorry in advance if i don't know the correct terms.
       
      The game i have in mind will be relative simple (at least that's what i hope). It should be turn based with a map and different locations the player can switch in between. At the different locations there will be simple minigames, sometimes withwith simple animations. There is a storyline, the player sometimes can chose between different outcomes. I would like to include character progression in form of attributes and an inventory and i also would like to include a battle system (turn based).
      I've seen that there are some flash games out there who have similiar elements as i have planned for my game. The thing is, i don't know anything about flash and read, that it's not worth it to learn it anymore.
      Since i have some basic skills in html and css, i thought it would be better to give html5 and javascript a try (i have to learn javascript though).
      But iam not sure if it's really a good choice, since all html5 games i've seen so far are either actionshooters and/or have a really crappy graphics compared to flash games. In addition to that, i have no clue if the game i want to create is possible with either flash or html5/javascript. Another issue is, that flash needs adope flash and that's about 900 $ a year, money i don't have at the moment. It's a while ago, since i have made something with html and css, but i remember that there where a lot of problems with compatibility and the different browsers. I assume the same problems still exist and are true for games too?
      I would be really happy if someone could enlighten me, what would be the best possibilities for me to get in the gaming buisiness. What would be the best way, and what do i have to learn?
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!