The back story:
As I wrote before in the Chirp Chirp topic, the situation with internal memory on Android is bad. On most devices, it’s not bad, it’s just VERY bad.
The HTC desire is a very good example here. It contains 512 MB of Read-Only Memory (ROM) and 576 MB of Random Access Memory (RAM). Even though ROM means “read only”, it is where the Android OS is installed. It’s also the location where the user interface is installed, and the apps.
Getting rid of Android to increase the available memory for new apps is obviously not an option. The user interface (the UI on top of Android, like HTC Sense) can not be fully removed without rooting the device. So that leaves a lot less memory available of those 512 MB. But hold on, it gets even worse: very useful apps like GMail (4.46 MB), Google Maps (11.30 MB), Android Market (6.05 MB) and the Youtube app (1.70 MB) are all installed right there. They cannot be moved to the SD card, so that’s more memory that’s not available for additional apps, like Cocos2d-x games.
In the end it means that on the HTC Desire, there’s about 50 MB available on average. But there’s more: it’s not possible to use all of that, because it must be shared with stuff like personal data (sms, email, etc) and most importantly, at least 30 MB of free internal memory is ALWAYS required when the vendor comes up with a ROM upgrade to install a more recent or otherwise updated version of Android.
So, on the HTC Desire, there’s only an average of 20 MB available for actual user apps. I really hope this speech gets the point accross, because, well, 20 MB is not much My point is: if you’re a developer looking into developing apps for Android, you’ll have to limit the installed file size of your apps, because otherwise the user will simply not install it, or they’ll remove your app much faster when they need space to install new apps, making you less money when you’re dependent on advertising, which seems to be one of the few methods to actually make some money on Android.
But… then there’s Cocos2d-x. Cocos2d-x is an awesome library. No doubt about that. But on my system, built using NDK-r4, it’s about 2.47 MB for just cocos2d.so alone. That’s without CocosDenshion, Chipmunk or Box2D!
Finding a way out:
Many people would just say: hey, just enable the “Move to SD” option and let the user install your app onto the SD card. Well, you could. But it’s only useful for your assets (images, sounds, etc). The actual app will not shrink in size, so when I made a nice little game using Cocos2d-x, I found it to be at least about 4 MB in size. That’s without the optimized ARMv7 code. With it, the size would literally double up, even though it’s an absolute requirement for some apps/games because floating point calculations will be MUCH faster with it. Box2D, for example, will get a huge speedup, which you cannot ignore as a developer looking into bringing high performance apps to the user. Fortunately, I didn’t use Box2D for that nice little game.
The biggest problem with “Move to SD” is that each version of Android up to 2.2 will install native libraries (like the Cocos2d-x libraries) into /data/data/appname/lib/libxxx.so , so that’s why the “Move to SD” option is useless: the native libraries will stay on the internal memory, not the SD card. Only with Android 2.3 and up, native libraries can be installed to the SD card, according to the following url (as told by Dianne Hackborn, who is an Android framework engineer): http://osdir.com/ml/android-ndk/2011-04/msg00415.html
Is there a way out of distributing Cocos2d-x apps for Android without taking away at least 4 MB of internal memory or more, from the limited amount of ~20 MB user app memory? Fortunately, yes, there is
The solutions:
- Move to SD
As mentioned before, this is only part of the solution as it will only install java code and assets to the SD card, leaving the rest of the (native) code inside the internal memory. Yes, it should be enabled, but no, it’s not the solution for fixing the issues with limited internal memory on most Android devices and making your users happier with your small sized apps.
- “APP_OPTIM := release” @ Application.mk
Now we’re getting somewhere. With this option put into Application.mk, your native libraries will lose debugging symbols and all that, but it will be much smaller. cocos2d.so (2.47 MB when built straight from Git) turns into a smaller library of 1.70 MB with this option enabled.
- Turning Cocos2d-x into a static library
This doesn’t have as much of an effect as the APP_OPTIM option, but it’s good for another 0.20 - 0.30 MB off the library size. That’s almost 1/3 of one Megabyte indeed. You can make Cocos2d-x go static by performing the following rituals:
# remove ‘cocos2d’ and other modules that should be static from Application.mk
# in Android.mk, add LOCAL_STATIC_LIBRARIES := libcocos2d (just before the include $(BUILD_SHARED_LIBRARY))
# change $(BUILD_SHARED_LIBRARY) to $(BUILD_STATIC_LIBRARY)
# in the Tests or Helloworld demo, or your own app, you should link with the following libraries:
libz, libpng, libxml2, libjpeg, libskia
Those additional libraries you’ll have to link to are normally linked into the .so file, but that won’t happen with a static library. Keep in mind that you’ll probably have to swizzle the inclusion order of these libraries to get rid of linkage errors.
The result for all of this is a static Cocos2d-x library that can be linked into your own app’s native shared library, so you’ll end up with just one library that needs to be loaded from the Java side of the code.
- GCC Visibility feature
Last, but most definitely not least, is the GCC visibility attribute. It’s a bit like __declspec for MSVC, but for GCC instead. Using this feature you can improve loading times for your native libraries and cut down on the exported symbol table, which can get as large as 5-6 MB when there’s 30,000 symbols. The compiler can also optimize your code a little better when it’s enabled.
Here’s some more information on how to use the feature, it’s quite easy:
http://gcc.gnu.org/wiki/Visibility
http://goldenhammersoftware.blogspot.com/2010/12/android-ndk-and-storage-size.html
(the person from the last article talks about stripping away debugging information, but as far as I know that’s not necessary once you’re using the release optimization in Application.mk)
The conclusion:
With all of these options it’s easily possible to make a Cocos2d-x library about 2.3 times smaller compared to what you’d get from Git. I don’t know if the 3rd party libs (the binary versions distributed with Cocos2d-x) were compiled without these options, but if they were, the results could be even better.
So here’s the part where I started testing these wonderful optimizations. I modified the makefiles for my app, included the static Cocos2d-x libraries, all combined into one shared object, and there I was: expecting to reap the huge benefits…
…well, no. Far from it. The actual APK file (the one you’d offer for downloading on the Market) had decreased in file size with about 1/3 less of the original size. That’s great, but my initial goal was to make the app use less internal memory on the actual device, in which I did not succeed Why? Dunno! The installation size (and I did move it to the SD card) was actually just as big as before without all the optimizations!
So I guess we’ll have to wait for Android 2.3, which does install native libraries to the SD card.
Thank you for reading this topic, and please, please do not spend six hours on it like I did But if you do, please do post your results. Maybe I didn’t do things the right way? Who knows…
ps. The APP_OPTIM option is quite good though. You (the Cocos2d-x team) might want to consider adding it to Git. It does a nice job on cutting at least part of the fat away!