Friday, April 29, 2011

Book on Android UI development

Packt Publishing has just released this book about Android User Interface Development from Jason Morris and I got a mail from them whether I could write something about the book in this blog. Why not, responded I and there came an e-book. Of course, I had a hidden agenda too. I am aesthetically challenged, so to say and therefore UI design is not really my strength. I intended to learn from a master.


First and foremost, as the subtitle says, this book is for beginners and the author takes this mission seriously. The book follows a rigorous, step-by-step methodology. The reader is expected to start from an empty project and extend it line by line while the book explains, what is going on. It is best to demonstrate with an excerpt:

  1. Start by creating an empty Button element below our answers ViewGroup LinearLayout (but still within the root LinearLayout element). Assign itthe ID skip, so that we can reference it in Java:
    android:id="@+id/skip"
  2. Create some padding between the answers and the new button by using a margin:
    android:layout_marginTop="12sp"
  3. Give it the display label Skip Question:
    android:text="Skip Question"
  4. Like all of the previous widgets, the width should be fill_parent and the height should be wrap_content: android:layout_width="fill_parent" android:layout_height="wrap_content"
And so on, and so on. There are books and trainings that call themselves hands-on but this book works really the best if you type the code while you are reading. Using this style, the book gets surprisingly far, starting from the simplest Hello, World type of applications it discusses lists and their relations to data, widgets (even the most exotic ones), layouts, including custom layouts and finally animations. Meanwhile it never gives up its practical approach, e.g. it teaches you, how to create images with transparent backgrounds with Gimp. I really appreciated that bit because I know from my own experience that this detail can be hard to figure out.

The author made some decisions that I found a bit strange. First and foremost, even though the title is Android User Interface Development, it does not discuss 2D (Canvas and co.) and 3D (OpenGL) at all. I am sure that some readers will miss these parts. I am big fan of command-line development environments but maybe Eclipse is better for beginners.

All in all, I enjoyed this book and I even learnt from it. The book does not cover all the aspects of Android development but it addresses a very important application type. It is a good place to start with Android development.

Tuesday, February 22, 2011

3-level expandable lists

I got a question in a comment whether 3-level expandable list would be possible. I responded that the ExpandableListView implementation is wired to have two levels but then another comment mentioned that it may be possible by embedding one ExpandableListView into another. I was busy then but now I found time to take the challenge and to implement a test program.

Click here to download the example program.

Our three-level list is a variation of an earlier example program but I inserted another level (somewhat without reason :-)).


The principle of the implementation is simple. The first-level list is backed by an ExpandableListAdapter. The child views generated by this adapter are ExpandableListAdapters themselves that provide the second-level groups and the eventual child elements. Listeners are set for the group events (expand/collapse) of the second-level lists. When a group event occurs on a second-level list, the layout of the first-level list is also recalculated to accomodate for the size changes of second-level expandable lists.

This sounds simple but there are some tricky points that made me spent more time on this prototype than I expected. The first issue is with the individual rows of the second-level lists and the view recycler. Check out getChildView() in ColorExpListAdapter.java. When the layout of the list is recalculated, the old views are offered to the list adapter for reuse. The getChildView() method gets the old view instance in the convertView parameter. If convertView is not null, the adapter has the option of reusing the old view instance. In this case the adapter does not instantiate a new view but sets the old view instance appropriately, decreasing garbage (Click here if you want to read more about the importance of less garbage collection in mobile environments). Now our problem is that we cannot just set up an ExpandableListView instance without destroying its internal state, i.e. the expanded/collapsed state of the groups. For this reason, it is extremely important to preserve the views and to prevent the first-level ExpandableListView of rearranging the second-level lists (handing out a second-level ExpandableListView in a certain position as a convertView at a different position). For this reason I implemented a cache in ColorExpListAdapter that makes sure that only one second-level ExpandableListView is generated for each child position in the first-level list and that second-level ExpandableListView instance is consistently returned for the same position. This guarantees that the collapsed/expanded state of the second-level views are preserved when the layout of the first-level list is recalculated.

The other tricky issue is the size of the second-level lists. The layout recalculation of the first-level list is triggered by a group event of a second-level list. Due to the way expandable lists are implemented, when this event occurs, the items of the second-level list which was clicked are not yet added/removed according to the expand/collapse action. This means that the size of the list will be incorrect when the first-level list layout is recalculated.

To solve this problem, observe the row count calculation in ColorExpListAdapter's calculateRowCount method and the way the row count is used in DebugExpandableListView's onMeasure method. DebugExpandableListView originally started to exist so that I can observe the layout of the second-level lists then it turned out that it has an important function: override the onMeasure method of the original ExpandableListView (actually inherited from ListView). Lazily, I kept the class name and the debug messages in it so that you can observe the rather complex operation of the layout process if you feel like.

Wednesday, January 26, 2011

Plugins with user interface

A while ago I wrote about plugins and now I received a mail from somebody asking about plugins that extend the UI of an application. From one side, these "plugins" are the cornerstone of Android application design: activity invocation can be considered a "UI plugin mechanism". But what if somebody wants to plug in UI elements into an application's screen so that the UI elements provided by the plugin appear on the application's own screen? Limited screen estate makes these sorts of plugins less relevant than in case of PC applications but there is a precedent: widgets plug into the Launcher's screen this way. I decided therefore to play with the idea and created a prototype.

Click here to download the application.

You will find 3 subdirectories in the package (respluginapp, resplugin1, resplugin2), each is a separate Android project. Compile and install respluginapp and launch the application. You will see something like this:
Now compile and install resplugin1 and resplugin2. You will notice that the screen of respluginapp dynamically changes as you install the plugin packages. Eventually it will look like this:



The prototype idea is simple. We have 4 rows on the screen. Each row has a default content (some text) but if a plugin is installed, the default row content is replaced by the UI elements provided by the plugin.

Sounds simple but it isn't.
  • Getting the layout from the newly installed plugin package and recognizing the plugin package is the easy part. The plugin package is recognized because it exposes a service with aexp.intent.action.PICK_RESPLUGIN intent action. That service will be used to handle plugin events. There is already a hack here: the name of a layout is available only to the application in the package because the resource compiler generates R.java that maps layout names to layout IDs. As the plugin host application cannot access this R.java, it has no access to layout names. Our plugin host application accesses the plugin's row layout by ID (0x7f030000 is hardcoded). This ID is allocated to the layout whose name is the first alphabetically among the layouts. As our plugins have only one layout in the package, it is not really an issue but this hack can cause quite a nightmare in larger projects.
  • The next hack happens with widget IDs. The resource compiler assigns IDs sequentially. Unfortunately it uses the same ID base for the plugin host application and the plugins so chances are that the IDs in the plugin host application and in the layouts of the plugins overlap. The plugin host application solves it by adding a row-specific offset to the IDs of the UI elements when they are loaded from the layout provided by the plugin package. But the downside is that from this moment, IDs used by the plugin host application and IDs used by the plugin will be different and the plugin host application has to constantly convert back and forth when it sends events to the plugin event handler service.
  • While the UI elements reside in the plugin host application's screen, the event handling is provided by a service exposed by the plugin. The event handling looks like the following: the event is intercepted in the plugin host application, the state of the plugin UI elements is serialized, the plugin event handler service is invoked that processes the event using the serialized plugin UI element state and spits out actions, how the plugin UI elements should be updated. Only some basic functionality is implemented in this prototype. Only button click events are intercepted and the state capture/update only works for TextViews and descendants. But you get the idea how complex it would be to do it properly. While fiddling with this part, I ran into a dead end street because I thought RemoteViews could be used to implement the serialization/deserialization. RemoteViews, however, does not support EditText so this approach had to be abandoned.
Is this something you should do? Well, I am not sure. Pulling UI events between host applications and services are not trivial and also it can be slow because by default the plugins are in a different package therefore in a different Linux process (see further explanation here). Our button click handlers probably do not consume so much CPU time but it can be worse with complex and frequent events. Also, the serialization mechanism presented here is kind of naive, check out the RemoteViews source to check, how deep you can go.

On the other hand, applications that change their screen layout when you install plugins are kind of cool as the Android widgets demonstrate. Decide yourself.

Update: this issue caused compilation errors in the projects. As raw Map is not supported anymore on the AIDL interface, I decided to follow the advice and replaced Maps with Bundles. I also converted the projects into Eclipse projects.

Click here to download the updated code.

Friday, December 24, 2010

JSON-RPC with a client-side library

A while ago I posted about my experiences with JSON-RPC. The conclusions were both positive and negative - JSON-RPC is a very promising protocol. Unfortunately I found that it is not so easy to find good client- and server-side libraries that support it. In particular, the JSON-RPC protocol handler in Android client needed to be implemented from scratch, without any library support.

Then one commenter proposed to check out this JSON-RPC library. Many things happened since but eventually I was able to get some first-hand experience with this library.

Click here to download the example program.

First, deploy the server part (in the server/ directory) as described at this post (actually, the server is same as the previous JSON-RPC example server). Then deploy the client part and you get the same old batch calculator functionality.

Under the hood, however, a lot of things have changed. Instead of fiddling with the JSON tokenizer and the Apache HTTP library, the JSON invocation is just one line.

double d = client.callDouble(
entry.opMethod(),
entry.getV1(),
entry.getV2() );

This simplicity comes with a price, however. The library only supports JSON-RPC 1.0 which means that there are no batch invocations and standardized exception handling. The client actually pumps the elements of the batch operation one by one into the server which could create hairy transaction consistency problems were the application a real data-intensive system. I could have refactored the server side and I could have sent the entire batch in one request (representing each operation as a JSON array sub-list) but then I would have lost JSON-RPC's simplicity on the server side.

The right way is to implement JSON-RPC 2.0 support in the library. Let's see if I or anybody else has some spare cycle in the new year for this.

That's about the valuable content, now the advertisements. First, please note the LinkedIn share button on the right panel. Try it, I am really curious what it does. Second, my very competent ex-colleague started a business of selling vintage powder compacts. Weird idea, isn't it? If you find it as weird as I did, try out the site, maybe you get some gift idea.

Sunday, December 5, 2010

Expandable list and checkboxes revisited

Once upon a time I wrote a nice little post about checkbox focus problems. The guy who originally asked the question was satisfied and went away. While I was looking the other way, a heated discussion started in the comments because the example program had an annoying property: it did not preserve the state of the check boxes when the groups were opened/collapsed but reordered the check marks randomly. Eventually I got a mail whether I could put my example program right.

Click here to download the example program.

So I did. The cause of the trouble was the adapter backing the expandable list view. It was a SimpleExpandableListAdapter which, as its name implies, is simple. In particular, it is not able to feed data to check boxes because they require boolean state while SimpleExpandableListAdapter supports only Strings (here is a posts that explains the relationship between views and adapters). The solution was to write a custom ExpandableListAdapter and the random check mark reordering disappeared.

Saturday, October 30, 2010

From homegrown JSON protocol to JSON-RPC

As I discussed in the previous post, JSON now starts to become fashionable, mostly because of its more compact encoding compared to XML. If we are commited to communicate over JSON with the server, the need for the most common client-server communication pattern, Remote Procedure Call (RPC) arise.

JSON has such a solution, called JSON-RPC. JSON-RPC is a lightweight client-server protocol. Beside the usual stuff (remote method identification, parameter and result encoding, exceptions) it has some remarkable properties that make it particularly suitable for mobile communication.

There are two JSON-RPC specifications out there, JSON-RPC 1.0 and 2.0. While 2.0 in most aspects can be seen as a natural extension of 1.0, there are differences too. JSON-RPC 1.0 was designed to be peer-to-peer with the main transport protocol being TCP. HTTP was also supported but with limitations. JSON-RPC 2.0 is explicitly client-server even though the change is only in the terminology used in the specification. The client-server nature of JSON 2.0 ensures, however, that HTTP as a transport is more naturally supported.

Let's start with the basics.

This is a JSON 1.0 request:

{"method":"add","params":[3,4],"id":0}

"method" attribute specifies the remote method, "params" attribute has a value of an array with the RPC arguments. The order matters and has to be the same as the arguments on the remote side. JSON-RPC requests and responses are connected with the "id" attribute because requests and responses does not have to be ordered. The response following a request - or over a full duplex bearer like TCP arriving at any time - does not have to be the response to the request just sent. It may be a response to an earlier request and the "id" parameter guarantees that requests can be coupled with their responses.

This is a JSON 1.0 response:

{"error": null, "result": 5, "id": 0}

"result" is the return value of the RPC call, "id" is the same as the corresponding request and "error" carries the error object if there was an error during the RPC processing. RPC 1.0 does not define the error value beside stating that it is an object.

JSON-RPC 2.0 extends JSON-RPC 1.0 in many important ways.
  • In order to provide backward compatibility, JSON-RPC 2.0 requests and responses must all have "jsonrpc": "2.0" attribute.
  • Error object is now specified. It has "code", "message" and "data" attributes (more about that in the specification).
  • It specifies oneway messages that JSON-RPC 2.0 calls notifications. Notifications are requests without "id" attribute. No response can be sent to notifications.
  • There are batched requests and responses. Batch is formed by having an array as top-level element and adding JSON-RPC request or response objects tothis array. E.g. the following is a batched request:
[{"method":"add","params":[2,3],"id":0,"jsonrpc":"2.0"},
{"method":"mul","params":[4,5],"id":1,"jsonrpc":"2.0"}]


and the following is a batched response:

[{"error": null, "jsonrpc": "2.0", "result": 5, "id": 0},
{"error": null, "jsonrpc": "2.0", "result": 20, "id": 1}]


The following is a JSON-RPC 2.0 error response:

{"jsonrpc": "2.0","error": {"message": "ServiceRequestNotTranslatable", "code": 1000}, "result": null, "id": 12}

That was a lot of explanations, let's see something that works!

Click here to download the example program.

This is the same client-server batch calculator that you have seen in the previous post. Use the deployment instructions of the previous post to try out the example program. If you want to try the example on a real phone, I recommend deploying the server part on the real Google App Engine infrastructure as described in the previous post.

The example program uses JSON-RPC 2.0 as communication protocol between the client and server instead of our homegrown protocol. The biggest change is on the server side, which is implemented as a Python application for Google App Engine. Instead of obscure parsing code, you see remote methods defined like this:

@ServiceMethod
def add(self, v1,v2):
return v1+v2

and the rest is done by the server-side Python JSON-RPC 2.0 framework which I hacked out of this JSON-RPC 1.0 implementation. The client side does not use any framework at all which is a major problem with this example program. So far I have not been able to find any decent JSON-RPC 2.0 client-side framework suitable for Android. If you have an idea, please comment!

Thursday, October 21, 2010

Client-server communication with JSON

A long time ago, XML was the emerging, fashionable technology. Those times have clearly passed and nowadays XML is just another boring piece of basic technology. If you want to be trendy, you have to go for something else. Luckily, there is a new in thing, JavaScript Object Notation, JSON. In this post, I show you a simple client-server application based on Android, JSON and Google App Engine.

JSON is based on JavaScript object literals and is a textual representation of complex data instances. It has 4 basic types: strings, numbers, booleans and null. Objects and arrays can be constructed out of these 4 types, object is really just the JSON slang for key-value pairs. The entire thing is specified in RFC 4627 so I stick to just some simple examples so that you have that JSON feeling if you don't want to read the RFC.

This is a JSON number: -4.7e12
This is another JSON number: 0
This is NOT a JSON number: 00012 (no leading zeros!)
This is a JSON string: "JSON"
This is another JSON string: "\u005C" (always 4 hexa digits after the \u, just one character, the \)
This is not a JSON string: 'notJSON'
These are JSON literals: true, false, null
This is a JSON array: [1,"hello",null]
This is a JSON array having two other JSON arrays as elements: [[1,"hello"],[2,"hallo"]]
This is a JSON object: {"a":1,"b":"hello"}
This is a JSON object having another object associated with "a" key and an array with "b" key:
{"a":{"aa":1,"bb":2},"b":[3,4]}

I think this is enough for starter because it is evident that JSON has quite powerful data structures. Arrays are equivalent of Lisp lists while objects can be represented as lists of dotted pairs.

How does JSON compare to the incumbent serialization standard, XML? JSON is fashionable in mobile communication because it is somewhat more bandwidth-efficient than XML. E.g. the last example can be written in (ugly) XML like this (XML tag mangling due to blog engine limitations):

[l]
[a]

[aa]1[/aa]

[bb]2[/bb]

[/a]

[b]

[k]3[/k]

[k]4[/k]
[/b]
[/l]

Not counting the white spaces, this is 58 characters while the JSON representation is just 32 characters, about 45% less. The situation is worse if we consider many "well-designed" XMLs out there (like e.g. SOAP documents) which are full of namespace declarations, long tag names, etc. JSON with its simple yet powerful syntax definitely has a space in mobile application design.

Click here to download the example program.

I will demonstrate the power of JSON with a simple client-server example application. This application is a batch-mode calculator. The user first enters operations on the device's UI. These operations are not calculated immediately. When the user selects the "Process list" menu item, the collected operations are sent to the server in one batch. The server calculates the operations that are sent back to the client which displays the results. As you might have guessed, the client and the server communicates with JSON data structures.



The client part needs to be deployed on Android emulator (let's start with the emulator, I will return to this) like usual. The server part, however, requires the Google App Engine SDK for Python. Download the SDK, edit server/ae_jsoncalcserver.sh so that it suits your directory layout then start the shell script (Windows users will have to turn this one-line shell script into a .bat file). If you access localhost:8080 with your browser, you get a short message that GET is not supported. If you get there, you installed the server part properly.

Now you can start the client on the emulator and you can start to play with it. The most interesting part is the process() method in JsonCalc.java. I structured the program so that the generation of JSON request and the parsing of the response is all there. The org.json package has been part of Android from the beginning and this package is really easy to use. For the server part, I used the simplejson JSON parser for Python.

The Android client logs the JSON requests and responses in the Android log (adb logcat). I represented the batch operation and its result as array of objects. Request and response objects have different keys.

Request example:
[{"op":2,"v1":2,"id":1,"v2":3.3},{"op":3,"v1":4,"id":2,"v2":6},{"op":4,"v1":6.6,"id":3,"v2":2.2}]

Response example:
[{"id": 1, "result": -1.2999999999999998}, {"id": 2, "result": 24}, {"id": 3, "result": 2.9999999999999996}]


In case you want to try it out on a real phone, you'd better deploy the server part on Google infrastructure. Then edit Config.java in the client source so that APP_BASE_URI points to your server's address and you are ready to go. When you recompile, make sure that you delete the client/bin/classes directory - chances are that javac will not notice your changes you made to Config.java because of its broken dependency resolution.