Regular readers of this blog, or a casual reader who has looked at my posts will quickly realize that I am a big fan of hybrid apps. I have written extensively on the various frameworks, Phonegap and Cordova and even on the rise of Mobile Native JavaScript apps (shockingly MNJS never took off as an acronym). I found a new love for Javascript 5 years ago or so (after a decade of hating it), and the thought of writing a complete App in Java or Objective C filled me with dread. My earlier experiences with both Android and iOS had been worlds of pain, mostly because, in my option, Eclipse and XCode are hostile programming environments. Most of my Android work over the past couple of years has been maintenance of old projects, always in Intellij and when I have to do plugin work for Cordova for iOS I always try to do it in AppCode.
I tried doing some work with Android Studio when it first came out, but Gradle for Android was so buggy and the integration with Android Studio so incomplete at the time I stuck with Intellij and reliable ant builds. But about a year ago I had to make a Q&D little mobile App that only had to run on Android and I thought I would give Android Studio a second try. Wow, the difference a year made -- now don't get me wrong, Gradle builds in studio is still is slow as hell (and I have yet to see Android Studio 2 fulfill the goals of fast compilation and hot-loading but I have faith). The improvements to Android Studio present in 1.5 make it a real option for quickly building an Android App that really seemed to be missing until this point in time. Instead of needing to be a master of tricks, kludges, external libraries, and of Android minutia to create a nice looking app, all you have to do is use the templates and wizards built into Android and out of the box you get a modern looking application. Java is still a ridiculously verbose language, but autoComplete, Live Templates, and other Intellij magic built in Android Studio help alleviate that situation, and if it gets truly terrible I guess I could switch to nicer language like Kotlin.
So I did my usual thing, and tried porting my Recipe Folder to an Android app and wow was I impressed. I actually managed to get 90% of the functionality going in about 5 hours, another 5 hours and it was looking much nicer than the Cordova App that is currently in the App Store. The only real problem was at that point I had 10% left to finish (and as most programmers know the final 10% takes 90% of the time) and also I decided to add a bunch of new features because releasing a second total rewrite (I rewrote from jQuery-Mobile to Topcoat-Touch last year) of the app without adding any features seemed like a bad idea. So my grand rewrite in Native has been stalled for the past couple of months, as I keep adding new features.
In my switch back to Native Android development, and looking at the space with fresh eyes I have come up with the Good, the Bad, and the Ugly of native Android development (in comparison to Mobile Web Dev). Oh and when I say Native I mean Java, not actually using C++ NDK, which is also a possibility on Android if you want to write the truly best performing apps (and lose far too much hair and sleep).
The Good
- Great templates, wizard, and development environment. First there is the fact that it is built on Intellij which I am a massive fan of, their refactoring, code completion, code navigation, local history, and a myriad of other features The continuous code analysis provides amazing feedback (and really helps you learn about a lot of Android best practices) The preview engine is not perfect (sometimes the end result looks very different from the preview you are shown), but it is pretty good.
- Easy access to all the APIs including the plethora of design driven APIs and animations that have come with Material Design.
- Native look and feel, no matter how hard people try to get a truly Native look for material design in Android, it always fails a little short. The best hi-brid apps eschew this, and use their own design aesthetic, but it is nice and comforting when running a nicely designed Android app that is using material design.
- Libraries, libraries, libraries. Since it is such a huge ecosystem, there is a library for pretty much everything you want to do, and for these most part those libraries are of higher quality than a lot of the Cordova plugins (that is not to Dr disparage the Cordova plugins, but due to the raid changing of the underlying base library, the fact that each plugin author(s) had to maintain at least 2 but up to 6 implementations, and the difficulty of writing testing, means that Cordova plugins -- even the sanctioned plugins from Apache -- are frequently buggy to some extent.
- You have much more control of things like performance, garbage collection, and animations so that if you really need to improve the performance of some aspect of your app, you can. While sometimes there are things you can do in the browser to improve performance, you are frequently limited to the nature of the browser and have to just settle on removing animations or features to make it perform better.
- There are a ton of not only libraries, but services like Fabric which does crash reporting, analytics, and is completely free (there is now a plugin for cordova, but I haven't used it) and has such a slick install process that it is a snap to get up and running. Error reporting (especially if you minification and concatenation of your code in Cordova is a nightmare).
- There are a ton of automated testing tools for Android: Espresso, UI Automator, Robotium, Monkey Runner, Appium. Although there are some tools for automated web testing on Android, I haven't found any that actually work with Cordova apps -- I know that Selendroid is supposed to work, but I have had no luck with it over the year (let me know in the comments if you have found a good testing framework for Cordova apps that actually runs against devices). So you are stuck writing mocha, karma, or protractor tests.
- At Google I/O last year (2105), Google announced the Design Support Library. This design library is really great at quickly getting you started with Material look and feel for your app. The Coordinator Layout is great way to get a real intricate and smooth animations working between screen transitions, and goes a long way in giving you a professional looking app with very little work. It also provides fabs, tabs, Snackbar, and the Navigation Drawer.
- The final real advantage I see is the stability of developing in Native code. I have spent the past few years fighting with bugs in Cordova, bugs in Cordova plugins, bugs or unimplemented features in the browsers in older versions of Android (as going forward the Chromium browser that is shipped with Android is getting updated so hopefully is going away, and with Crosswalk it may be a solved problem). There are the odd bugs in the Android SDK and Frameworks, but these are mostly documented on sites like StackOverflow, the rapid release schedule of Cordova and complexity of the project and few number of developers and testers compared to Android leaves it as a buggier development platform.
The Bad
- While the preview in Android Studio is getting pretty good, there are lots of times when things will look fine in the previewer and look completely different when running on a Device or on an emulator (and it may be only one version of Android where it looks different or wrong). Theming/styling in Android is confusing, challenging and if my experience is typical, quite error prone. You can have similar issues if you are using the latest CSS in Cordova and you test on a device that using an old version of the Android browser or some such, but generally if you design a site in the Chrome browser you are pretty sure what it will look like when you load it on a device.
- The way that you wire events to controls seems like something out of 1990's or early 2000's where you have to manually wire up Event Classes for each control that you want to respond to:
Button clickButton = (Button) findViewById(R.id.clickButton);
clickButton.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
Toast toast = Toast.makeText(context, "Hello from button", 500);
toast.show();
}
});
- If you have a dozen or so controls you want to set actions for you end up writing these onCreate functions with hundreds of lines of boilerplate code that is difficult to navigate around. Android Studio now auto-collapses a lot of boiler-plate code so it doesn't look quite as bad. Also you can have the Activity or Fragment class handle all onclick's in one function and use a switch to delegate event handling, or you can use AOP and a library like Butter Knife to handle it a much cleaner manner.
- While it has certainly got better over time, I don't believe that Gradle will ever be confused with a speed daemon, I mentioned this above, but it is a pain point and worth mentioning again. I eagerly await the promise of Hot-Swapping in Android Studio 2.0, but for now, code rebuilding is currently painfully slow. While Cordova is not much faster if you are actually doing a full compile, there are a lot of plugins and tooling that allow you to rapidly develop in PhoneGap. The magic that is Chrome Dev tools is also a huge plus for Cordova development, but if you are doing Native Android (with any Network Calls) spend the time it takes to get stetho working it is well worth the time investment.
The Ugly
- While the Support Library is arguably an amazing and important feature that allows modern features and a modern look on older Android devices, it is also a huge mess. Accidentally call getFragmentManager() instead of getSupportFragmentManager(), or use import android.app.Fragment instead of android.support.v4.app.Fragment and everything will appear to work fine, until it randomly doesn't. I'm sure eventually the tooling will still warning developers about these potential errors, but if you are like me you will spend a lot of hours pulling your hair out only to discover you used the support library when you didn't mean to (because you were targeting an Api 14+ device) or used a standard app library control when you meant to include a support library control. Or you couldn't figure out what namespace to add when you wanted to reference a support library control in your activities XML.
- Fragments, like the support library, are another great idea that works great until it doesn't. While fragments can save time, and prevent code duplication, they also add a bunch of potentially unnecessary complexity (especially in Fragment Lifecycle). If you are planning on creating a tablet and phone version of your app then they may be worth the trouble, but in a lot of cases they are not worth the headache.
- Java is an old language, not as old as C or even Objective-C but it has a lot of old baggage and is missing quite a few features that more modern languages support. It is also a very verbose language, and the lack of some way of passing functions (like delegates in C#) means that the only way to do events is through implementing interfaces in anonymous classes. This has the effect not only of computational overhead whenever an event is declared, but also leads to ugly code (see above). Also Java may not have been the best language to choose for a memory restricted device as the amount of time that is spent fixing Jank due to garbage collection and the fact that performance optimization states you should use integers rather than enums. That there is a giant section on micro-optimizations on the android doc, that we are going to be on our third VM Runtime (Dalvic, Art, and now maybe OpenJDK) says that Java may not have been the best choice for Android -- but I guess we are stuck with it.
- The last truly painful aspect of developing for Android is that you have to get your head around the Android lifecycle and what that means to development. Android Applications, Activities, and Fragments have to be ready to be destroyed and recreated at any time. At first you may think that this is no big deal, but then you must remember that not only do you have to keep ready to serialize and deserialize the state of an Activity at any time (hence the provided onSaveInstanceState and onRestoreInstanceState that you should override if you Activity requires any state), but also you really cannot have any global variables for your application unless they are variables that can be recreated at any time. While that may sound very appealing (we all know that global variables are a bad thing), until you realize that you must either pass in any Auth Tokens you need to all your activities, and that you must be prepare to reload any data your app retrieves from a database or a web service at any time. If you don't proactively prepare for these global variables disappearing, you will end up with a lot of null pointer exceptions and crashes that only show up when your app is used after it has been backgrounded for an extended period of time. This also explains why so many android apps (especially games) effectively start over when they are backgrounded even for short periods of time. Of course Cordova apps are not immune to this, they just always start over and you won't see the crashes you might see if you haven't handled your Native Android app properly.
In summary I would say that at least trying Native is worth doing if only so that you know how to gauge the differences between Native and Hybrid development. You will get more comfortable with the platform and that will certainly help you out when you have to debug a plugin that is acting wonky. As a pragmatist I believe it always the right idea to pick the best tool for the job, and when developing an app it is important to know what you are getting when you choose to pick between Native and Hybrid. If you have never developed both, you are not in the best situation to lay out the positives and negatives for each approach. I will certainly continue developing Hybrid Apps, but if the case is right I will also develop Native apps as well. IU guess once a generalist, always a generalist.