My Lessons from Facebook
By now many of you will have read the Facebook blog post, where Facebook engineers revel in some hacks they made to the Dalvik VM to allow it to run Facebook’s app with its massive code base. The comments on that post, and the various tweets and such expressing further opinions, seems to suggest that the larger development community is somewhat dismayed with Facebook’s approach.
That being said, what lessons should Android developers take from Facebook’s work in this area? Here are my primary takeaways:
Guidelines Do Not Come on Stone Tablets
The argument given for why Facebook was blowing past capacity limits of the dex step was… code organization.
… as well as using newer abstractions that encouraged large numbers of small methods (generally considered a good programming practice). Unfortunately, this caused the number of Java methods in our app to drastically increase.
All else being equal, having many small classes with many small methods, lots of interfaces to implement pluggable strategies, and the like are good guidelines to follow in Java development.
They are guidelines. They are not laws.
Hacking a virtual machine to allow you to retain lots of small methods is not an especially wise choice. For really big apps, where you insist on a monolithic code base (see the next section), you may need to “devolve” the code organization a bit.
Modularize, Modularize, Modularize
The specific problem that Facebook ran into was that the
LinearAlloc buffer, used as part of processing dex’d bytecode, has a fixed size, in the 5MB-16MB range. The more methods you implement, the more entries there are in this buffer, and having too many methods blows out the buffer limit.
It is unclear to me precisely how large a
LinearAlloc entry is, as I am nowhere near expert enough on Dalvik’s innards for that. But, if we estimate that four bytes is probably the smallest likely entry size,
LinearAlloc could handle in the low millions of methods. And, in the “lots of small methods” mindset, this could mean that in the low millions of lines of code, you could start hitting the
LinearAlloc limit, particularly on Android 1.x/2.x.
This means that by the time your lines-of-code count is getting into the millions, you will want to think about modularizing your product into discrete apps.
To be honest, the odds are decent that you needed to do that anyway for permission management. The Facebook app, at the present time, requests 21 permissions. This is excessive. Having a core Facebook app with fewer permissions, and plugins to enable optional features requiring additional permissions, would simultaneously improve app acceptance and help reduce the odds of hitting the
LinearAlloc limits, even without having to necessarily “devolve” the code organization.
With Great (Brand) Power Comes Great (Testing) Responsibility
The Facebook team brags:
We used manual testing, DeviceAnywhere, and a test lab that Google let us borrow to run our test app on 70 different phone models, and fortunately, it worked on every single one!
This is simply appalling.
Facebook is a multi-billion dollar business. CommonsWare is a multi-dollar business.
Facebook has thousands of employees. CommonsWare has thousands of paperclips.
Facebook apparently has few devices for testing, having to borrow a 70-device test lab. CommonsWare has its own ~50 device test lab, with all but one device paid for out of pocket (directly or via a conference registration to Google I/O).
To quote “Sesame Street”, “one of these things is not like the others…”.
Having only a device or two is to be expected for solo developers, small firms, and the like. A firm the size of Facebook needs its own device lab, with 100+ devices, to do continuous integration testing across the devices in the lab.
Yes, this will cost tens of thousands of dollars. Perhaps Mr. Zuckerberg can rummage through his sofa cushions to come up with it.
Beware Closed-Source Libraries with JNI
The fact that Facebook was able to pull of their monkeypatch of Dalvik shouldn’t be a security issue, insofar as they can only mess with their own copy of the runtime environment.
However, so could anyone else’s native code.
Hence, I’d be a bit leery of closed-source Android library projects with JNI binaries in
libs/. Probably they aren’t trying tricks like this… but, being closed-source, you have no good way to know.