Until recently (summer of 2012, I've published this rant on 2012-10-14) I didn't care much for Android. But I needed to develop for Android and now I do have an opinion. Here I'm noting down my experience so that nobody can say my disliking of Android is unfunded and without base. Because since I had to develop for Android I really hate and despise it.
I know very well that most Android fans will regard me as a stupid Apple troll. Be my guest. I'm not writing this for Android fans or even haters: I'm writing it to clear my soul off all the anger and frustration Android built up in me. During the development I couldn't talk about it, so I couldn't blow off steam and now is my chance to finally let it all out.
First, I'd like to make it clear that I'm talking about my experience developing for Android. I can't speak about using it much since I haven't done so for a meaningful time. I've just seen maybe half a dozen Android apps so I can't criticize the experience as a user. There are enough religious wars about that already (BTW, I'm a vi guy ;-).
As you can already guess, I didn't found developing for Android to be an act of pleasure. Here I'll explain in detail why. If Android would've been just one or two years old I'd say lots of the issues are teething troubles, but Google had more than enough time to iron those out by now. They have lots of very smart and talented people and more than enough money, so they could deliver a better experience. So I wonder, why don't they?
It's not all bad in Android land but of course you remember the bad things the most. Especially if they drove you nuts and cost you several hours to resolve or work around. I'll try to praise a few things but the overwhelming majority of this post will be grumping, ranting and bitching.
About my background: I've been programming for 25 years now (who remembers TurboVision?), over 10 years for a living. In 1998 I turned my back on Microsoft and went Linux-only, at home as well as at work. I did Linux development for a living for almost a decade. In 2007 I got infected by Apple and slowly turned a Mac/iOS developer which is what I do for a living for two years now. Over the time, I worked with over a dozen different programming languages and several operating systems.
Apple's developer tools spoiled me in a way since their developer tools, APIs and documentation are of pretty good quality, IMHO. This is what I need to compare Android with since it's what I know the best right now (except for C99 and POSIX/SUS). Also, they're direct competitors in the same field.
All in all, I think Google isn't doing enough for their developers. Even Microsoft recognized how important it is to make development as smooth an experience as possible ("Developers developers developers" shouts Uncle Fester, I mean Steve Ballmer). With Google, I've got the impression they're sometimes only doing enough to get the product out of the door and that's it (special greetings to the documentation, more on that later).
It should be noted that I was supported by an Android pro when I started developing for Android. He trained me for two days and showed me lots of things and tricks I wouldn't have found out myself. He also helped me modernize parts of the project which I adopted from another developer.
What caused most of the trouble was our need to have a native part: Android usually is developed using Java ("managed code") while we needed to have a (large) part written in C++ for several reasons: ease of porting existing (very complex) code, performance and OpenSL ES are the most important ones. And this surely is the source for the biggest troubles I've experienced, native development on Android isn't very pleasant. But there were also enough issues with pure Android Java coding that drove me insane and cost me several hours each.
So, let's get started with why I hate Android:
The installation wasn't pretty but worked… except for the warning that the source of a few packages couldn't be verified or things like that. The installation howto simply says to accept them. Doesn't sound very trustworthy.
The first and third time I did this I was then greeted by Eclipse with a seemingly broken UI: two overlapping windows, some divided sections that need to be scrolled, showing cut-off text. The explanation/introduction text was covered and I couldn't reach it. The second installation didn't have that glitch.
I was then working my way through the Building your first app tutorial and noticed that the screenshots and wordings didn't match the current version of Eclipse and/or Android plugins. Some things had different names, some options were missing, a few new ones we there instead. In the dialog for creating a new Android app there were four additional pages which the tutorial didn't cover. I understood what it was about, but the first impression was already pretty bad. A total beginner will be puzzled by this and maybe stop there.
Then I wanted to know what certain options (that weren't covered in the tutorial) actually meant. In the lower left of the dialog is a "?" button. If you click it you get the following "help": "About New Android Application: Wizard to assist in creating a new project." Alright, but what the heck do the options mean?
Eclipse has a few really nice features, but way too often violates the very important Principle of Least Surprise. Things often weren't intuitive, some were strange, hidden or overly complicated. It does have a lot of nice features, though. For example, "Organize Imports" is very helpful. Another example is moving code parts around while holding the Alt key. Unfortunately, a lot of keyboard shortcuts are a bit uncomfortable on Mac due to the fact that the Fx keys are usually used as multimedia keys and you thus have to press the additional Fn key. So "debug application" is Command-Fn-F11 which isn't easy to press on a desktop keyboard. Luckily, you can easily remap the shortcuts.
When I opened the settings, I noticed it has a search bar. I've only seen this once before (Vuze). It's really necessary as Eclipse has so many things to tweak. This is both a strength and weakness: for a power-user, it's good. For a beginner, it's overwhelming.
Auto completion of Eclipse is pretty bad, IMHO. It only opens at certain points and if you mistype, it's gone and you have to back out to the last dot or something (Ctrl-Space doesn't seem to open the usual suggestion list, it merely blindly tries to complete). I also noticed it often doesn't list all available methods/classes that would match. In native code, I had no auto completion at all but I give Eclipse the benefit of the doubt and suspect that this had to do with the project and not necessarily Eclipse. Other issues I've noticed was buggy reordering of tabs via drag&drop and the fact that LogCat often makes Eclipse beachball (hang).
I was a bit baffled that the Eclipse UI doesn't seem to have changed the slightest in the past 7 or 8 years (which was the last time I've used it). Especially the icons are simply ugly. It's a detail, and lots of people will regard it as nitpicking, but Google has so much money and resources, they could've designed a few nicer icons and a nicer theme and package that (or better yet, donate it to the Eclipse project). It's the subtle UI things that make you enjoy using an app or not. The standard Eclipse "Mac" theme is horrible, it's a lot of white without any delimiters (you can't tell where one elements ends and the next one starts) and is totally un-Mac. Switching to the "Classic" theme improves the situation but still won't win a beauty contest.
Maybe you might think that an IDE doesn't need to be pretty, but if you use it all day, every (work)day it should be as pleasant as possible. I know lots of developers who spend a lot of time finding the right font, color set, tweak their IDEs with macros and other stuff to make life easier (for example, I'm using the excellent XVim plugin so I can use Xcode with VIM-like editing and tweaked the color set to a variant of ZenBurn colors). We have to work with it all day, so we don't like our environments to be butt ugly.
One really bad usability experience was integrating a new C++ file into the project. In Xcode, it's a right-click on the folder in the navigator, then "Add files to foo…", select file, done. After a few tries with Eclipse I needed to google how to do it. Suggestions were to import a "File System" (which seems to mean something different to me than to Eclipse people) and the file may not yet be in its final place. Huh? Drag and drop doesn't work, either, no matter whether I tried "Copy" or "Link": it always told me source and destination were the same file. I've seen an Android expert and a "normal" Android developer struggle with this as well, so I'm not the only idiot struggling with this (google suggests we're not alone).
After two weeks I found out how to do it by accident: you simply copy the file to the final directory and that's it. You then only need to right-click on the directory in Eclipse's navigator and select "Refresh" (or press F5) since Eclipse doesn't observe the directories and thus doesn't immediately notice if a file appeared or got deleted. I only found out because after I started Eclipse I found a lot of "*.orig" files left behind by Mercurial in my project (which causes errors if it's an .orig to a resource XML, BTW). The (non-)management of files is a bit annoying as you always see files in your project that really aren't/shouldn't.
So now I finally had the files I wanted in my project. Eclipse then complained about typos in the comments, but didn't even notice that the file wasn't syntactically correct and didn't compile. Again, native development facepalm.
I hit a similar problem while working through a guide: I downloaded a sample project (FragmentBasics) and wanted to open it in Eclipse. It took a few minutes until I found out that I'm supposed to create a new project and select "Android project from existing source" since it didn't already contained the necessary project file(s).
The Eclipse documentation is extensive but seems to only be targeted at people developing for Eclipse (e. g. developing new Eclipse plugins) instead of Eclipse users. Auto completion creates placeholders and I wanted to find out how to move to the next one: I couldn't find out by looking through all the menu entries and couldn't find out by searching and navigating wiki.eclipse.org either. Even a Google search let me down, but I'm pretty sure such a basic operation must have a keyboard shortcut.
After we've moved the project to a new repository and I thus checked it out in a new directory I got an error from Eclipse when trying to build the project: Errors occurred during the build. Errors running builder 'CDT Builder' on project 'xxx'. java.lang.NullPointerException. This error message is absolutely useless. No indication what went wrong, where or why. I wasted three hours: found the Eclipse build log file with the full stack trace, googled, read Eclipse source, reinstalled Eclipse, messed with its configuration files… in the end it turned out that there now was an absolute path in the .cproject file unknowingly inserted by a colleague. Before his edit it was an absolute path of my machine which I didn't know either because I would've fixed it: absolute paths in files shared in a project are an absolute no-go. Only deleting the file and re-adding native support fixed the issue (this time there was no path reference at all and it still works fine; go figure).
Even though I make it sound like that, Eclipse isn't bad. There were just a few WTFs that annoyed me. So let me finish with more Eclipse features that I really like: "Source -> Override/Implement Methods…". That one is great, especially when working with interfaces. When auto completion does work, I like that it can guess what arguments to insert and often it's correct which saves more typing.
To test iOS apps on your desktop, there's the iPhone/iPad simulator. Apps need to get specifically compiled for it and the binary doesn't work on a real device. Android takes a different approach: it uses an emulator. This means it simulates a device and executes the real code that also runs on the final device. Those two concept have different advantages and disadvantages. The most obvious one: while the iOS simulator (and the apps that run in it) are really fast, the Android emulator is slow. Like, very slow. I mean really, really slow.
I'm working on a quad-core Intel i7 with 16GB RAM and SSD so it's a pretty fast machine. Still, the emulator takes a whole minute to boot and the apps then are very slow. Way too slow for our multimedia app, there is absolutely no chance it can decode video and audio in time. It's able to only play a low one-digit number of frames per seconds. With the iOS simulator, smooth playback isn't an issue at all (since the machine is way faster than a real device). Our Android app on a mid-powered Android phone runs just fine at 25 frames per second.
Unfortunately, the emulator doesn't automatically come to the foreground when you start debugging your app which is annoying. It's also very primitive: there doesn't seem to be a way to actually rotate the emulated device (at least I haven't found any such option). It's also ignoring my keyboard input, I needed to use the usual Android on-screen touch keyboard to input text with my mouse. Maybe there's some magic I'm missing, but I didn't bother wasting any time with this as debugging with the emulator simply doesn't make sense in my case as it's way too slow to run our app anyway.
The debugger used by Eclipse needs lots of improvement. If the app crashes in the native part the debugger doesn't even notice. If you get a Java exception, you can't access the exception itself so you can't see the exception error message, or is there again something I'm missing? After two months, I finally found the "Stop" button by accident: it's only active if you click on the activity in the thread list, in all other selections it's greyed out for whatever reason. I can't tell whether it's debugging my app right now or not as there doesn't seem to be an indicator. Am I just blind? I couldn't spot anything. Those are important usability issues that drive me mad, they're unnecessary and could be easily solved.
A propos "couldn't spot": the break point markers are very tiny and often obstructed by other icons (and sometimes cut in half; Eclipse UI bug).
LogCat seemed to be useless at first: it keeps on scrolling to the end when you've scrolled up and try to read something, even if your app is stopped (detached). After two days I noticed the tooltip over an icon: it said "Scroll Lock". Alright, let's do this like in the last millennium: scroll lock indeed keeps LogCat from scrolling when you don't want it. This is so annoying, I always forgot to lock or unlock the scroll lock. With Xcode, you just scroll and it stays there. Scroll to the bottom and it will follow again. That's how it should be done.
I then wanted to save the log and was surprised that the resulting file was empty: you first need to select what to save. If nothing is selected, Eclipse happily saves exactly that: nothing. Since there is no point in saving "nothing", why not be intelligent and assume that the user wanted to save "all" instead? LogCat also keeps locking Eclipse and beachballing for some time (even with no filter) if there's a lot of messages getting printed. It's annoying that the filter doesn't cover tags.
You also can't select/copy/paste text fragments from LogCat.
There's a command line tool "adb" which also allows you to watch the log. We'll need it in case there's a crash in the native part to get a stack trace (if you're very lucky) since Eclipse can't do it.
Some tutorials are out-dated, most notably the "Building Your First App" tutorial as explained in the SDK Installation section of this rant. I've seen code examples that are syntactically incorrect (missing braces and the like) or are semantically incorrect (method defines a return value yet the code doesn't have a "return" statement). Apart from that, most guides are OK but could be more thorough (and more correct). Especially in light of the API reference…
The API reference immediately reminded me of my times as a Java 2.0 developer as it's using Javadoc (nothing wrong with that), but it's more beautiful. Unfortunately, it's often way too brief: quite often, explanations are missing and sometimes a short code example would be needed. The linked guides sometimes explain things missing in the reference, but not always (and are also sometimes too brief).
I found that the reference quality varies wildly but tends to be insufficient. One example would be the GestureDetector reference. Its overview (where its usage should be explained) is just a mere 72 words (one paragraph and two bullet points). There is no explanation in which order (if any) the listener methods are called and when. By contrast, the reference for iOS' UIGestureRecognizer has 1150 words in its overview, and that's just a base class: there are subclasses that implement the specific gesture recognitions while on Android, the GestureDectector is used to detect all gestures.
It's not just the overviews, the documentation for the methods are often equally bad or even worse (because they're completely missing). For example, the important ProgressDialog class has 21 methods listed, but only three are documented. The remaining 18 methods have no documentation at all!
At least some Google developers have some humor, as you can see in the documentation for View.performHapticFeedback: "BZZZTT!!1! Provide haptic feedback to the user for this view."
There are examples of classes whose documentation is excellent, like the Activity class: lots of text, diagrams, tables, code examples… That's how I expect a reference to look like! The reference for View is of similar quality.
Unfortunately, these are the exceptions. The majority is under-documented, IMHO. Another example: how to find out whether the WiFi changed using WifiManager. StackOverflow to the rescue because the reference isn't of much help here as important things are not documented/explained here. I needed a lot of googling, guessing and trying.
All in all, the documentation is in need of improvement. It could be worse, but it should be way better.
API and bugs
I have the feeling that you need more 3rd party stuff than on iOS. That might simply be due to the demands of my project, I cannot generalize that: for other projects, it might be the other way around.
For me, Android was missing Bonjour/ZeroConf support and missing asynchronous HTTP. The later was a real problem. I found two frameworks for that, the first didn't compile on Android (even though it's a pure Java library) and the second worked fine until I wanted to read an HTTP header of the reply which said library simply didn't support. I wasted several hours solving the seemingly simple "asynchronous HTTP" problem until I finally resorted to just use synchronous HTTP on a different thread. Sounds equivalent but isn't and I find it a bit dirty. Turns out, this is the way to do it on Android. On Jelly Bean, I was then surprised that my code crashed. Reason was that Google decided that instead of providing asynchronous networking APIs it would be easier for them to now simply forbid networking on the main thread. Well, that's one way you can "solve" this issue…
On the plus side, there's JSON: it has always been supported by Android. iOS only has it natively since iOS 5 (and the very nice JSONKit implementation is still way faster).
The constant need for Context objects (for example to get resources or read/save settings) is very annoying. You often need to add warts to your APIs for example because you somewhere decided that you need to read or save some setting. I found various solutions, but all are ugly and pollute otherwise clean APIs. I haven't yet found out why these objects are necessary, I suspect there's a valid reason. Still, I hate that I am often forced to adapt my classes to get a Context instance in various places. It also is big problem with singleton patterns. Again, I'm forced to add warts like artificially starting the singleton in a "disabled" state until I can pass a Context or attach it to another object just because it has access to a Context.
Simply loading/storing an array of strings (either in a file or the settings) is surprisingly difficult. If you want to save a set, that's easy. But as soon as the order is important you either need to use hacks (for example, storing keys "foo_1", "foo_2", etc.) or you need to serialize (for example, using JSON). To store a map/dictionary, you have no choice but to serialize. Granted, under the hood, iOS also merely (de)serializes when you operate on a file, but they have convenience methods so loading/storing are one-liners.
Creating settings screens is pretty easy, I like the XML approach here. But it has serious limitations: as long as your settings are static, it's easy to nest them. But as soon as you've got dynamic elements, like a list, things get pretty ugly. I wanted to have a list with entries created dynamically at runtime, which worked quite well. But if you want it to move on to another nesting level you cannot use the usual solution of nested properties but have to use a new activity. Since I needed to support Gingerbread (still by far the most widely used version at the time of writing) I also couldn't use the PreferenceFragment since the Support Library does not have it (it looks like that's the only fragment not supported by the Support Library; bummer).
I liked the layout based UI, especially RelativeLayout is pretty cool. The downside is that manually changing sizes is problematic: I had the need for a view with a fixed aspect ratio (this ratio can change at runtime). This took a lot of effort. But for "normal" GUI building I like not having to worry about exact positions/sizes. I found that it's easier to manipulate view positions/sizes on iOS, but if done right you have fewer need to do that on Android (with iOS 6, we finally have AutoLayout which has been available on Mac since 10.7 Lion and is similar to RelativeLayout).
Bonjour/ZeroConf turned out to be a huge problem: not all Android devices support multicast and some that do are buggy. This problem affects a lot of apps, not just ours. Since Jelly Bean, there's a native Bonjour/ZeroConf implementation (android.net.nsd.NsdManager) but it's very buggy: for example, it has problems with certain (but not all) service names containing spaces and/or dots. I ended up concatenating the wrongly-parsed service name and then parsing it myself. Sometimes it couldn't resolve specific services, returning an error code that wasn't documented (the only constant with that value, 3, doesn't make sense here). These problems are clearly bugs in the Jelly Bean Bonjour/ZeroConf implementation since JmDNS (an open source implementation) doesn't have any of these problems. But JmDNS often takes about 10 seconds to start. Great. The Jelly Bean implementation is fast but so buggy that we can't use it, and JmDNS works alright but takes 10 seconds on start and requires extra work to monitor the network when networks change. I've ended up using JmDNS, since the Jelly Bean implementation is FUBAR.
The app I'm working on would benefit from low-latency audio. But Android's audio system is far from "low-latency". In fact, I was really amazed by how bad the sound latency is. There's even a bug report dating back to 2009 with lots of people explaining why this is such an important issue, but Google doesn't seem to deem it important enough. The Android audio APIs are absolutely insufficient for high-quality low-latency audio processing and are a serious problem for a lot of apps: Video with lip-sync audio? Possible, but only with low accuracy, you have to accept that you're sometimes a tiny bit off (problem is, humans notice if you're just a tiny bit off; often they don't realize the lip-sync is off but notice that it feels weird). For games and real-time audio processing apps it's a deal-breaker: imagine an action game and you see something happening but the audio feedback arrives with half a second delay. That's a huge and very noticeable delay. I've played Angry Birds on a Nexus 7, and the delay between letting the bird go and the sound was so huge, it was ridiculous: the bird already traveled 1/3 of its distance. And now imagine a shooter or simply an explosion… by the time the explosion animation is done the sound might finally start playing. Absolutely unacceptable.
The delay varies widely between devices but is generally very bad: on the Galaxy SII with Gingerbread it's about 500ms roundtrip latency, the Galaxy Nexus with Jelly Bean has about 525ms, the HTC Sense with Gingerbread between 500 and 630ms! I repeat myself: that's absolutely unacceptable. By comparison, iOS devices have a typical roundtrip latency of 6–12ms. Google claims to have lowered the latency to sub-100ms with Jelly Bean but only for certain devices and specific audio settings. Without exaggeration, this is the worst audio latency I've ever witnessed on a main stream OS, we even had better latency back in the day when our machines had frequencies in the low megahertz range. This isn't a Linux issue, it isn't even a Linux-on-low-powered-hardware issue, it's just an Android issue.
The native audio API OpenSL ES does look very nice at first. Its API has a beautiful design and is easy to use (as far as audio APIs are concerned, that's pretty remarkable as audio processing is complex and complicated). Unfortunately, it's too primitive in many respects. It has no support for precise timing at all and you get too few information, forcing you to sometimes guess what it's doing. For example, you need to feed audio packets into a queue. That implicitly starts audio processing if it's not running (implicit is bad here). You have no way of finding out when a piece of audio is going to be played or was played so synchronizing for example with a video is a guesstimation. The data you feed into the queue isn't copied, it's merely referenced, so you need to take care of memory management yourself. That wouldn't be a big deal, but the callback only tells you that a buffer has been played, it doesn't tell you which buffer or when (since you need to manage your own queue to be able to free the memory, so you can be pretty certain which buffer was played, but I'd have preferred the API telling me for 100% confidence). So OpenSL ES is beautiful and way easier to use than Apple's CoreAudio, but it's also a lot more primitive, unfortunately. Simply playing an MP3 is a piece of cake and the API is sufficient for that, but for serious audio processing it lacks important features.
I was bitten by an undocumented change of behavior in the Preference class: you can apply a custom layout to settings rows. For example, you can use that to make an icon appear next to a settings text on Gingerbread or if you want to have an icon on the right instead of the left. Works great on Gingerbread, but since Honeycomb (AFAIK) a custom layout makes the row disappear! You need to create a subclass of Preference and fix the visibility property manually to make the line appear again, because Google decided that custom layout defaults to invisibility for some reason. Another few hours of debugging and swearing until I found that one.
In our app, there's a list in which every cell has a background image. If you tap the cell, the background should change and then again once the cell is selected so you see what is selected. For things like that, Android has "Selectors" which are really great. But selection in the ListView seems to be broken (and I think the class is a bit primitive to begin with). I ended up using the "checked" state instead of "selected" and needed to create a subclass of LinearLayout to implement the Checkable interface and set the Drawable state correctly. For that, I was forced to use an attribute that is undocumented (or even mentioned in the API at all), you need to grab it from the resources (android.R.attr.state_checked). To find out all of the above and implement it I needed to google, read StackOverflow questions/answers, write test code and wasted a total of two hours (given the questions on StackOverflow, I think I was pretty fast; other people wasted days). Given the amount of questions about that on StackOverflow, this seems to be a very common problem (and I've heard from another Android developer "Oh, so that's how it's done, I solved it in a different way but it has a lot of problems."). On iOS, the desired behavior is well-documented and just a one-liner.
I've got the impression that Google often ignores the developers and their bug reports. Even obvious and easily reproducible bugs seem to get ignored or even declined (see also the linked audio bug report above). To be fair, Apple isn't doing much better here IMHO. So the fact that Android's got a public bug tracker might lead to the impression of Google ignoring the developers even more than Apple. People can see and comment on these bugs; Apple's horribly out-dated bug tracker is closed, nobody except the reporter and Apple technicians can see issues. So Google's bug tracker itself is way better than what Apple offers (every bug tracker is better than Apple's, it's shit; there's even a petition that asks Apple to finally improve it).
The Activity and Fragment concepts are strange at first. An Activity runs in its own process and get destroyed and recreated when rotating the device. I do understand that the Activity concept is an interesting tool that enables solutions that are impossible on iOS, but it seems to cause a lot of problems for normal apps (like, sharing data between the core app and the settings activity). Creating dialogs is unnecessarily complicated with the new Fragments API, the legacy API is easier to use for this (if you only need simple dialogs). Compared to iOS, simple dialogs are harder to create on Android, while more complicated dialogs are easier to create on Android (since iOS doesn't really support them and you need to build them yourself).
In native Code, I really missed Grand Central Dispatch which is an Open Source threading/concurrency library from Apple; it's available for BSDs and also got ported to Linux. It provides solutions for lots of concurrency problems and makes threading a lot easier to handle. Once you know it, you never want to do threading without it ever again. It would've been a great help.
Another (seemingly minor) issue are fonts: not only is the font choice severely limited (just a single font), even the character set is limited. I wanted to print a time range (like 12:00–13:00) and if you're a typo nerd, you use an en-dash for that. Well, the Galaxy SII doesn't have the en-dash. On iOS, you have 39 (iOS 4) to 60 (iOS) fonts available (if I counted correctly), most with the usual italic, bold, bold/italic styles. You can include custom fonts in both Android and iOS apps, but depending on the font that does cost royalties (creating fonts is a lot of hard work, it usually takes a good designer several months to create one; people often think fonts should be free without realizing what it takes to make a good one).
I'd like to finish this section with a few positive things: I like the way resources are handled (the "R" class). The whole resource concept is very clever: you want a special localization just for German in landscape orientation for displays with low resolution in night mode? No problem. That's totally awesome! I especially like the support for plurals in localizations, that's one of the few things I wish iOS would copy from Android. I also found the Drawable concept to be very powerful and versatile.
Now we're entering the deepest depths of hell: Developing using native code. It is the most problematic area of Android development and what really, really makes me hate Android. Documentation is even scarcer than with Java, but the single biggest issue is that debugging is totally broken and usually does not work at all.
I couldn't make native debugging work in Eclipse at all, so always had to use the command line. I know GDB very well, so at first I thought it's going to be uncomfortable but would work. Not so: the GDB is totally unreliable on Android. Often, it didn't stop at my breakpoints and more often than not, when the app crashes GDB doesn't notice or provides a useless "dummy" stack trace. I usually had one of two "dummy" stack traces: one is three levels deep and says it crashed in epoll_wait while the other is just one level deep and in the dynamic linker. If the crash is within the first 5 seconds or so after the app start GDB won't notice that anything happened at all. Overall, GDB turned out to be almost useless as it can only catch and debug about one in four crashes. It cannot detect when the app crashes because a C++ exception was thrown and not caught or when assert triggered (results in the three-line dummy stack trace).
If I did manage to have it break, the stack traces were garbage until I figured out that it's important to start ndk-gdb from the project top-level directory, not the "jni" directory!
What's more useful is to let the app run and crash without a debugger. Android then produces stack traces in the log which can be turned into readable stack traces with the ndk-stack tool. You usually get better results this way than with GDB, but there are still crashes where even this fails. Also, it usually only prints the first one to three stack frames while LogCat seems to show more stack frames. When hunting for a very nasty bug, I usually got a stack trace that was six levels deep, but of course my calling method would have been at the seventh level which was not printed. So very often you just know your app crashed and that's all the information you can get get. You then have to guess and resort to printf debugging which is tedious and time consuming.
I really wonder what the Google guys did to poor GDB to make it this useless. I've been using GDB for more than a decade on Linux, it works like a charm on Mac and iOS, but on Android, it feels like it's just an observer that only sometimes is allowed to get a peek into the app if some magical gatekeeper feels like it.
So here I am in 2012, using printf debugging because that's the most reliable thing to do. Android native debugging is the most primitive/broken debugging environment I've ever used in my life with the sole exception of developing on the Sega Genesis (MegaDrive outside the US): that one had no debugger at all and no text console so even printf debugging wasn't possible. But apart from that, I had way better debugging even in the 80s with DOS. I don't think I'm exaggerating here, in my opinion it's not just bad it's fucking-shit-I-wanna-kill-somebody-right-now bad. Debugging is the most cruel part when working with Android, it drove me mad like nothing else and it frequently let my blood boil! It also has cost me several days of work hunting bugs that would have been trivial to find and fix if the debugger was working correctly.
Eclipse doesn't understand GCC error messages that span several lines, it either only shows the first line or a strange mess. A line with the text "Error 1" and no additional information on mouse over is not that useful. Luckily there's the "Console" tab which shows the raw, unfiltered output. Error messages that relate to templated C++ types are still often pretty hard to parse, the GCC folks say that this would improve with version 4.6, with should be out by 2013. So here's hoping that this age-old problem of GCC might finally improve. Competition by LLVM/Clang seems to have inspired the GCC folks to finally address a couple of things, it seems; that's a very good thing (mind you, GCC still is an excellent compiler, but it's error messages often suck).
So hopefully the static code analyzer will also get to the excellent level of Clang. In it's current form, the static analyzer used by Android/Eclipse has a couple of issues. It doesn't seem to support symbols defined in the second "include-level" and beyond. For example, if you #include <foo.h>, and foo.h does #include <bar.h> which defines the symbol baz, that symbol is marked as undefined by the static analyzer. This renders the static analyzer a nuisance and not a help since it marks lots of symbols as undefined even though they aren't. So I needed to disable those checks. But it also complained that some of my constructors weren't initializing all variables even though they were (in plain sight, no surrounding "if" or other conditionals). It also complained that some destructors were not virtual even though the keyword "virtual" was clearly present. So it produces a lot of false-positives which forced me to disable it.
Maybe the Android/Eclipse folks should switch to Clang whose static analyzer is already very good and still improving fast. Not only does it find errors/problems, it can show the code path that leads to the problem which helps understanding the problem. It has proven to be a very valuable tool in Apple's Xcode. Apple has abandoned GCC for Clang and is contributing to LLVM/Clang for several years now; the fact that Clang is using the very liberal BSD license allows Apple to integrate it into their IDE (enabling such commercial IDE integration is one stated reason for LLVM/Clang to make use of the BSD license instead of GPL). What might be preventing a switch is that it still looks like GCC's C++ support is a bit better than Clang's.
To do correct memory management in C++ I wanted to make use of Boost's Smart Pointers. It was pretty easy to include those as they don't need to have anything compiled, just copy the corresponding headers. But to make the Smart Pointers work I need to switch the C++ STL (Standard Template Library) implementation. For some reason, Android provides three different STL implementations! Each with a different feature set. Why they do this is beyond me.
It's possible to do performance analysis of native code using gprof. In a way it's better than Apple's Instruments since it's more precise (with gprof, the compiler adds logging statements at the begin and end of every function; Instruments pauses the app every few milliseconds and records the stack trace thus is able to miss function calls). It requires more manual work, though. For Java, there's DDMS which looks pretty good but I didn't use it so can't say anything about it (the Java part is not performance critical in our app).
Now that I've done Android development for several weeks I'd like to take one of those huge Android smartphones or even tablet, dip it in Tabasco and broken glass and then shove it up some Android engineer's backside! Sideways! I didn't care for Android until recently, but now that I had to develop for it I really hate it! I really do. The NDK is the single biggest reason, pure Java development might be a little more fun. Eclipse is not bad but it's not fun using it, either. Almost everything about Android development feels like it's not completely finished yet, like I've been given some kind of beta. This is especially true of the documentation which is oh so vital, but yet so low-quality in too many respects.
There were few activities in the past years that stressed me this much. The API and tools (especially the often-useless debugger) filled me with so much hate. It depressed me, because all the anger and aggression hit me almost every day during the first weeks. Luckily, I finally attained resignation and then serenity, with few aggressions in the final weeks. I now regard Android development as a kind of Zen training.
I think you should get free entry to your local S/M club if you're an Android developer: You're developing for Android, even with native code? You're one really tough pervert, here, have a spiked whip for relaxation.
The whole experience made me see Apple's API, documentation and tools in a whole new light: I'm now thankful every day I get to use them as I now know hell.