Last month, The Big Nerd Ranch released the second edition1 of its guide to Android programming. The book leads the reader through developing several Android apps. In addition, it brings up concepts that readers don’t need to understand to write the apps in the book, but might nevertheless find interesting. To address these concepts, the authors have sprinkled skippable sections throughout the book called “For the More Curious.”
I liked that idea, so I decided to extend it! This is the first of several posts that serve as an extended “For the More Curious” reading companion to Android Programming: the Big Nerd Ranch Guide, 2nd ed.
You’ll notice some common themes in my reading companion posts:
- Interest in automated testing. I’ll ask questions about how to unit test new features in Android apps, and I’ll lean toward conceptual architectures that make classes easier to test.
- Interest in UX Design. In addition to improving the design of my Android code, I’d like to improve the design of the screens it produces. So I’ll dig into the design side of Android a bit.
- IDE shortcuts. I use Android Studio to write Android code, and I like to do it without removing my hands from the keyboard as much as possible. So if I learn a mouse-click way to find something, I’ll try to also learn the keyboard shortcuts to find it, too.
Without further ado…
Android Programming for the More Curious, Part 1: pp. 1-150
Android Studio Features and Navigation:
- BNR mentions that you can see all your breakpoints with Run -> View Breakpoints. You can also do this with CMD + Shift + F8. You can also set and unset breakpoints by putting your cursor on a line and hitting CMD + OPTION + Shift + F8. I hit that key binding with my left pointer on CMD, my left middle finger flat on both OPTION and Shift, and my right middle finger on F8. I know other people who prefer to do the left-hand complex with their pinky on Shift, their middle on OPTION, and their pointer on CMD.
- BNR mentions that you can run Lint—an Android-specific static analyzer—with Analyze -> Inspect Code. You can also run a specific inspection by name with CMD + OPTION + Shift + I. That key binding pops up a modal that shows you lots of inspection options. It’s worth a look just to see all the things that the analyzers will check for you: from button order, to paddingLeft instead of paddingStart, to finalize() declarations.
BNR mentions that you can receive a variety of result codes upon starting an activity for a result. A result code is an integer that tells the starting and receiving activity how the started activity finished. The default one is RESULT_CANCELED (0), and that gets returned always if you do not call setResult(int resultCode, Intent data) in your started activity. You can also return RESULT_OK (-1) or RESULT_FIRST_USER (1), which exists as an offset to define your own custom result codes.
So what does this mean? Actually, the Android Developer Documentation doesn’t elucidate all that much. That being said, an example of when you would want to use this might be if you were streaming video. Suppose you have an activity that starts the video-showing activity. If back gets pressed, then the starting activity wants to know what happened with your watching of the video. Maybe it wants to send you an evaluation form if you finished the video, or it wants to send you a ‘Why did you leave?’ dialog if you pressed the back button part-way through the video. Then, let’s say it wants to suggest a different video if you press back before even pressing play on the video you chose. (We’re not going for UI smoothness and realism here. This is an example). You can define result codes like RESULT_FIRST_USER +1, RESULT_FIRST_USER +2, and RESULT_FIRST_USER +3 for each of those states, and execute logic in the starting activity conditional on those three result codes.
There are two ways to start fragments from your activities: in the layout, or programmatically. BNR focuses on the second of these, for good reason: you cannot programmatically control the life cycles of layout fragments . I would add another reason to prefer starting fragments from your activity class rather than the layout: it’s much easier to test those fragments, which is critical if you rely on automated testing to drive software design and prevent regressions as your app grows.
That said, I had only ever started fragments programmatically, so I wanted to know what a layout fragment looked like. As it turns out, the Android Developer Documentation had an example. Here is a version of it:
Why would you ever do this? I can think of a possible reason having to do with testing. Though I am not convinced I agree with this reason, I could certainly see how someone might argue it. When we write automated tests for an app, we want to test all of the app’s programmatic behavior. That way, we know if a change we have made affects the way our code executes. There is less justification for testing the contents of static layout files: if this information just gets inflated and our java code never alters it in any way, then there is no reason that our system ought to break it, and therefore there is less need to check that our system is breaking it.
Automated testing for mobile frameworks, though, can be a challenge. That includes Android. Developers face some limitations when it comes to automated testing of Android apps. Fragment behavior especially introduces noticeable limitations. One must have a faked, mocked, or stubbed activity (or parent fragment with a faked, mocked, or stubbed parent activity of its own) to inflate a fragment, and we must have ways to control and monitor the fragment’s life cycle in tests. The Robolectric testing library provides a wide selection of tools for testing Android. It’s even quite fast, at least compared to Android’s built-in testing tools. However, when an app gets complex enough, an all-in test run can take multiple minutes. This is long enough that developers stop running all the tests all the time—which can lead to larger and more confusing regressions. In particular, the test fragment manager performs some of the most time-expensive operations I’ve seen in Robolectric. So if you start a fragment programmatically and you want to test that it gets started properly, your test will take longer to run. That can add up in a test suite with 1000 tests.
That said, maybe you don’t have complex requirements for controlling the life cycle of a given fragment. Maybe you’re using a fragment because you want to follow the AUF (always use fragment) mantra, and you just start the thing up when the activity starts up and pretty much leave it there in the activity’s view. In order to avoid the extra time it might take to run a test of whether the fragment starts when the app starts, one might argue that it doesn’t need to be tested if it’s just hard-coded into the layout. Like I said, not convinced I agree with this, but I can see why it might be tempting.
BNR mentions that Android 4.2 introduced nested fragments: fragments that can be inflated inside of other fragments. (Previously, fragments could only be started from activities, and they could not go inside one another). So what does this look like in practice? It’s quite similar to starting fragments from activities, and the Android Developer Documentation again provides some context. To start child fragments inside of a parent fragment, you can call getChildFragmentManager() inside the parent fragment. This returns you an instance of FragmentTransaction, just like getFragmentManager() or getSupportFragmentManager() does in your activities. You can call all the same methods on this fragmentManager as you could on a fragmentManager summoned from an activity, including beginTransaction(), add(int layout, Fragment fragment), and commit().
note: you can have layout fragments and you can have nested fragments, but you cannot have nested layout fragments. No inflating fragments into layouts that contain fragments defined therein.
BNR mentions that, when one wishes to set a listener on the checked-ness of a checkbox, there are two import options for the OnCheckedChangedListener. The book suggests using android.widget.CompoundButton. I went to see what the other option was, but as of August 27, 2015 it looks like there’s just the one:
I do have AppCompat and the support library in this app, so maybe I’m missing something else that offers the other OnCheckedChangedListener.
Android Design Guidelines
BNR mentions that the Android Design Guidelines suggest using 16dp gutter margins, and that those guidelines provide a variety of suggestions that developers can follow to make sure that their apps provide good user experiences on a variety of devices. I checked out these guidelines, and they are pretty boss:
They feature material design, a metaphor used to describe the functionalities that Android 5.0 added to create more tactile user experiences. Tactile, in this case, means something like touchable. These libraries provide textures and animations that help make it obvious to your app’s users how they should touch your app, and where.
For example, check out this list of CardViews that I asked an example app to spit out for me:
I did not do any custom styling to make them look like that: the rounded corners and raised-off-the-background look come in the CardView specification. I can tap them and scroll them like normal list items, but they look so much more fun to touch!
Speaking of CardViews, the BNR book goes over Recycler Views that produce a list of items like this list of cards. In the next ‘For the More Curious’ post, I’ll dive into that, with some code samples and drawings to illustrate how to combine RecyclerViews and CardViews to make lists of cards. Look out for that in the next few days!
1. Compared to the first edition, the second edition focuses less on backward compatibility with Android OS 2.x versions and uses examples from Jelly Bean, KitKat, and Lollipop. It is still about 60 pages longer than the first version, with ample sections devoted to Android 4.x’s improvements on previous OS versions.↩