Wednesday, January 20, 2010

JARs on the classpath

Directory tree of a typical Android project (at least those created by the "android create project" command) looks like this:

As you may have seen the directory structure of countless J2SE projects, there is a directory to store the 3rd party class libraries of the project. Under the "lib" directory, you can place your Java class libraries in JAR format and they will be added to the classpath when the Android application is running.

Or will they? If you remember the APK format, there is no such thing as directory for libs in JAR format, particularly because the Dalvik VM is simply not able to execute ordinary Java class files in those JARs. You can find JARs on the device but these JARs contain class files in DEX format (Dalvik VM's class file format) inside so the extension of these files is misleading. Then where are those JARs from the lib directory that we "added to the classpath"?

You might have guessed it: these JARs are unpacked, processed by the dx tool just like the classes under the src directory and are placed into the same classes.dex file where the application resides. One application is one DEX file and JARs containing Java class files are not "added to the classpath", they are converted into DEX format and added to the DEX file of the application. Unlike in OSGi, an Android installable package cannot share code with other system components, only services.

Saturday, January 16, 2010

Oneway interfaces

In early betas, the Android IPC was strictly synchronous. This means that service invocations had to wait for the return value of the remote method to arrive back to the caller. This is generally an advantage because the caller can be sure that the called service received the invocation by the time the remote method returns. In some cases, however, this causes the caller to wait unnecessarily. If synchronicity is not required and the method has no return value, oneway AIDL interfaces may be used.

Oneway methods are specified by addind the oneway keyword to the AIDL interface definition.

oneway interface IncreaseCounter { void increaseCounter( int diff ); }

Only the entire interface can be oneway and these methods must all have void return value. The stub compiled from oneway AIDL interface does not have return path for remote methods on the service side and does not wait for the method to execute on the client side. The delivery is reliable (no invocations are lost) but the timing is not guaranteed, e.g. two sequential oneway invocations may arrive at the invoked service in different order. Oneway interfaces are therefore more complicated to use (they are also faster and don't block the caller) and are used extensively by the Android framework internally to deliver event notifications.

Click here to download the example program.

Our primitive example has an Activity and a Service. The service exposes two interfaces: one "normal" (Counter.aidl) and one oneway (increaseCounter.aidl). The interesting bit here is how one service can expose two interfaces. The onBind method checks the Intent used to bind the service and returns different binders based on the Intent. The important point here not to use the Intent extra Bundle to differentiate among interfaces. I did this and I can confirm that even though extras arrive at onBind (the API documentation states the contrary) but the framework gets completely confused and thinks that the service has already been bound with the same Intent (the framework seemingly cannot figure out that the Intent extras were different). In the example program I abused the category field therefore and this works nicely.

Sunday, January 10, 2010

RemoteViews as hypertext document

In the previous blog entry I tried to demonstrate, how a web programming construct, the feed can be implemented using LiveFolders. In this entry, I would like to highlight another relatively obscure feature of the Android API, the RemoteViews widget and argue that RemoteViews are similar in nature to another web programming construct, the hypertext document itself.

RemoteViews is the key mechanism behind the AppWidget feature of the Android platform. There are many good introductions to AppWidgets (like this or this) so I will be short. AppWidget is a representation of an Android application on the home screen. The application can decide, what representative information it publishes on the home screen. As the screen estate is limited, the representation is obviously condensed, e.g. an application executing a long-running task can publish the progress indicator as home screen widget. The technical problem is that it is not evident, how to embed functionality (even condensed functionality) from another running application into the Launcher, the default home screen provider. If Launcher delegated the refreshing/redrawing of the views embedded from another application to that application, then malfunction of that application could disrupt the Launcher itself - something to be definitely avoided as the home screen is the last refuge of the users if something goes wrong with apps. Different platforms apply similar strategy. The idea is that the home screen application does not invoke other executables when it draws the home screen, it rather uses some sort of description of the widget UI in data format and displays that. For example that is the reason Symbian forces the programmer to implement widgets as HTML/JavaScript applications and that is the reason Android decoupled the applications from the Launcher view structure

The workhorse behind Android AppWidgets is the RemoteViews widget. RemoteViews is a parcelable data structure (so it can be sent through Android IPC) that captures an entire view structure. The creator of the RemoteViews sets up this data structure, by specifying the base layout and setting field values. This has no effect on the UI and therefore background components like services, broadcast event handlers, etc. can also do it. In fact, AppWidgets are broadcast event receivers, they process broadcasts from Launcher and respond with RemoteViews. When the RemoteViews data structure is received and deserialized, it can be applied to any ViewGroup of the UI and can therefore appear on the UI of another application. From this point of view, it works just like a hypertext document except that RemoteViews describes Android views and not general web controls.

RemoteViews is a powerful construct that can be easily used outside of the context of AppWidgets. I present now a simple Activity and a Service where the Service provides an entire formatted View (and not just the data) that can be displayed easily by the Activity. Even though this is against the Big Book of service-oriented architectures (services should provide raw data and the consumers should care about formatting), there are lot of use cases for this construct. For example the output data of the service may not be so easy to display (e.g. in case of a map application) or you would like to tie the output of the service with advertisement, logo or link to your main application (a PendingIntent in Android parlance). In this case your Service (broadcast receiver, etc.) may just return the View structure you would like the client application to display. In the web world, web widgets are formatted mini webpages to be embedded into other web pages, hence the similarity of this web technology with RemoteViews.

Click here to download the example program.

There are three points to note here.
  • Check ITimeViewService.aidl under the src/aexp/remoteview directory and see how the remote method returns a RemoteViews instance.
  • Check and see how the RemoteView is constructed and updated. To make the point of returning formatted views as opposed to raw data, I added two formatted TextViews and a ProgressBar embedded into two LinearLayouts to the view structure returned by the service.
  • Check and see how the RemoteView is applied to the view structure of that Activity. Everything under the "Get view data" button comes from the service when the RemoteViews is inflated into the view structure of the Activity.

RemoteViews has disadvantages, however. It cannot serialize any Views, e.g. it cannot serialize an EditText view. I am aware of only this article about the restrictions of views the RemoteViews can serialize/deserialize.