Sunday 5 September 2010

how to think about RemoteViews

The RemoteViews mechanism in android is used in two crucial places which anyone writing apps is going to have deal with sooner or later: home screen widgets and status-bar notifications. But the documentation for it lacks a high-level overview and so it is quite hard to understand. Here is that high-level overview.

First, a RemoteViews object is not, whatever you might think from its name, a View. It is similar but don't fall into the trap of thinking it is a subclass; it is more like an alternative.

The RemoteViews mechanism is a way of letting one process pass a chunk of screen to another process to be displayed as part of the second process's layout. You create a RemoteViews object by specifying the layout xml file you're going to use for your chunk of screen and then calling a series of methods to populate the various bits of the layout.

These methods are exactly analogous to the ones you could call on a View object if you'd just used that layout xml in your own process, but there's no underlying View for them to be called on so what's really happening is they're getting stored up to be called later at display-time. When you've finished setting things up how you want them you pass the RemoteViews object to the other process, the one that's going to be doing the displaying.

In the common case you don't do this directly: when your RemoteViews is going into a notification you set it as a field of the Notification object and then when you pass that Notification to the NotificationManager it does the IPC for you, and when your RemoteViews is going into a home screen widget you pass it to AppWidgetManager.updateAppWidget and that does the IPC. When the second process receives your RemoteViews object, with it having been appropriately serialized and deserialized in a way you don't have to worry about, it calls the apply() method on it and then it gets displayed: at this point there actually is a View object and all the things that you did earlier get applied to it.

You can never yourself get to this View object: it's in someone else's process, after all. If at some point you need to change what's in the View you have to fiddle the RemoteViews object and then pass it again to the process doing the displaying, and it has to go through the applying process again.

A note on calling the setFoo methods: these methods don't map one-to-one onto the familiar methods you would use to set attributes of a View. So for example if your layout xml file contains a TextView element which you've given an id using the android:id attribute in your xml - say android:id="@+id/title" - then you don't have a TextView to call setText on but rather you must call setCharSequence on your RemoteViews object, specifying as your first argument the id of the TextView element (in this case R.id.title) and as your second the name of the method you really want to call, in this case "setText".  The third argument will then be the actual text you want to put into the TextView. This means that you can only get to methods of the eventual View which take a single argument, and only if that argument is of one of the types which RemoteViews supports. It also means that in effect your method type checking is put off until run time: if it turns out that there is no method setText(CharSequence) on a TextView, you get an exception when the other process tries to show your view and that's the first you know about it. So you have to be careful.  Observe also that you have to be more precise with thinking about types: there is no method setText(String) on a TextView but normally you can act as if there is because a String is a CharSequence so you will just get setText(CharSequence) instead. But if you try to do the same thing with RemoteViews, by calling setString instead of setCharSequence, then it won't work and you won't find out until you get the exception at run time.

No comments:

Post a Comment