Monday, September 15, 2014

Starting From Scratch: Android - Activities


This week we're continuing the Starting From Scratch series. Today we're going to take a look at Android Activities. I'll discuss what an Activity is, how you create one, the Activity life-cycle, launching an Activity programmatically, and finally I'll give a few Activity tips I've found along the way.

What Is An Activity


According to the Android documentation, an Activity is "... a single, focused thing that the user can do." Yeah, that's pretty vague. So let's break it down.

In the early days of Android (pre-tablet) an Activity represented pretty much everything on the screen, how it got there, and how you interact with it. Basically the Activity was analogous to a "screen" in an application. If you wanted to display a different screen you either had to dynamically tear down the current screen and rebuild the new screen (very laborious) or, preferably, start a new Activity which owned the UI and interactions for the new screen. This allowed developers to encapsulate the concepts within a screen pretty nicely and manage workflow easier.

As Android matured, and tablets became mainstream, it became necessary to reuse portions of an Activity within other Activities. Thus was born the Fragment (which I'll talk about later in this series). Prior to Fragments a typical application would have many Activities. Once Fragments were introduced the number of Activities an application had were reduced from one per screen to one per workflow.

Creating an Activity


There are two steps necessary when creating an Activity. The first is to write the Java class which extends the Activity class. This class should implement any necessary life-cycle events (which are outlined below). The second step is to declare the Activity in the Android manifest. You can see an example of this by looking at the initial Activity that was created for your application in your AndroidManifest.xml.

Activity Life-cycle


An Activity has several states that it can be in. Those states are associated with creating, starting, resuming, pausing, stopping, or destroying the Activity.

Creating


When an Activity if first being created a call will be made to the Activities onCreate method. The onCreate method is responsible for associating a UI layout with the Activity. This is done via a call to the setContentView method with the associated layout ID. As mentioned previously in this series, a layout is a composition of the UI elements to display on screen.

For Example:
@Override 
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
}

If you have any state that needs to be restored before the official call to onRestoreInstanceState you can do it after the call to setContentView using the saved instance state Bundle passed in. The typical reasons you may want state restored here is if you want to be able to make any decisions on the other life-cycle methods based on the current state of the Activity. Because of this, the onCreate method is passed a Bundle which will contain any state that was saved when the Activity was paused.

You cannot assume that the Bundle passed in to the onCreate method will exist. In the case that there is no previous state or if it's the first time the Activity is run the Bundle will be null. Therefore it is very important to check to make sure the Bundle exists before you try to use it. If the Bundle exists you can use the information you've saved in it to restore the internal state of the Activity.

NOTE: It is really important to draw the distinction here between resetting the internal state of an Activity and resetting the UI of an Activity. YOU SHOULD NOT try to interact with the UI in the onCreate method. The onCreate method is called when the Activity is starting but before it is displayed on the screen. If you try to get a handle to UI elements they will not exist.


Restarting


The onCreate method is only called when the Activity is not already in memory. Often it is the case that the Activity has been paused but not cleared out of memory. In this case when the Activity is about to be re-started the onRestart method is called. For most people there really isn't anything you need to do in onRestart. The internal state of your Activity will be the same as when it was paused, so there is nothing to really restore. The exception being if you are using a Cursor.

@Override 
public void onRestart() {
}

Starting


The onStart method is called after either onCreate or onRestart when the Activity is visible to the user. The Activity UI is not yet ready to be interacted with. At this point you still don't want to do anything programmatically with UI elements but you are able to reset internal Activity data.

@Override 
public void onStart() {
}

Resuming


Finally, we're at the point in the Activity life-cycle where you can do something with the UI. The onResume method is called right after the Activity has been displayed on the screen. At this point the UI is ready to be interacted with. If you did not register your UI event listeners in the layout file this is where you would want to register those listeners.
@Override 
public void onResume() {
}


At this point in the life-cycle your Activity is considered to be running.

Pausing


When your Activity is about to go into the background, but it is still active, the onPause method is called. This happens when another Activity is about to be put on top of your Activity (while it's still running) or your Activity is about to be put into the background or killed.
@Override 
public void onPause() {
}

This is a good time to commit changes to data and unwire any listeners that you've set up. It's important to note that onPause doesn't necessarily mean that your Activity is going into the background. This will also be called when a dialog is displayed on top of your Activity. In this case the next life-cycle event will be onResume instead of onStop.

It's important that this method returns very quickly as the onResume method for the Activity about to show will not get called until this method returns.

Stopping


The onStop method is called when another Activity has come to the foreground and fills the entire screen. On stop cause be used to save state as well as stop things that are time intensive.
@Override 
public void onStop() {
}

Destroying


The onDestroy method is called right before your Activity is about to be removed from memory. This is a good time to clean up the internal handles that your Activity may have.
@Override 
public void onDestroy() {
}

Saving State


In the previous life-cycle events I mentioned saving/restoring Activity state several times. Users will expect an Activity to be in the same state that they left it in. The user can leave the Activity for a variety of reasons like to answer a phone call or to use another app. Sometimes when a user has a lot of applications running or is doing something very memory intensive Android needs to completely remove the Activity from memory when it's not on the screen. In that case displaying the Activity again will cause it to re-display as if it was never on the screen to begin with. This is why it is important to make sure you restore the internal Activity state if there is any to restore

Saving state is done via the onSaveInstanceState method. Restoring state is done via the onRestoreInstanceState method. The onSaveInstanceState method is called before the Activity is killed. If onSaveInstanceState is called, it will be called before the onStop life-cycle event is called. The onSaveInstanceState is passed a Bundle which should be used to save instance state too. That bundle will then be passed back to the onCreate life-cycle event.

onRestoreInstanceState is called after onStart and is used to restore any state saved in the call to onSaveInstanceState. For example:
@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString("SomeVariable", someVariable);
@Override
public void onRestoreInstanceState(Bundle savedState) {
    if(savedState != null) {
        this.someVariable = savedState.getString("SomeVariable");
    }
}

Launching an Activity


There are two main ways to launch an Activity. The first way is to specify the type of action you want performed and allowing the user to select the appropriate application they want to handle that. This is commonly used when sharing data or launching a url.
private void openUrlInBrowser(String url) {
    Uri uri = Uri.parse(url);
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    this.startActivity(intent);
}

The second way to launch an Activity is by class name. This is the case when you want to launch a very specific Activity. Typically this is how you would launch another Activity within your application.

private void openSomeOtherActivity() {
    Intent intent = new Intent(this, SomeOtherActivity.class);
    this.startActivity(intent);
}

In either case launching an Activity is a two step process. The first step is to create an Intent which details what you want to do. You decide how you want to launch the Activity based on the Intent constructor you select. The second step is to call the startActivity method from your Activity.


Activity Tips



  • Only restore an Activities internal state in onCreate. Never try to restore UI.
  • The first time an Activity is being displayed, the Bundle passed into onCreate will be null.
  • Reset an Activities UI in onResume.
  • Don't change the name of the Activity used to start the application. Changing that will cause the any home screen icons associated with the previous Activity to be broken after the application is updated. The is because the icons are associated with the previous Activity name.



Monday, September 8, 2014

Starting From Scratch: Android - Resources

This week we're continuing the Starting From Scratch series. Today we're going to take a look at Android resources. There are many types of resources in Android. First i'm going to outline the different resource types. Then I'll explain what configuration qualifiers are and what they're used for. Finally I'll call out a few important configuration qualifiers that will be used in most applications.

All resources in your Android application are stored in the res/ folder of your project. Resources are grouped by type and configuration.

Resource Types


The easiest way to explain the different resource types is to simply list out the folder structure and what type of resource is provided in each folder. The following is a list of the various resource types.

res/
anim/Store tween animation XML files in this directory.
color/Store color XML files in this directory.
drawable/Store bitmaps (png, jpg, gif) files, or XML files for bitmapsframe animations, 9 patches, state lists, and shapes in this directory.
layout/Store layout XML files (UI composition) in this directory.
menu/Store menu XML files in this directory.
values/Store XML files for strings, styles, other value types in this directory.

Configuration Qualifiers


One of the most powerful things that you can do with Android resources is specify configuration qualifiers for the various resource types. Configuration qualifiers allow you to target specific resources towards a specific device, screen size, or etc. There are different configuration qualifiers depending on the context in which they are used.

Configuration qualifiers are appended to the resource type and separated with a dash (<resource type>-<configuration qualifier>). I won't go into detail on each qualifier as table 2 in the Android resource documentation provides an exhaustive list of all qualifiers. But I want to call out a few different qualifiers that are important to know.

Screen Pixel Density


Screen pixel density qualifiers are used with drawables and help you to avoid images that look terrible from being scaled up as well as providing lower resolution images for screens that are not very dense.  There are six types of screen pixel densities nodpi, tvdpi, ldpi, mdpi, hdpi, and xhdpi.

nodpi is for resources that you do not want scaled. tvdpi is for screens that fall in-between mdpi and hdpi (like televisions). ldpimdpihdpi, and xhdpi are for low, medium, high, and extra high density screens respectively.

For example, images in the res/drawable-mdpi/ folder should be images that are ideal on a medium density screen.

If a screen pixel density is not specified that's ideal for the device Android will scale up/down based on the best match density.

Language and Region


Language and region qualifiers use the ISO 639-1 language and an optional ISO 3166-1-alpha-2 region preceeded by a lowercase r. For example fr-rCA for Canadian French. Language and region qualifiers are the easiest way to localize your application and provide different text translations for different languages and regions.

Screen Size


Screen size qualifiers allow you to target screens using small, normal, large, and xlarge. When targeting Android versions prior to Honeycomb these are best way to provide layouts optimized to specific screen sizes. But, it's important to note that the screen size qualifier is not the ideal way to determine phone versus tablet. There are some tablets with lower screen sizes than some of the higher end phones. This is because the size relates to the density of the screen.

If you're providing an application that supports a version of Android prior to Honeycomb and you want to target screen size you should use a combination of screen size and the smallest width qualifier.

Smallest Width


The smallest width qualifier allows you to specify screen sizes with a minimum number of density independent pixels. The format us sw<N>dp. For example layout-sw600dp provides a layout for screens with a minimum of 600 density independent pixels.


In Oct 2013 I gave a presentation at the Big Android BBQ on Building For Multiple Screens. If you're interested in a deeper dive into the subject checkout my presentation slides.

Monday, September 1, 2014

Starting From Scratch: Android - Creating Your Project

This week I'd like to start a new series called Starting From Scratch. The idea behind the Starting From Scratch series is to give you real world insight into a specific topic. According to PC World, Android currently makes up 84% of the overall smartphone market. So I thought a good place to start the Starting From Scratch series would be with Android.

If you've been following this blog for a while, you know that I'm not a fan of the traditional IDE. Given that, I won't be using Eclipse for this series. I'm going to walk you through creating a project from the command line.

Installing the Android SDK Tools

The first thing you need in getting started with Android development is to download the SDK. Head over to the Android developer site and download the SDK tools (you don't need the ADT plugin for this series). The second thing you need to do extract the SDK tools and, for convenience, add them to your path.

On *nix, for the latest version to date, this can be accomplished by running:
$ cd ~/
$ curl http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz | tar -xzvf -
$ echo 'export PATH="$PATH:~/android-sdk-linux/tools:android-sdk-linux/platform-tools"' >> ~/.bash_profile
$ . ~/.bash_profile
Installing the Android SDK

Installing the SDK tools doesn't actually install any of the SDKs. The next thing we need to do is install one or more of the Android SDK versions. Which SDK versions you install depends on your applications target audience. You should take a look at the Android Platform Versions website to see what versions you should install. For this tutorial we're going to install the platform tools and Android 4.4.2.
$ android update sdk --no-ui -t tool,platform-tool,19
Creating a project

The android tool that comes with the Android SDK allows you to create projects from the command line. There are several things that you need to provide the tool when creating the project. These are:

  • Android version
  • Package name
  • Path of the project
  • Name of the Application
  • Name of the main Activity

The Android version is the name of the Android version you want to target. You can get a list of all installed Android versions by running $ android list target.

The package name is the namespace to use within your project. This is typically your domain in reverse. For instance if your domain was www.foo.com your package name would be com.foo.

The path is the directory where the project files should be created under.

The name of the application is, well, the name of the application.

The name of the main Activity is the name of the Activity that will be launched when a user opens the application. I'll get into what an Activity is later in this series.
$ cd ~/
$ mkdir MyFirstAndroidApp
$ cd MyFirstAndroidApp
$ android create project -t android-19 -k com.example.myfirstandroidapp -p . -n MyFirstAndroidApp -a MyFirstAndroidActivity

Building The App

At this point you actually have an app that can be built and installed on a phone. You should follow these instructions and put your device in developer mode. Once you have your device ready you can plug it in and run:
$ ant debug && ant installd
The default Android command line build system is built on top of Apache Ant. Ant uses a build file which tells it how to to build a project. When you installed the Android SDK tools you got several Android specific Ant targets which have been created to allow compilation, signing, and other interactions with your application build artifacts. When you created the project using the android binary a build.xml file was created for you in your project directory. This is the Ant build file.

Running the command ant debug tells the Android build system to compile the application for debug. The ant installd command tells Ant to install the debug Android package (APK) on the connected device.

Monday, August 25, 2014

Backing up using git

Over the years I've found that as I move from machine to machine, whether adding a new machine to my collection or replacing an old one, it's a pain to constantly copy files from one place to the next. I've also found that the backup process tends to be error prone as I either forget to back something up, don't backup often enough because of having to plug in the extra drive, needing to retrieve something that I've deleted, or I accidentally overwrite something in the backup.

There have been several projects, both open source and commercial, that help solve these problems by doing incremental backup. But I've found that they tend to be platform specific or not supported on the wide variety of machines I have in my mix. Most of them also still require you to use an external drive in order to protect against harddrive failure.

A few years ago I switched to using git as my backup mechanism. It doesn't require me to hassle with an external drive while still solving the problem of protecting against disk failure. It's supported on all major platforms. There are cloud hosting options as well as options to host locally. It provides incremental backup. I can easily exclude things from backup as well as retrieve deleted items.

Initial creation (one-time steps)

Create repository to back up to on backup machine


$ ssh your.backup.machine
$ mkdir -p /path/to/backup/location/my_backup_repository.git
$ cd /path/to/backup/location/my_backup_repository.git
$ git --bare init
$ exit

Add repository location on machine to backup


$ cd /path/to/backup
$ git init
$ git remote add origin USER@your.backup.machine:/path/to/backup/location/my_backup_repository.git
$ echo "backup using git" > README
$ git add README
$ git commit -m "Initial Commit"
$ git push origin master

Performing the backup


It's important that BEFORE you back anything up, you encrypt anything that you don't want backed up in the clear. I like to encrypt things using openssl because of it's wide range of platform availability.

Make sure that you .gitignore anything you don't want backed up. Often I backup my OS X ~/Documents folder. But this is the default location for some application configuration that I don't want backed up. It's important that you add these folders to your .gitignore so you don't unnecessarily back them up and use up unwanted space.

On backup machine


$ cd /path/to/backup
$ # encrypt any files you want encrypted
$ git status
$ git add .
$ git commit -m "backup $(date +'%Y-%m-%d_%H%M')"
$ git push

You now have a secondary backup of your important files. But even better you have a simple way to restore from a backup.

Restoring a backup


$ cd /path/to/restore/to
$ git clone USER@your.backup.machine:/path/to/backup/location/my_backup_repository.git ./

Done!

Monday, August 18, 2014

Are you teaching anyone anything new?

If I had to pick only one piece of advice my grandfather gave me to share with others it would be that the best way to learn something is to teach it.

You may be thinking to yourself that this doesn't make any sense. How can you teach something to someone that you don't already know? The answer is simple, you start by learning yourself.

Teaching someone else something new is not about being exhaustive and not always about being comprehensive. It's really about setting them up to understand the problem domain and to be able to intuitively answer, or find the answers themselves, to questions about that domain.

Here are some questions you can ask yourself in order to prepare for teaching something you don't already know about.

What is the context?

When do people use this approach/tool/etc? What's different about it than it's alternatives? Does it require a certain platform, design, or methodology? Who is it's target audience? How is it consumed? How is it distributed? What's it's history? What problem is it trying to solve?

These are all basic questions that help you understand why this thing exists. Understanding it's purpose is one of the first steps in understanding how to use it.

What are the fundamental assumptions?

Almost everything you interact with makes some type of assumption. A t.v. assumes you can see or hear. A microwave assumes you have electricity. A car assumes you have have a license.

What assumptions exist in your problem domain? How have they influenced the decision making in that domain?

What are the tools?

Are there particular tools that a "standard ..." would use? For example a plumber uses a monkey wrench. A Java developer uses Eclipse. A .Net developer uses Visual Studio.

What are the tools that are used to aid in solving problems in that domain? How are they used and in what context? Understanding what tools to use will save you time that you would otherwise be spending re-inventing the tool and it's use-case.

What are the gotchas?

Every problem domain has some area or areas that people get hung up on. C++ has pointers. Object-oriented has polymorphism and inheritance. Java has the final keyword and it's many different meanings.

These are good places to start. Understanding where people get hung up is usually a good place to learn what people are missing or misunderstanding that has caused them to get hung up.



Monday, August 11, 2014

What does your development community look like?

What does your development community look like? No, I'm not asking what you're development environment looks like. And I'm not asking what development frameworks or platforms you're using. I'm asking what does the tribe of developers of which you are apart look like?

Growing as a software engineer is not just about learning new algorithms, platforms, or tools. It's also about learning systems and methodologies. Here's some questions you should ask about your development community to make sure you're putting yourself in a place where you are able to grow and grow others.

Are the people in your community able to challenge each others ideas?
Does your development community foster the idea of critical review? When ideas or plans are presented does the community enable constructive criticism? Do egos get in the way of someone pointing out flaws in designs or approaches? Is dissent about an approach fostered or is it taken (or given) as a personal attack?
Exposing your ideas, approaches, and designs to a larger group is a great way to expose holes, risks, and errors in approach. But only if the community embraces and fosters an environment where criticism is constructive. It has to be okay to be wrong. And it has to be okay to change your mind.
If your community encourages healthy debate and constructive criticism you will grow as an engineer. Your designs will be better and less error prone, simply because it will account for more than you could have come up with on your own and it will account for context that you do not have.
Are you being exposed to new or different paradigms?
Have you ever switched between a statically typed language and a dynamic one? Or procedural programming and object-oriented programming? What you notice when you do is that the assumptions are not the same. Nor are the trade-offs or optimizations. Being exposed to a wider range of development paradigms helps you to start asking new or different questions about your current development paradigm.
For example if you've been a hard core java programmer and never been exposed to Ruby you may not have been exposed to lambdas or closures. You won't have experiential knowledge of the problems they solve or their usefulness.
Does your community include people with a range of backgrounds?
Does everyone in your community use the same tools and frameworks?

Does everyone in your community use the same tools, frameworks, and platforms? Each tool, framework, and platform comes with it's own set of idioms and standards. They all have their pros and cons, but understanding the trade-offs will help you to think more critically about the problems you are trying to solve. It will foster choosing the correct tools to solve your problem rather than shoehorning your problem into the tools you know.

Monday, August 4, 2014

What's your measure of success?

In the software industry, or any industry for that matter, you're constantly making decisions as to where you're going to invest your time and resources. Maybe you're building a greenfield project and deciding where to start. Maybe you're 12 months into a project that isn't going as planned. Maybe your company or organization has pivoted it's business objectives and you're being asked to pivot with it.  Whatever the case, I've too often seen these decisions made without fully understanding what you're committing to and without having a clear plan to measure whether where you're investing your time and resources is actually being successful.

Measuring success isn't simply a matter of checking a box that something has been delivered. There's much more too it than that. You need to make sure you're delivering the right thing. You need to make sure that you're able to fail quickly if/when you discover that you're not on the right track. You need to have a clear definition of what's enough.

Here are five questions I like to ask before I invest in any project that allow me to have a clear understanding of what success looks like.

Do you have well defined user scenarios?

Well defined user scenarios allow you to know if you're building the correct thing or not. A well defined user scenario should clearly articulate what problem you're trying to solve, how you plan on solving it, and it should tease out some of the unknowns or external dependencies.

A great place to start with a user scenario is with a story board. Storyboards allow you to think about your product as it is used in the real world. Too often we start our user scenarios with an interface or UX design. These designs are great but they don't help us tease out the context surrounding our problem. For example, where users are when the problem occurs. Or what type of constraints the environment adds to the mix, like only having one hand available.

Can you show that you're providing business value now?

Does your new investment require that you wait until the product is complete before you can judge it's fitness? This should be a red flag that the structure and milestones of your project are not lined up correctly.

Agile using scrum and Kanban are two great ways to help you answer this question. Agile allows you to break things into smaller more iterable pieces. It gives you a way to measure your velocity as you go to help you adjust your milestones and scope. Kanban allows you to understand what work in progress is going on. It also allows you to understand and account for the unexpected work that inevitably comes down the pipe. Kanban allows you to make more informed resource decisions based on current resource allocations and throughput.

Is what you're investing in structured in a way as to allow you to change scope?

Do you have a way to show incremental progress? Do you have a way to see your work in action in the real world? If not, you should. Having a way to check-in and get a real world feel for how what your building works in the real world is crucial.

One way to do this is by setting up regular demo days. In agile, these tend to fall at the end of a sprint. It gives you a way to quantify what it is you're delivering and a framework from which trade offs can be discussed. I've seen pretty successful scrum teams decide what they're demo'ing at the next demo day BEFORE deciding what stories are in their sprint.

Having demo days allows you to break up larger work into smaller more consumable pieces. It also allows you to identify dependencies earlier in the process. Maybe your demo requires some work from another team? Maybe it requires some additional hardware or resources. Maybe it requires a deployment. Whatever the case, you're able to start asking the correct questions and unblocking your team before any work actually begins.

Are you able to measure success incrementally?

Demo days are one way to measure success incrementally but not the only way. Alpha and beta releases are another way. Getting something in-front of real world users (even just internally) will open your work up to a higher level of scrutiny which will inevitably make your product better. It will also force you to ask yourself the correct questions on how you measure success for individual features. For example, if you're adding a button to make peoples lives easier, how do you know if they're using it? How do you know if it was discoverable? How do you know if it actually solved the problem? Knowing the answers to this class of questions is paramount to the success of your project. Asking that class of questions helps inform the approach you take to solving your problems.

Do you understand the unknowns?

As you start asking the other questions it will become clear that there are always unknowns in your project that need to be fleshed out. It may be understanding the cost associated with the project and how it's going to be funded. It may be intra-team, inter-team, external, or 3rd party dependencies that are not apparent at the inception of the project. It may be that the level of effort is greater than the rewards for building any particular piece of your project.

Whatever the unknown, it should be your goal to root it out. You're process should make finding the unknowns easier than allowing them to crop up at just the wrong time.