Wednesday, November 21, 2012

Hey rook, abstract activities cannot be launched


I'm not a programming master by any means, but this one felt like a rookie mistake. I was hunting down an InstantiationException. Below is a helper class used to launch a new activity simply by taking in an activity's name in the constructor, then launching that activity in an onClick() event.

public class ActivityLauncher implements OnClickListener {

Class<? extends Activity> mClass;
Bundle mBundle;

public ActivityLauncher(Class<? extends Activity> clazz) {
this(clazz, new Bundle());
}

public ActivityLauncher(Class<? extends Activity> clazz, Bundle bundle) {
mClass = clazz;
mBundle = bundle;
}

@Override
public void onClick(View v) {
launchActivity(v.getContext(), mClass, mBundle);
}

}

The launch activity method comes from another helper class. See that one below:

public static void launchActivity(Context context,
Class<? extends Activity> clazz, Bundle extras) {

Intent intent = new Intent(context, clazz);
intent.putExtras(extras);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}

This structure works great to maintain clean code. Whenver you need to set an onClick() listener that simply launches another class, just new up an instance of ActivityLauncher, passing it the name of the class you want to start. If you need to pass some values to the activity being started, pass in a bundle along with the activity class name. Where did I make a rookie mistake? I passed in an activity that was actually an abstract class. That code compiles, but it leads to InstantiationException, a runtime exception.  It's not quite clear because the constructor is invoked in the tangle of Android source code and not in any code that I've written. I, however, should know that one can't instantiate an abstract class, which also means one can't launch an activity that's defined as an abstract class.

Sunday, November 11, 2012

From now on, I'm putting off choosing a cloud service


It can be tedious to make network calls during the development of a cloud-based application. It's possible to make thousands of calls across the wire. There is a way to reduce the impact of the network during development -- wait until later to decide on a cloud-based solution.

One of the things that stuck with me from an Agile conference last spring was Bob Martin's talk about putting off database implementation as long as possible. He described an application on which he and a partner worked where he knew the application would eventually need to store information in a database. For the most part databases are going to be used in the same way – handling reads and writes of data.

I knew that if I wanted to create an application with a cloud-based backend, that choosing which service to use could be a big deal. But I wanted to minimize the impact of the backend on my application as much as possible and I didn't like the idea of calling though to a cloud-based network during application development.

Having experience with both Google'sApp Engine and Amazon's Simple DB, I found both to have good and bad. Before hearing Uncle Bob speak, the first thing I did was consider which cloud-based storage solution to pick. I spent most of my time weighing the pros and cons of each service. Having worked with Amazon Simple DB in the past, including on some proof of concept projects at Northwoods, I knew Amazon was a reliable, and well documented service. But at the beginning of this project, I ended up choosing Google's AppEngine. AppEngine seemed to have the easiest cloud solution to implement. With AppEngine, Google offered an Eclipse plugin that made it simple to simulate a cloud environment. AppEngine also provides a starter project that wires up the network access ahead of time. The project even implements a push notification architecture.

However in experimenting with AppEngine, I found my implementation started to become closely tied with the implementation of the cloud-based backend. I had cloud-based database calls in nearly every activity. These activities also were tied to Googles object-relational model. I started to think that it wasn't in my application's best interest to have proxy objects that can only talk to one kind of service.

The tighter coupling with Google also became more apparent when it decided to bring its Cloud to Device Messaging service out of the beta phase and into full production. There was enough of a difference migrating to the new version of the service that it didn't make sense to further bind the application to Google. There was far too much left to be done on the actual program, and what if by the time it was done, Google's Cloud service changed again?

Turns out what I ended up doing (and should have done in the first place) was put off the real database stuff. The best way I've found to write a cloud application is to create an interface that will serve as my application's face to the cloud. I can simulate interaction with the cloud by making calls through this interface to a class that implements this interface and returns the expected data. Essentially I can implement an in memory database using HashMaps that function as if I were making calls to a reliable and fast network. When the time comes to actually choose a cloud solution, I'll use the adapter pattern and create a class that implements my application's data access interface as well as the interface provided by the cloud solution provider of my choosing. In this adapter class is where I can put the logic to handle network conditions. One of the major changes I'll have to keep track of is creating the UI screens that must be present to handle loading data from the network. In this architecture, those pages can get lost since the “network” calls return asynchronously during development.

This approach has had many merits, especially when it comes to mocking and test-driven development. Although the Google AppEngine solution allowed me to debug with breakpoints on a local server, this approach of defining my own interface has allowed me to create mocks without a concern for what is happening on the other side of the wire. Since the database interface is injected into a base activity, it is available to all other activities that inherit from it. This is useful for mocking and test driving with Robolectric. This architecture also has forced me to keep logic in the application layer while keeping the cloud database with the sole responsibility of storing and retrieving data.  

General Architecture


Saturday, November 3, 2012

A method organization strategy for Android activities


Using the principles of low-coupling and high cohesion, our class files shouldn't balloon out of control to the point where single line method signatures get lost amongst the trees, but it doesn't hurt to have an extra layer of organization on which developers can agree. It's an inevitable fact that we have to read each other's code. The list below reflects how I organize methods in an Android Activity class at the moment. I wouldn't mind making this an exhaustive list so if you got any ideas....speak up.. :).
  1. Constructors – default followed by paramterized (public, then package, then private).
  2. Any method that overides Activity lifecycle events, starting with onCreate(). The rest should follow in the order that they are called on the activity lifecycle stack.
  3. Next come any classes that override an interface. These should all be appended with the @Override annotation.
  4. Include the @Override annotation also on any methods that override an abstract class implementation. First list the methods you are forced to override from the abstract class, followed by the methods that you chose to override.
  5. Next come any methods that are being optionally overriden from any concrete superclass.
  6. Then we list our public methods that are specific to this class. I like to start with the public methods that have more meat to them, followed by the getters and setters.
  7. Package-level implementations are next.
  8. Private methods follow the package-level ones.
  9. After the private methods come the abstract methods. I like the abstract methods down here so that when another programmer is looking at this abstract class and wants to find out which methods this class does not implement, he or she can scroll to the bottom. Those one-line method signatures don't get lost among the methods that have a heavier implementation.


Tuesday, October 23, 2012

Simple test for AlertDialog using Robolectric

EDIT (10/30/2014): All references to the method shadowOf() is actually a reference to Robolectric.shadowOf(). I have Robolectric saved as a favorite in Eclipse, which automatically creates a static import for it. 

---

Anyone who has tried to use the Android testing framework knows it can be painful, mostly because of the slowness of feedback loop and the setup required to run tests. Robolectric eases this pain. If you're writing an Android application and haven't discovered Robolectric, do yourself a favor. First, it makes it possible to run unit tests without needing to connect an Android device or an emulator. Read more about it here

Ever tried to test the appearance of a dialog on screen? The Android testing framework makes it hard. Robolectric makes it easy.

Lets test the apperance of a dialog on screen with the help of a simple sample activity.

This activity, cleverly called ChangePasswordActivity, helps a user change his password. The activity has a field to enter a new password and a second field to confirm the new password entered. Once satisfied the user can click the 'Change Password' button to send the changes to the database.

For those of you that might have trouble visualizing what such an activity might look like, see below.


Below are the field declarations.

public class ChangePasswordActivity extends BaseActivity {

     private EditText newPassword;
     private Button changePasswordButton;
     private EditText confirmNewPassword;

     //some code here

     protected void inflateViewObjects() {
          newPassword = (EditText)
                findViewById(id.change_password_new_et);
          confirmNewPassword = (EditText)
                findViewById(id.change_password_new_confirm_et);
          changePasswordButton = (Button)
                findViewById(id.change_password_btn);
     }
     //some code here
  
}


Next let's look at the code that gets invoked when a user pushes the 'Change Password' button.

void handleSubmitNewPasswordClick() {

     if (meetsPasswordChangeCriteria()) {
          sendPasswordChangeToDatabase();
          showConfirmationToast();
          finishActivity();
     }
}

Before the new password is sent to the database, it must meet certain criteria. For instance, both of the fields should contain text, and that text should match. If any of that criteria is not met, the password is not persisted.  

Let's look at the meetsPasswordChangeCriteria() method.

private boolean meetsPasswordChangeCriteria() {

     boolean criteriaMet = true;

     if (passwordFieldBlank()) {
          criteriaMet = false;
          createPasswordFieldsBlankDialog();
     }
     if (passwordFieldsMatch() && criteriaMet) {
          criteriaMet = false;
          createPasswordsNotMatchingDialog();
     }
     return criteriaMet;
}


Here we see a couple of conditions are tested. If a condition is not met, a dialog is created informing the user of the status of the password change. We'll look at the first condition.

The passwordFieldIsBlank() method helps set the flag that tells the calling method, in this case handleSubmitNewPasswordClick(), whether or not the criteria was met. We'll assume in this case it was not. That means the passwordFieldIsBlank() method returns true.

Lets have a look at that simple method.

private boolean passwordFieldBlank() {
    return newPassword.getText().toString().equals("")
          || confirmNewPassword.getText().toString().equals("");
}

Pretty basic. Check to see if the two fields have any text. If one does not, then this method returns true, which, in the previous method, meetsPasswordChangeCriteria(), causes the criteriaMet flag to be set to false before creating a dialog that prompts the user that there is some missing information. The screen then looks like the one below.





So finally we get to the reason for this post. How do we create a unit test that checks to see if this dialog appears? 

If you're reading this test case, you're probably new to Robolectric. Well, that's fine, I am too. But the following method is an example of a typical test method in Robolectric and hopefully you're past the point of setting up your Robolectric test project.

What we're going to do with this test is create an activity, ensure the newPassword and confirmPassword fields are blank and then we're going to programatically push the 'Change Password' button. Then its up to Robolectric – and its beautiful concept of shadow classes. Let's look at the code and then I'll talk about it.


public void itShowsDialogIfAFieldIsBlank() {
     setUpActivity();
     setEditText(R.id.update_email_edittext, "");
     setEditText(R.id.update_username_edittext, "");
     pushUpdateButton();

     AlertDialog alert =
           ShadowAlertDialog.getLatestAlertDialog();
     ShadowAlertDialog sAlert = shadowOf(alert);
     assertThat(sAlert.getTitle().toString(),
     equalTo(activity.getString(R.string.all_fields_required_)));
}


The setUpActivity() method encapsulates the setup needed for instantiating an activity. Right now, think of it as a black box. Just know that after that method is called, we have a valid instance of the ChangePasswordActivity class.

The method setEditText() passes the id of the EditText field we wish to set the text of and then a string value of the text we wish to be set. In this case it's blank. The pushUpdateButton() encapsulates pushing the 'Confirm Password' button. That is just a matter of calling findViewById() to locate the button element and then calling performClick on it.

Once that's done we know that our activity should display a dialog like the one in the last photo. But how do know. We use the static method getLatestAlertDialog() of the ShadowAlertDialog class which looks at the current activity and finds the id of the last dialog that was show. The activity class holds on to this information.

Once we have that AlertDialog, we need to make some assertions about it. Well it would be helpful to know things like the title that is shown in the dialog. Normally the AlertDialog class doesn't expose a property that would allow us to examine the title of the dialog, but if we look at the shadow implementation that Robolectric created, we can get access to properties that might be useful to test. In this case, we're asserting that last dialog has a title that says “All fields required”. Actually here that string value is held in the string resource file. That way if it ever needs changed, we only need to change it in one place.

In this assert method we're using the equalTo() method that comes part of the hamcrest core matchers library. You'll have to import org.hamcrest.CoreMatchers. I'm doing a static import. I could have just as easily used assertEquals(string1, string2), but I find that the CoreMatchers library offers some advantages in readability. We can dive into that another time.

The purpose here was to hopefully help someone test something that might have once seemed untestable.