This isn't a general gripe about the bloat of Android apps nowadays*, but a very specific question:
If you install Android Studio 2.3.3 and create the "Hello world" sample app (as described in the Building your first app official tutorial), then build a release APK, the resulting file is 825KB (I tested this on Linux, but I suspect the output is the same on other OSes).
I've already enabled ProGuard and there are no images or other resources.
What goes in that APK by default?
Why?
How can that bloat be taken out?
By comparison, in 2013 a Hello World app was under 10Kb.
* I remember when decent fully-functional apps were a few hundred KBs, and by comparison a PWA like Uber is 1% the size of the corresponding Android app
Exclude all AppCompat libraries and you will see size decrease to about 100kb.
appcompat v7 is automatically attached even if you do not use it at all
In your build.gradle, exclude from "dependencies": compile 'com.android.support:appcompat-v7:26.+
You'll also have to edit res\values\styles.xml to become only this:
<resources><style name="AppTheme" parent="android:Theme.Light"></style></resources>
(make sure to remove the <!-- Customize your theme here. --> lines). Also, in MainActivity.java, change the extends AppCompatActivity part to
public class MainActivity extends Activity
Turns out that by removing all sorts of resources and using insane compression, an empty APK can be brought down to 678 bytes (!).
Thanks to Udayraj Deshmukh for pointing out a blog post detailing how to reduce an Android APK's size by 99.99%.
Related
We're trying to make our apps more accessible, and one of the things we're looking at is supporting larger font sizes when configured from the system settings.
We noticed that on Android 7.0 (and probably earlier versions too), the system font sizes are not properly applied within our app. We also noticed that some other apps like gmail were properly handling the font size change, so it was "something" our app was doing wrong. After quite some time investigating we found out that using the androidx library seems to be the cause.
For example, creating a "hello world" new project under Android Studio, you'll see a "use androix artifacts" checkbox:
Then, if you set the accessibility font size to the highest value:
This will be the result for your app if you checked the "androidx" checkbox (not working):
This will be the result for your app if you didn't check the "androidx" checkbox (working properly):
If you check that box, the produced app binary will not apply system accessibility font size changes properly on Android 7.0, otherwise it will work fine.
Migrating to androidx was a little bit of work and since it's the future (the support library is gradually getting deprecated), going back is not really an option :/
Has anyone else encountered a similar issue?
Could that be a bug with the androidx library?
If anyone found a way to keep the androidx library and not have this bug, I would really like to know about it, thank you in advance!
Update your implementations, example:
implementation 'androidx.appcompat:appcompat:1.2.0-alpha01'
You may also need:
implementation 'com.android.support:support-v4:28.0.0'
Whats the difference between
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt
and
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
when I'm using 1st one its crashing with facebook login, but not when i use second, it'll not crash (in release build).
but size get increase from 4 to 5 MB in second.
I'm using API 21.
Facebook sdk 3.0
Without seeing the contents of the proguard files, I can only guess, but I think it's fair to assume that proguard-project.txt contains definitions to keep class names / methods / members / etc relating to the Facebook SDK.
The reason it's crashing in the 1st setting is because it doesn't include your project's proguard settings, meaning it minifies more than the 2nd setting (including the Facebook SDK). Then, in release, the Facebook SDK is possibly loading a class / invoking a method via reflection or something similar, and crashing because it can't find a method / class name which has been minified.
This is also why the 2nd setting results in a larger binary - minifcation means smaller binary, since every class is being reduced from "SomeLongClassName" to "a". The more you minify, the smaller (and more efficient) the resulting binary is.
Minification is an optimization, and shouldnt (IMO) have a higher priority than code coherence and architecture. If you need to exclude several items from minification (which your ProGuard probably does with the Facebook SDK), then you should, and you shouldn't worry about the resulting APK size.
Background
It seems some old Android OSs (and maybe even the newest ones) have a limitation on the amount of code each app can hold.
As I've found, the limitation is on a buffer called "LinearAlloc" .
On 2.2 or 2.3 it's about 5-8 MB , and I think it's 16 or more on others.
The problem
If you have a too large code (and apps can reach this state), you won't be able to install the app at all on older devices, getting the next error (also reported here) :
Installation error: INSTALL_FAILED_DEXOPT
Please check logcat output for more details.
Launch canceled!
What I've found
One solution is to just remove as much code and libraries as possible, but on some huge projects such a thing is very hard to do.
I've found the next links talking about how Facebook solved this, by somehow increasing the limit:
http://www.slashgear.com/how-facebook-fixed-its-gingerbread-dalvik-problem-04272478/
http://arstechnica.com/business/2013/03/how-facebook-dug-deep-within-android-to-fix-its-mobile-app/
https://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920
Also, Google has posted how to solve it by loading code dynamically :
http://android-developers.blogspot.co.il/2011/07/custom-class-loading-in-dalvik.html
The question
How did Facebook do it?
Is it possible to overcome this in other ways too?
Is there any free library that increases/removes the limitation of this buffer?
What is the limitation on newer Android versions, if there is any?
How do other huge apps (and games) handle this issue? Do they put their code into C/C++?
Would loading the dex files dynamically solve this?
The limit is the total number of method references:
https://code.google.com/p/android/issues/detail?id=7147#c6
https://code.google.com/p/android/issues/detail?id=20814#c6
A middle ground between doing nothing and the multi-dex approach described in the FB/Google articles is to use a tool like ProGuard to remove references to unused code at the Java level. See:
http://proguard.sourceforge.net/
http://developer.android.com/tools/help/proguard.html
There is a new solution, made by Google:
https://plus.google.com/+IanLake/posts/JW9x4pcB1rj?utm_source=Android%20Weekly&utm_campaign=59f1f4bf4d-Android_Weekly_125&utm_medium=email&utm_term=0_4eb677ad19-59f1f4bf4d-337848877
http://developer.android.com/reference/android/support/multidex/MultiDexApplication.html
It seems all you have to do is any of the next things:
- extend from "MultiDexApplication" instead of from "Application"
- call MultiDex.install(context) in your application's attachBaseContext
But now I wonder:
Is that really it?
Does it have any issues ? Does it affect performance?
How does it work?
What should be done with ContentProvider, as it's getting called before Application gets initialized?
The post says "gives you MultiDex support on all API 4+ devices (well, until v21, where you get this natively)" . Does it mean that from v21 it will be the default behavior, or just that the class will be built in and you won't need to use the support library's class ?
Will this solution work on Eclipse too?
I have a project A that should use a wizard to create a project B. Project B is based on a template. Project A should send data to that template and generate an APK from this data and template programmatically.
I've read about the APK structure and what it includes, like classes, resources, and manifest files. My question is, what is the best way to achieve this template-based generation of an APK? What could I use for this?
This is the open source project you're looking for.
https://code.google.com/p/terminal-ide/
Terminal IDE is a command line java / android dev kit that runs on the device itself. Using a correctly configured vim, bash and busybox, in a custom terminal + custom keyboard environment. All these applications are put together so that they interact correctly. A full Terminal emulator + custom ANSI keyboard ensure good bash, vim and busybox emulation. All set up and ready to run in a NON-ROOT environment. Normal user permissions are enough to run all of this.
You can also download it from Google Play here. The application has 4.7 stars on the Play Store from 1,324 ratings. Here is one of the latest written 5 stars reviews this application received on Google Play. And it's not unique by any means (the emphasis in bold is mine).
A Google User - March 12, 2013 - Version 2.02
Wow!
I have over 30 years programming experience (mostly in C/C++ and Assembly) and have been looking to learn to program Android. I looked everywhere for a way to compile and run Java tutorials on my tablet, with no success, until I found Terminal IDE! Then I started playing around with its capabilities and let me say again, wow. DEVELOPER - I have only one suggestion, which others have brought up too: reduce the internal memory footprint. Maybe breakup the different languages into installable add-ons?
In a way, it's a lot like the Android Java IDE called AIDE, minus the GUI interface (but unlike AIDE, it's doesn't have low artificial file limits, and since it's open source it can actually be used to build your own project upon and extend).
I have developed one Android app in one project with Eclipse - it's structured (coming from iPhone) so one constant defines whether it's the demo or the full version.
Now I have the problem that everytime I want to create the demo version I need to change the constant but also need to make a copy of the project with a different package name.
Obviously changing code in the original full version needs to be copied over to the demo or I would have to redo the creation of the demo app everytime I submit my app.
I see three possible approaches:
1.
While I have looked into library projects it is still unclear to me how this really provides a good solution in this case.
For example if I have the full version with an activity structure:
A1
A2
A3
using utility classes U1,U2
Certainly U1 and U2 can be in a library project and referenced from both projects - but the activities, strings.xml, graphics, layouts need to be duplicated (or is there another way that I don't see?) This does not seem to be a good way forward and unfortunately has not been explained in similar questions on this topic when this approach was suggested.
2.
The other way would be to create different package names based on different build settings (similar to iPhone), however, this does not seem to be possible in Eclipse rather than by using some external scripts (which - honestly - i rather avoid since it seems rather error prone) while also the compilation has to be invoked outside Eclipse
3.
The probably most straight forward approach (and also currently with smalles effort) is to just manually copy the project, change the one constant, rename the package and compile/export every time I submit. This - however - seems to be rather "basic" and certainly does not look professional (as compared to iPhone/xCode build setting/target solution)
What would be the best approach (requiring minimum amount of changes and still being stable and easy to use) ?
Many thanks!
EDIT
For everyone who tried tim's solution - it works fine, however I ran into a problem with custom attributes.
Check this: How to solve Android Libraries custom attributes and package name remapping during build?
it will solve the isse for libraries
I'm doing this currently in eclipse, and it is not difficult.
Convert existing source to library project.
Create two new projects, free and paid.
Include the library project in the free and paid projects.
It's not necessary to have a single Activity or resource inside the free/paid projects. All you need is a manifest for each which referenes the activities from your library. My free and full projects do not currently have a single java, resource, or layout file of any kind, it's just a manifest which references activities from the library.
I use the exact same code for both projects, and I differentiate them by saying :
if(getApplicationContext().getPackageName().equals("full version package name")) {
//do full stuff
} else {
//do free stuff
}
Some gotchas I've hit, especially if you've already released your app on the market:
If you change the full name/path of any activity, it will disappear from your homescreen. Therefore if your library has a different package name than the existing version, you will lose any homescreen icons. They can be replaced by the user but it's not ideal.
Similar for appwidgets, if you change their receiver name, they will disappear on upgrade.
You may not under any circumstance change the package name of a released application.
If you've already released a free and pro version, it's somewhat unfortunate, because the activity path will need to change to a common library path, and you can't rename the released package to match the library path. So somebody will have to lose their existing icons.
In my case I had only released a free version before splitting them, and I was able to name the library with the same package name as the free version. I was skeptical that you'd be allowed to include a library with the same package name as the wrapper package, but apparently it's possible to do so (working fine for me).
So in my case I've got three projects:
Core Library: package name : com.myname.myapp
Free Version: package name : com.myname.myapp
Pro Version: package name : com.myname.myapp.Pro
And the free and full version manifests add activities which are named com.myname.myapp.ActivityA or com.myname.myapp.ActivityB, which exist only in the library project.
I see the simplest approach would be to have three projects:
demo
full
library
The demo and full projects will each have their own unique package name as defined in their respective Manifest file. Their Activities are merely ports that send information in a bundle to the primary Activity in the library project. The Activity in the library project will read the Bundle passed in for the necessary parameters that determine whether it was launched by the demo Activity or the full Activity. Then it will proceed accordingly.
So the logic is like this:
User launches the demo Activity -> The demo Activity creates a Bundle with the information that says it's the demo Activity -> The demo Activity launches the library Activity which then executes the rest of the program in demo mode.
OR
User launches the full Activity -> The full Activity creates a Bundle with the information that says it's the full Activity -> The full Activity launches the library Activity which then executes the rest of the program in full mode.
It's very simple by using build.gradle in Android Studio. Read about productFlavors. It is a very usefull feature. Just simply add following lines in build.gradle:
productFlavors {
lite {
packageName = 'com.project.test.app'
versionCode 1
versionName '1.0.0'
}
pro {
packageName = 'com.project.testpro.app'
versionCode 1
versionName '1.0.0'
}
}
In this example I add two product flavors: first for lite version and second for full version. Each version has his own versionCode and versionName (for Google Play publication).
In code just check BuildConfig.FLAVOR:
if (BuildConfig.FLAVOR == "lite") {
// add some ads or restrict functionallity
}
For running and testing on device use "Build Variants" tab in Android Studio to switch between versions: