Posted on 19 Nov 2013
This post was originally published on the
SmartLogic Blog.
Writing a medium to large Android app requires having code structure. In creating our latest Android development project, I came across a structure that has helped me out.</p>
Java Code
I like to have separate files for each class in my Android projects, the only exception being AsyncTasks. Having this many java files means you have to have more packages than the base package. I ended up with a package for each type of main class. Each class is named ending with its type.
- com.example
- activities
- Contains all the activities. Classes are all named with Activity at the end. That way, you can immediately know what it is when reading Java code that doesn’t have its full package name.
- adapters
- Contains all the adapters
- authenticator
- Contains any class related to signing a user in. I create a local account and having all related classes together is very handy.
- data
- Contains all classes related to data management such as ContentProvider and SQLiteHelper.</p>
- data.migrations
- Contains all of my SQLite migrations. I created a class for migrations, read about it here, and put them all in this package.
- fragments
- helpers
- Contains helper classes. A helper class is a place to put code that is used in more than one place. I have a DateHelper for instance. Most of the methods are static.
- interfaces
- models
- Contains all local models. When syncing from an HTTP API I parse the JSON into these Java objects using Jackson. I also pull Cursor rows into these models as well.
- preferences
- Contains all classes for custom preferences. When creating the preferences I required a custom PreferenceDialog as well as a custom PreferenceCategory. They live here.
- sync
- Contains all classes related to syncing. I use a SyncAdapter to pull data from an HTTP API. In addition to the SyncAdapter a SyncService is required, so I created a package.
Layouts
The layouts folder can easily become disorganized since you can’t have any folders in it. This is a known issue and has been ignored for 4 years now. To get around this I name my layout files with a prefix depending on what they are for. This sorts them together in the Eclipse file listing.
- R.layout
- activity_
- adapter_
- fragment_
IDs
All of my IDs are snake_case. I had a project that was inconsistent with how IDs were named and it was a pain. Many projects seem to do mixedCase notation for IDs, but it seemed weird having the different styles in Java code depending on what type of resource it was, e.g. R.layout.snake_case vs R.id.mixedCase.
Values
For values I have a separate file for each type of resource, e.g. dimens.xml. This works out well for most of the values, except for strings. There are a large amount of strings in this app and having them all in the same file is pretty unusable. In this case I split out some of the strings into groups by their activity. This allows you to easily find a string if you’re looking at the activity already.
How do you structure your code for Android development projects? Comment and let me know.
For more on Android development, plus Rails and iOS, follow @SmartLogic on Twitter.
Image Source
Posted on 27 Jul 2013
Recently I’ve been working on an Android app that uses a hypermedia API. This has presented some challenges because the JSON library I’m using turns JSON into java objects. Using a HashMap is out because you get into generic hell land.
Up until now I haven’t had to make a hypermedia client in anything but ruby. In ruby it’s very simple because the JSON you receive from the server gets turned into a hash. I think I have come across a nice way to get around it in java though.
Below are my java objects that Jackson parses into.
Link
public class Link {
@JsonProperty("href")
public String href;
}
HalRoot
public class HalRoot {
@JsonProperty("_links")
protected RootLinks links;
public String getSelfLink() {
return links.self.href;
}
public String getOrdersLink() {
return links.orders.href;
}
public class RootLinks {
@JsonProperty("self")
Link self;
@JsonProperty("http://example.com/rels/orders")
Link orders;
}
}
HalOrders
public class HalOrders {
@JsonProperty("_embedded")
protected Embedded embedded;
@JsonProperty("_links")
protected Links links;
public List<HalOrder> orders() {
return embedded.orders;
}
public class Embedded {
@JsonProperty("orders")
List<HalOrder> orders;
}
public class Links {
@JsonProperty("self")
Link self;
}
}
HalOrder
public class HalOrder {
@JsonProperty("_embedded")
protected Embedded embedded;
@JsonProperty("_links")
protected Links links;
public List<HalItem> getItems() {
return embedded.items;
}
public String getSelfLink() {
return links.self.href;
}
public String getItemsLink() {
return links.items.href;
}
public class Embedded {
@JsonProperty("items")
List<HalItem> items;
}
public class Links {
@JsonProperty("self")
Link self;
@JsonProperty("http://nerdwordapp.com/rels/items")
Link items;
}
}
HalItem
public class HalItem {
@JsonProperty("name")
public String name;
}
Posted on 02 May 2013
A recent API Craft Google group post has gotten me playing around with hypermedia APIs again after having an extended stay in Android land. I updated my hypermedia APIs repo, specifically the hypermedia.rb script.
I made it look similar to the nerdword-api script/client.rb which boots a server in the script instead of relying on an external server beeing booted. This was taken from how Cucumber boots capybara.
Below is the code required:
#!/usr/bin/env ruby
require File.expand_path('../config/environment', __FILE__)
require 'capybara/server'
Capybara.server do |app, port|
require 'rack/handler/thin'
Thin::Logging.silent = true
Rack::Handler::Thin.run(app, :Port => port)
end
server = Capybara::Server.new(Rails.application, 8888)
server.boot
Posted on 28 Apr 2013
When using a Cursor in Android I prefer to pull out a row into a Java object. The Java objects will mostly be getters and setters, but will also include convenience methods as well. In order to make going from a cursor row into a model instance I create a static method on each model class called fromCursor
.
This method will pull out columns into the appropriate member variable. It will only pull out a column if it is in the selected columns.
public class MyModel {
public static MyModel fromCursor(Cursor cursor) {
MyModel myModel = new MyModel();
if (cursor.getColumnIndex("_id") != -1) {
myModel.setId(cursor.getInt(cursor.getColumnIndex("_id")));
}
if (cursor.getColumnIndex("description") != -1) {
myModel.setDescription(cursor.getString(cursor.getColumnIndex("description")));
}
/* ... */
return myModel;
}
}
Posted on 05 Apr 2013
The LoaderManager is a nice way of handling Cursors in an Android activity or fragment. It was a little tricky to get started with so I wanted to have an example I could come back to as a reference of how to use it.
public class FirmInfoActivity extends Activity implements
LoaderManager.LoaderCallbacks<Cursor> {
private static final int LOADER_ONE = 0;
private static final int LOADER_TWO = 1;
private AdapterOne mAdapterOne;
private AdapterTwo mAdapterTwo;
@Override
protected void onCreate(Bundle savedInstanceState) {
// This is incredibly important to do, I forgot it a few times
// and had no idea why it wasn't working.
getLoaderManager().initLoader(LOADER_ONE, null, this);
getLoaderManager().initLoader(LOADER_TWO, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch(id) {
case LOADER_ONE:
return new CursorLoader(this, Uri.parse(""),
null, null, null, null);
case LOADER_TWO:
return new CursorLoader(this, Uri.parse(""),
null, null, null, null);
}
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
switch(loader.getId()) {
case LOADER_ONE:
// Update based on the loaded cursor;
break;
case LOADER_TWO:
// Update based on the loaded cursor;
break;
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
switch(loader.getId()) {
case LOADER_ONE:
mAdapterOne.swapCursor(null);
break;
case LOADER_TWO:
mAdapterTwo.swapCursor(null);
break:
}
}
}