There is absolutely a ton of stuff broken with Android, no doubt about it, and I don't want to sound schoolmasterly but in this case it looks like a RTFM. From the Android NDK docs [1]:
You must specify an ABI for each CPU architecture you want your app to work with ... To build machine code for two or more distinct ABIs, using spaces as delimiters. For example:
APP_ABI := armeabi armeabi-v7a
This setting tells the NDK to build two versions of your machine code: one for each ABI listed on this line.
Whereas the post says:
To reduce our APK size and ensure that our App would run on all possible devices, we have flavors of our App for the x86, Armv7 and Arm architectures. Each flavor only contains the native libraries corresponding to its respective architecture
That documentation describes how to create a fat binary, it does not say that you are required to. In fact, it exlicitly states:
When you build multiple machine-code versions, the build system copies the libraries to your application project path, and ultimately packages them into your APK, so creating a fat binary. A fat binary is larger than one containing only the machine code for a single system; the tradeoff is gaining wider compatibility, but at the expense of a larger APK.
Furthermore, Google play explicitly allows you to offer multiple APKs, each targeting a specific device configuration [0], although there they do "encourage you to develop and publish a single APK that supports as many device configurations as possible".
Having said all of this, none of it is relevent to the main problem discussed, which is that the installer failed to unpack the libraries.
The PackageManager bug was worked around by Google Play, and was fixed in API 18 (JB MR2). As of today, this is only a problem if you distribute or support the unofficial distribution of ABI-specific packages outside of Google Play and you target API 17 or below.
It's also worth noting that Chromium removed their workaround and are shipping a new native library loader purely to optimize for the fact that they are now the system WebView implementation, thus they need to reduce RAM usage when they are loaded by many running processes.
I've been developing for Android since 2007 and doing NDK development since that was available and I've seen my fair share of library trouble. I've learned that when Google "encourages" you to do something it's code speak for "you better stick to this or we'll guarantee for nothing".
As to why the installer failed to unpack the libraries, it's a bit suspicious that the article doesn't mention an investigation of the root cause nor called it a bug on Google's end. My hunch is that the root cause is somewhere in between making some un-documented moves in Android.mk and how the package generator picks up the libraries.
Hi, author here. If you take a look at the end of the article under the "Acknowledgements" there is a comment from the Chromium source code that describes what the issue was. We did file an issue on AOSP's issue tracker, but nothing was accomplished there...
Hi, author here. I forgot to mention in the article that we distributed an APK that contained all libraries for each architecture for a long time. The problem existed even though the APK had all libraries for all architectures. It was not until much later that we introduced flavors for each architecture. The reason we introduced flavors was to make our app work on the Asus ZenFone. It is an x86 device, however it tries to load ARMv7 libraries (even though x86 libraries are right there in the same installation) and attempts to utilize libhoudini to work, but it just ends up crashing on app start.
Trust me, we've read the documentation surrounding this multiple times.
Does anyone have data that shows the APK size actually hurts the downloads / purchases of an application? Regardless I think this was a mistake on the developers' part and could have been anticipated. First of all, it is important to have a smaller APK for users in developing economies and/or low incomes, but today's newer devices come with so much space that going to great lengths to reduce the APK size early on is a premature optimization, especially when we're talking about removing libraries that are required for proper running of the APK on every conceivable handset configuration. You should only ever reduce the APK by removing things that absolutely are not critical to run the same application package everywhere.
So: First, ensure that your app runs for everyone. Then find ways to reduce the APK size without compromising that.
And yes, I'm aware that Android / Google Play has added APK splitting, allowing platform-specific APKs to be distributed from the Play store. I actually would argue on balance this was probably a bad idea which presumes an iTunes-like single distribution channel.
Yes, APK size is still critical. We still see a huge amount of people owning and running cheaper devices which have problems installing large apps.
Your friends with 700EUR phones aren't really all of the market and there's still enough devices out there that will not successfully install a 30+MB APK. And that's not even talking about how wasting users storage for things he doesn't need (just because you're a bit lazy) is just... wrong.
Yes, I agree it is important and it is still also one of the last things you should think about. Certainly breaking the one-package-runs-everywhere by delivering different packages for different architectures in order to save disk space is one of the last things that should be considered. There are plenty of optimizations before this that you can make by compiling your native libraries differently, removing redundant information in resources, etc, before you should consider delivering architecture-specific APKs. And this is precisely because there are many back-channels for distributing APKs (back-channels which are also probably more commonly used in developing economies where those newer phones are unaffordable) in addition to the Play store.
There are two Androids: the quirky but generally usable Android described in Google's documentation and occasionally found on Nexus devices, and then the Android actually found in the wild after manufacturers, carriers, 3rd party app stores, users and general neglect have their way with it.
> We started to log the installer package name in our crashes and quickly figured out that, yes, users were installing the app from various sources, and each new UnsatisfiedLinkError was coming from a manually installed app where the user mistakenly installed the wrong flavor for their device’s architecture.
It is probably still blamable on bad design from Android, that it's possible to successfully install and launch an app that can only possibly use some of the code it requires (or maybe the app devs need to do something to indicate the native libraries are absolutely required?)
Security vulnerabilities and bugs happen regularly in every major operating system. Windows, Mac OS X, Linux, iOS, Windows Mobile, Android, etc. Most of the time these bugs are patched in a timely fashion due to the fact that the publisher can release a bug fix directly to the end user's device. This applies to everything except most Android devices.
When a major bug is discovered in Android, first, Google fixes it and published the code. This is usually done quickly. Second, the phone manufacturers take that fix and incorporate it into their own Android build process with all their extra layers (HTC Sense, Samsung TouchWiz, etc). This second step takes anywhere from a few weeks to infinity (aka it never happens). Third, the carrier takes the manufacturers build and adds their own cruft to it, maybe tests it, and then pushes it out to their customers as an over the air (OTA) update. This third step takes anywhere from a month to infinity (aka it never happens).
Due to the interference of manufacturers and carriers, I would not recommend using Android on anything other than a Nexus device purchased directly from Google or a retail/online store. Even a Nexus, when purchased from a carrier, won't get updates as quickly as it should (speaking from experience with my Nexus 6 and T-Mobile).
When Texas Instruments exited, it's likely neither Google nor anyone else had the proper legal agreements in place to force the release or licensing of the appropriate code/patents to ensure continued updates. After this rather large debacle which affected numerous manufacturers in the mobile industry, I'd wager that's now part of all of them.
Don't forget that even without an exit, sometimes OEMs don't update drivers, leaving manufacturers to drop support far earlier than they should in the lifespan of a product. Case in point, the 2008 Apple Macbook. It only had 32-bit graphics drivers. So, that meant it couldn't run the 64-bit-only OS X Mountain Lion released just 4 years later in 2012. I found this all out firsthand supporting my girlfriend's problematic 2008 Macbook (which also had notoriously bad connections to the LCD panel).
Sure, and bugs happen and Google often fixes them, but the fixes don't reliably end up in the hands of users before devices eventually die and are replaced. It's hard for me to believe this is still an issue this many years into Android's existence, but here we are.
Here's another fun Android / PackageManager behavior - Android doesn't kill an app before upgrading it (unlike on iOS). You may think this is a feature, except that after the upgrade the PackageManager is now out-of-sync with the running code. So for example, say v1 of the code is still running after an upgrade to v2, and the code calls getPackageInfo() in order to find out say, its versionCode. The running code will erroneously think it's v2.
Is the problem discussed in this article something that has to be worried about still? This is hideous ... By my understanding the only way the solution discussed in the article can work is by including the native libraries for every architecture inside the apk which seems like it will make the app larger than it needs to be ...
I think I'd rather just detect the issue and throw up an error telling the user that they've managed to install an apk built for the wrong architecture and need to install a different apk ...
If you are distributing through Google Play (or, I assume, most 3rd party app stores), then you can provide a different apk depending on the target architecture. The problem they were running into (after they worked around the installation bug) was that some users would sideload their APK, but sideload the wrong version.
As far as I can tell, their ReLinker solution is only a workaround to the installation problem.
If you're clever enough to sideload are you not clever enough to recognize and remedy the problem? Developers should not be enabling people who aren't able to handle that responsibility.
Actually, now that I think of it, I can see a business requiring employee devices to be flashed with a set of apps but not wanting to put the effort to actually check that it'll work. In that case the developer should determine if the user is an individual or a company and in the later case sell them a contract for support.
There are popular "mirror" sites for apks, I wouldn't expect most users of them to be able to debug a wrong arch package download. Ideally the people taking the apks to mirror wouldn't pull thin apks, but sigh
Many people choose to not link their device to a Google account, so cannot access Google Play. Others use devices that haven't licensed Google Play. Widely used apps are frequently available officially as apk downloads (eg WhatsApp), or other more inclusive app stores (eg f-droid), others you have to get from unofficial sources.
> If you're clever enough to sideload are you not clever enough to recognize and remedy the problem?
Hmm, I always shared apk files with friends when they simply were too big, kinda easy to do with ES File Explorer or other root-using file managers. Lucky for us, our CPU archs never proved to be a problem though.
But I can very well imagine this being a problem for warez, or people in countries not served by Google Play (or who simply have no easy access to payment, e.g. because they don't have a CC), or people with devices not sold with Google Play (cheap ass chinese phones).
Yes, if you enable CPU ABI splits there will only be the lib for a specific architecture available in an apk.
You could load the lib from a remote server after install if you need to save space but if we are talking about saving 1-3 MB I'd definitely save myself the hassle and just pack the libs for all architectures in one apk file.
Fire OS fixed this bug in the Package Manager. Amazon treats both, the buyers and the developers, as its customers. Has a nice process in place to test top 10k+ apps on each upgrade and identify and try and fix issues arising due to AOSP.
Google needs to start doing the same thing or something similar. I have seen a fair share of broken stuff. Something even as basic as a ListView was horrendously let loose in a middle of a refactor between JB and L.
Hi, author here. This is definitely not a case of using shoddy app stores or installers. The vast majority of the problems stemmed from installations that came from Google Play. The article mentions a tidbit about alternate installation sources as a heads up / piece of advice for people running into the problem because we were still confused as to why it was happening for a small number of people. It just turned out that they were using shoddy app stores / installing manually, and were installing the wrong version for their device.
The app https://play.google.com/store/apps/details?id=com.kii.safe is free, so no. Some users might not want google to know they're using a particular app (this one a is a security one, so probably a higher proportion of users would care). There are over 10 million users from the play store, so 100,000 is not that high for sideloaders.
Plus, google might block downloads in some countries.
Some devs sell APKs directly for users with devices without Play store. Samsung has their own app store. As does Amazon. And there is f-droid for open-source stuff. And I bet there are quite a few ones with regional importance.
Dealing with the play store can be a bit of a pain. The UI really isn't that great and the downloads are sometimes very slow. So it's honestly just a whole lot easier to side load sometimes.
You must specify an ABI for each CPU architecture you want your app to work with ... To build machine code for two or more distinct ABIs, using spaces as delimiters. For example:
This setting tells the NDK to build two versions of your machine code: one for each ABI listed on this line.Whereas the post says:
To reduce our APK size and ensure that our App would run on all possible devices, we have flavors of our App for the x86, Armv7 and Arm architectures. Each flavor only contains the native libraries corresponding to its respective architecture
There we have it.
[1] http://developer.android.com/ndk/guides/abis.html