Thursday, 25 April 2013

Trial: Choosing Lift over Rails

Today, I'm going to start on a slightly scary journey. I'm going to start work on a new webapp but I'm not going to use Ruby on Rails which I (mostly) love (with plugins) and already know well. Instead I'm going to use the Scala-based Lift framework which I have never used and is completely alien to me. This is going to cost me a lot of time at the beginning, not just because I don't know the API, but because the mindset of the beast seems drastically different to typical web frameworks that most of us are used to.

(NOTE re RAILS: I will make many comparisons to RoR because it's the web framework I know best and cos it's well-known. Just like everyone thinks Maccas (ie. McDonalds for non-Aussies) when you think fast-food. It's tough being on top. Bad luck RoR.)

(Have you made a similar transition? I'd love to know how it went. Let me know!)

Why?

In no particular order...

  • Security. Big security focus. Immune to lots of common vulnerabilities. I think it even automatically uses random param names for POSTs, etc.
  • Speed. Compiles to Java bytecode, runs on JVM. Parallel rendering. Scala has built-in, native XML support so that should be faster than parsing textual templates. I read somewhere that a modest, old processor can comfortably serve 300 req/sec on a single processor, doing a modest amount of transformation. With threading I've read that this can exceed 20x the speed of the same webapp in RoR running multi-process, presumably on something like Puma, Thin, whatnot. (No sources, sorry, it's arbitrary anyway. And we all know Ruby can be scaled, that's not the topic here.)
  • Snippets rather than MVC. MVC has never felt right to me. It's great for trivial DB-interface-like-webapp kind of stuff, but outside of that I've often run into ambiguous scenarios that don't feel comfortable... although I have used it happily for lack of a better alternative. Lift instead uses snippets which are pieces of logic/functionality that you can use all over the place in as many views as you like. Seems much better in terms of reusability, organisation on the other hand I'm not sure yet. Snippets are also executed in parallel -- nice.
  • Easier Ajax with wiring/binding, server-push, more. In addition to what the links say, most of the Ajax plumbing seems to be automatic; you don't even need to declare URLs or actions. Also type and parameter safety -- excellent.

Those are the main reasons that come to mind. There other niceties too such as lazy loading. The doco flaunts designer friendly templates as some awesome feature but I'm personally on the fence about it. I'm a one-man everything team at the moment so it doesn't immediately appeal to my situation. I can see it potentially making CSS dev faster (because you can work off a static template with all cases hardcoded which gets wiped by Lift) but I think any small gains will be offset by the major productivity loss of not having HAML.

Downsides. The Oh Noes.

Basically, less <good thing>.

Less adoption. Less incumbency. This results in less libraries to choose from. Less plugins. I assume more reinvent-the-wheel kind of stuff for me to write.

Less community. It especially won't be as big as RoR's. This means less examples and code online, less questions on StackOverflow, less forums and blogs, less information and help. It sounds like the mailing list is friendly enough and I'll be joining today but it's nicer to have more resources at your disposal rather than just always hitting up the same guys for help.

Less developers on the market. If this app makes me millions of dollars and I get to a point where I need to outsource or hire then I'm going have significantly less people that can do the job. It's hard not to compare to RoR which is ubiquitous these days and which would cause no sweat finding capable helpers.

I'm going to accept these problems because Scala is awesome, Lift is philosophically fresh and whispers of great advantages once you climb the learning curve, and there are aspects of Lift that will have a direct impact on time, money and resources. For example, I'll be able to host my project free for longer (due to improved performance). If my project gets popular, yes, I'll have it harder looking for manpower but, (and this runs contrary to my innate tendencies), I often read & hear (with supporting evidence) that it's best to focus entirely on the short term when starting up a small business or venture (notice I avoided calling it a "startup"). Worries like scalability, resourcing, support, etc. can and should be dealt with once the project premise is proven to be successful and profitable. Like the 37-Signals guys say, "A business without a path to profit isn't a business, it's a hobby", and if this gets off the ground and becomes just a hobby then I'm not going to fuss about that stuff anyway.

Wednesday, 17 April 2013

Lessons Learnt on My Second Android App - Pt.1

Hi. Recently I released my second Android app. It's a collection of game timers and bells and whistles to be used whilst playing board games, card games, party games, the like. Practically, that means you can use it to play 4 player Scrabble where each player has 2 minutes-per-turn and a total of 25 min for the entire game, for example. Or, for a game like Scattegories you can have an hourglass set to run for a random amount of time between 1~2 min and then scare the crap out of everyone when it goes off. That kind of thing.

If you're interested, you can peruse some screenshots here and find the app here: “Time Us!”

Moving on, I'd like to share and record for future-me, the lessons I learned and observations I made whilst creating this app.

Scale Bitmaps Effeciently

Those tiny graphic files in your resources consume a surprising amount of memory when in use. A 100KB PNG is compressed and when loaded can comsume 10MB of memory as a Bitmap. As we know memory is quite important on mobile devices, especially on older devices. If the user is on a small LDPI device for example, their tired old phone isn't going to have much memory to spare. If you're scaling bitmaps there's a chance that you're consuming more memory that you need to, and the amount of waste gets higher with older devices where it matters more.

Say you have a large graphic that you scale to a smaller size, you're going to find that without some special options in-place, the device will load the full graphic into memory and retain it as it was loaded prior to scaling. I originally assumed that on a small LDPI device the graphic would scale down to about 20% (or whatever) of its size and accordingly only consume 20% of the full size in memory. Right? No. By default Android loads first, scales later and doesn't let go of that full-sized bitmap.

So how can you reduce memory consumption? The first recommended solution (because it is more processor-performant as well) is to prepare different copies of your graphics for each density. Ok great, but there are scenarios where that's not the best approach so what then? It turns out that there's a whole trove of info on this issue online in the official Android docs, the specifics on this scaling issue here:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

You can mostly just copy-and-paste the utility code from the link above. By applying the methods therein, I was able to reduce memory usage by up to 80% or so (from memory), depending on the device. I didn't know this page existed until I needed it so I suggest you bookmark it and/or keep a mental note if you don't read the above page immediately.

Free Bitmap Memory Manually

Now that we know a little more about bitmap memory consumption, let's talk about another snag I encountered.

My app's home screen has two looks: dark & light. The light-mode background consists of 2 images. On a w360dp XHDPI phone, it consumes 5.8MB for bkgd img #1, and 4.0MB for #2, making a total of 9.8MB.

(FYI: You can easily see the memory profile of, and analyse your app by doing this from Eclipse: DDMS → Devices → Select process and click “Dump HPROF file” in the toolbar.)

What would you expect to happen to that 9.8MB when the user hits a button and the app moves on to the next activity (screen)? I expected that the OS would release the memory because it's not in use. Not so. As long as the home activity is in the task history the memory will stay consumed and locked which in my case means for the entirety of the app. Now maybe technology is catching up but the old phone I had for two years before my newer Galaxy Nexus, was a HTC Magic, and that thing was constantly chugging due to memory problems and as such. Consequentially, I'm not comfortable with holding onto 10MB for no reason on a mobile device.

So what can you do? Well I could draw both images onto a single canvas but that's not really solving the problem, that would just drop the 10MB down to 6MB and it would still be unavailable later. To solve the problem I did three things:

  1. Remove, dereference, recycle the images in onStop().
    I wrote a utility function to do just that, and then simply called in in my activity's onStop() method. It looks like this:
  2. Set the images during onStart().
    In your activity, just use View.setBackground(Drawable) or View.setBackgroundDrawable(Drawable) in your onStart() callback to restore the images that you clear in onStop().
  3. Turn off hardware acceleration.
    Devices running Android 4.2 require an additional kick in the pants to get them to let go of the memory. You'll want to turn hardware acceleration off or else you'll end up with instances of GLES20DisplayList in memory that consume exactly the same amount of space. Instructions to turn off hardware acceleration can be found here: http://developer.android.com/guide/topics/graphics/hardware-accel.html

There's actually a whole bunch of doco on bitmaps and memory on the official Android site so for more info, take a look at http://developer.android.com/training/displaying-bitmaps/index.html.

PNGOUT

If you have a moderate amount of graphics and like me you optimise (ie. re-generate) them for multiple densities, then the size of your APK can balloon fast. This situation lead me to discover a utility called PNGOUT (Windows, ArchLinux, Other Linux + OSX). It optimises your PNGs and gets them down to a smaller size, from memory, using a special compression algorithm targeted at graphics. As with most types of ultra-compression it can be slow but the results are worth it.

I ran it over my PNGs. It took 30min and reduced my total size from 7.4MB to 5.9MB, a 20% reduction. Nice.

Screen Widths

I learned a few things about screen width. Firstly I had to find a database of devices and specs. One doesn't exist but I did find two admirable attempts here and here. After some analysis, from what I can tell, most older phones have a width of 320dp and I don't think there are any phones with less than that -- AdMob's smallest banner ads are 320dp. For LDPI and MDPI, 320dp is a safe bet. Once you get to HDPI, it seems that around 70% of devices are 320dp, 25% are 360dp and the rest are larger than that. Here's the most interesting piece of news: for XHDPI it seems that the 360dp is the minimum and majority. There doesn't seem to be any 320dp XHDPI devices in existence!

What does this mean? Why do I care? It means that on some devices in some scenarios, you're going to end up with more free space that you expected and a smaller perception. Instead a larger graphic is more appropriate and that's something to consider when generating graphics. You'll need to make a conscious decision about which dimension is more important to you for each graphic, and enlarge appropriately. If you want a graphic to be perceived as being the same size on a 320dp-width and a 360dp-width HDPI device, then you might want to generate another copy at 360 ÷ 320 = 1.125x the original size. If you're supporting tablets then it might pay to do something similar for the most common tablet sizes.

Here's what I ended up with:

DirFactorContent
drawable-ldpi0.75Everything
drawable-mdpi1Everything
drawable-hdpi1.5Everything
drawable-w360dp-hdpi1.6875Width-sensitive GFX only
drawable-xhdpi2 or 2.25 depending on the gfxEverything

If you'd like to learn more have a read of these:
Supporting Different Densities
Supporting Different Screen Sizes

More Next Time

I learnt more but I'll post that next time. If you read this far I hope I've been helpful.