Parcelgen: A Code Generator for Android Data Objects
-
Alex P. Mobile Team
- Apr 19, 2011
When I switched from working on Yelp’s iPhone app to our Android app, one of the first things I encountered was the radical difference between the equivalent classes to handle what I normally consider a “screen” or “page” of an app. On Android, an Activity handles what on iOS you use a UIViewController for, but they work in fundamentally different ways. One of the biggest differences is that on Android you can’t just instantiate a new Activity
and display it like you can with a UIViewController
. Instead you create an Intent and tell the system to start an Activity based on that Intent.
This works fine for simple Activities, but things get complicated when you have a complex data object which you want to display in a new Activity. Android doesn’t let you pass arbitrary objects between Activities. As this Android google group discussion points out, data passed to Activities must be placed in globally-accessible state, stored on the device’s flash memory, or passed inside an Intent
. A static singleton works for a few objects, but it requires having a place to store all the objects being passed and is generally considered poor design. If you use a static collection to pass multiple objects, the receiver must make sure to remove the objects lest they leak memory. Writing an object to flash is expensive, slow, and requires having a reliable way to write and read an object (such as sqlite or Serializable
). Intent
seems like it should be great, except all objects written to an Intent
must be of a limited set of Java types and classes, or know how to read and write themselves to a Parcel by implementing the Parcelable protocol.
After trying a number of different techniques to pass objects between Activities, we began making the basic objects in our application conform to the Parcelable
protocol. This greatly simplified the process of launching new Activities, and allowed our application to save all its objects when suspended via onSaveInstanceState()
so recovering from being stopped by the Android system was much easier to handle. As our application grew and we added more and more data objects, I tired of writing similar Parcel- and JSON-related code for each class.
Enter Parcelgen
Inspired by Android’s existing code generation in aapt, instead of manually writing classes that implement Parcelable
, I wrote a Parcelable code generator in Python. All it needs to know is the class name, instance variables and types, and a little metadata about each object to create.
I realized that I needed the exact same information to read an object from a Parcel
that I needed to read an object from a JSONObject
, so I enhanced my script to generate code to do that too.
How it Works
For each object to generate, write a small json description of the object’s members and their types:
{
“do_json”: true,
“package”: “com.yelp.parcelgen”,
“props”: {
“String”: [
“id”, “name”, “imageUrl”, “url”, “mobileUrl”,
“phone”, “displayPhone”, “ratingImageUrl”,
“ratingImageUrlSmall”, “snippetText”, “snippetImageUrl”
],
“int”: [“reviewCount”],
“double”: [“distance”],
“Location”: [“location”]
},
“json_map”: {
“ratingImageUrl”: “rating_img_url”,
“ratingImageUrlSmall”: “rating_img_url_small”
}
}
This is the parcelable description for (a subset of) a Business returned by the Yelp API as used in a sample app I wrote for parcelgen.
Then execute the parcelgen python script to generate the java code for the object:
$ python ~/parcelgen/parcelgen.py parcelables/Business.json src/
This creates two files: _Business.java and Business.java. _Business
contains the parcel and json reading/writing logic, and Business
contains a CREATOR static variable as required by Parcel. Business
doesn’t have any dependencies on the object’s properties. If the json description changes and you re-run parcelgen _Business.java
will be overwritten, but not Business.java
. This lets you add data and application logic to Business
without losing the flexibility to modify the parcel description later (any members added to Business
won’t automatically get saved to a Parcel).
Creating and Passing Parcelgen(erated) Objects
Want to pass an object to a new Activity in an Intent? Just use Intent.putExtra()
( BusinessesActivity.java):
intent.putExtra(“business”, mBusiness);
Then, in your Activity’s onCreate()
:
Business business = getIntent().getParcelableExtra(“business”);
Want to create a list of objects from a JSON array of dictionaries? Check out how the sample app does it:
JSONObject response = new JSONObject(result);
List
response.getJSONArray(“businesses”), Business.CREATOR);
Using parcelgen saves a lot of repetitive code and developer time in applications that are based around a web API. In Yelp for Android we use parcelgen to handle businesses, users, reviews, and other basic data objects. Whenever you tap a business from the search results list, it’s passed to the Activity to display the business through a Parcel with the help of parcelgen.
Read Up
Detailed instructions on how to use parcelgen in your project and some more advanced features are outlined in parcelgen’s readme on github.
There is a working sample Android application on github here you can use as a reference for how to use parcelgen.
Fork me on github
Parcelgen is open source under the Apache 2.0 license and available for anyone’s use and modification on github. Please feel free to submit patches, feature requests, and such on github.
If you use parcelgen in a project, please drop us a line. We’d love to know about other people using it! You can contact me directly through Yelp or through github.