Monthly Archives: November 2010

The world of ListView

The notes taken out of the Google I/O talk “The world of ListView“.

Due to performance and memory considerations, the ListView views hierarchy is fixed, nothing that is not displayed on screen is not part of the hierarchy. The item views are recycled.

Terminology

  • index (refers to the child view),
  • position (refers to the Data in Adapter),
  • id (refers to the unique identifier for data).

Stable IDs:

  • if hasStableIds() == true, the ListView can do a number of tricks to presentation and handling of the items.
  • An ID always refers to the same value.
  • Helps ListView.

Adapters

  • getView(int position, View convertView, ViewGroup parent)
  • full data presentation control
  • Potential optimisation through the re-use of convertView

getView()

the slow way

public View getView(int position, View convertView, ViewGroup parent) {
  View item = mInflater.inflate(R.layout.blabla, null);
  ((TextView) item.findViewById(R.id.text)).setText(DATA[position]);
  ((ImageView) item.findViewById(R.id.icon)).setImageBitmap(
    (position &1) == 1 ? mIcon1 : mIcon2);

  return item;
}

the correct way

public View getView(int position, View convertView, ViewGroup parent) {
  if (convertView == null) {
    convertView = mInflater.inflate(R.layout.blabla, parent, false);
  }
  ((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]);
  ((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap(
    (position &1) == 1 ? mIcon1 : mIcon2);

  return convertView;
}

the Fast Way

create a special datastructure in your app, eg.

static class ViewHolder {
  TextView text;
  ImageIcon icon;
}

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
  convertView = mInflater.inflate(...
  holder = new ViewHolder();
  holder.text = (TextView) convertView.findViewById(R.id.text);
  holder.icon = (ImageView) convertView.findViewById(R.id.icon);
  convertView.setTag(holder);
} else {
  holder = (viewHolder) convertView.getTag();
}

  holder.text.setText(DATA[position]);
  holder.icon.setImageBitmap(...

  return convertView;
}

Comments

  • do not change convertView structure (use view type system for this instead)
  • handling data changes: use the adapter properly, then notifyDataSetChanged() (for new or updated data); notifyDataSetInvalidated() (in case there is no more data available).

Built-in view type system

  • getItemViewType, type of view for a given position. Used to provide the right convertView
  • getViewTypeCount (how many different types to expect)

Slow data source

  • Adapter modifications on the UI thread
  • Fetching data can happen enywhere, request data on another thread
  • Commit adapter changes on the UI thread
  • Call notifyDataSetChanged() in the same UI thread.

Item properties

  • Enabled: item can be selected, clicked
  • Disabled: section dividers/headers within content
  • Single choice mode (radio buttons)
  • Multiple choice mode (checked items)
  • Focusable items (or rows)

Headers and Footers

  • ListView.addHeaderView()
  • ListView.addFooterView()
  • must be called before setAdapter()
  • isSelectable == Adapter.isEnabled() (see below)
  • if you use header/footer, and then list.setAdapter(myAdapter); list.getAdapter() != myAdapter;  Internally, ListView wraps one adapter in another one.

Selectors

  • used to highlight the selected item
  • Not shown in touch mode (there is no selection in touch mode!)
  • Shown behind list items by default. Use android:drawSelectorOnTop=”true” if you want to change it.

Other features, DOs and DON’Ts

  • android:transcriptMode, used to control the behaviour of the scrolling of the list when content is added.
  • android:stackFromBottom, stacks items in reverse order, starts with the last item from the adapter
  • these are useful for chats, messaging, talk, irc, etc.
  • android:textFilterEnabled=”true”, filters and presents only the items that match what’s typed, you need to re-implement the Filter instance.
protected FilterResults performFiltering(CharSequence prefix)
protected void publishResurts(CharSequence constraint, FilterResults results)
  • for optimisation and efficient rendering purposes, all ListView items are turned into bitmaps, with a default black background. Use android:cacheColorHint=”myBackgroundColor”, or set it to #0 to disable it.
  • Use android:smoothScrollbar=”false” to avoid scrollbar changing size when scrolling quickly through a list.
  • DON’T – never set android:layout_height=”wrap_content”
  • DON’T – never use ListView inside a ScrollView
  • It is ok to have horizontal scroller with ListView using vertical scroller though.

C2DM: building push applications

Notes from Google I/O talk “Building push applications for Android“. How to keep the data on the device fresh? There are two techniques: polling and pushing.

Polling is simple to implement. Device must periodically ask server for new data. This is appropriate for situations where data changes constantly (stock quotes, news headlines). The drawback is that radio draws a lot of power, and must stay on for several seconds. Power consumption on a device not actually polling data, is between 5-8mA. However, any active network access increases the power consumption to 180-200mA. Note also Tx is more expensive than Rx. On average, each poll costs roughly 0.5mAh. This gives 144mAh/day for 5m update frequency, and 48mAh/day for 15m updates. (Battery capacity could be between 900-1400mAh).

Pushing uses network only when necessary. Google Contacts, Calendar and Gmail sync and android market, all use of push. It is a bit more complex to implement. Solution: Android Cloud to Device Messaging.

Android Cloud to Device Messaging

  • Requires Froyo 2.2 and Market
  • will be available to all developers
  • Uses existing connection for Google services
  • Allows servers to send lightweight data messages to apps
  • Tell app new data available
  • Intent broadcast wakes up app
  • App supplies UI, e.g. notification if necessary
  • Best effort delivery
  • Heartbeat implementation to re-establish broken connections
  • SSL data delivery implemented

C2DM lifecycle:

App registers with Google, gets registration ID

// Use the Intent API to get a registration ID
Intent regIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
// Identify your app
regIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0);
// Identify role account server will use to send
regIntent.putExtra("sender", email_of_sender);
// start the registration
startService(regIntent);

App sends registration ID to its App Server.

  • App will receive the ID as an Intent com.google.android.c2dm,intent.REGISTRATION.
  • Service may issue new registration ID at any time, and the app will again receive REGISTRATION intent broadcast. app must update server with new ID then.
// Registration ID received via an Intent
public void onReceive(Context ctx, Intent intent) {
  String action = intent.getAction();
  if ("REGISTRATION".equals(action)) {
    handleRegistration(ctx, intent);
  }
}

private void handleRegistration(Context ctx, Intent intent) {
  String id = intent.getExtra("registration_id");
  if ((intent.getExtra("error") != null) {
    // registration failed. Try again later.
  } else if (id != null) {
    // Send the reg ID to the app server (in separate thread)
  }
}

App server sends (authenticated) message to Google

Google sends message to device

  • Device receives message, converts it to Intent
    App woken up/started by Intent broadcast
  • com.google.android.c2dm.intent.RECEIVE
  • data.<key> set as intent extras
  • app needs com.example.app.permission.C2D_MESSAGE
public void onReceive(Context ctx, Intent intent) {
  String action = intent.getAction();
  if ("_RECEIVE".equals(action)) {
    // grap a wakelock, use IntentService to do work
  }
}

App can unregister ID (when user no longer wants push)

The device can be offline for a period of time, and a number of messages might be issues during that time. To avoid message explosion, there is a simple mechanism that handles this situation:

Collapse keys

  • latest message replaces older ones with same key
  • Avoids message explosion for offline device
  • App may use multiple collapse keys (max of four in flight per device)
  • State should be in app server, not in message (tell app when it should fetch data)

Attenuation

  • Messages may not be delivered to device immediately
  • Protects devices that are receiving many messages (avoid constant radio wake up)
  • Attenuation per app/collapse key
  • This is done automatically by the Connection Server

Delay While Idle

  • Device tells Connection Server when screen is on and off. Screen off means device is idle.
  • Apps can request message only be delivered when active. This avoids waking up device with info that will not be seen/used (think chat presence, friend location updates, etc).

Demo applications (open source examples)

  • Chrome to phone: a simple extension to Chrome that allows any webpage to be pushed onto a mobile device.
  • JumpNote: notes, with two way push sync, App Engine backend, GWT UI, uses Sync Framework, uses C2DM

Question session

Only Froyo 2.2 onwards, no back porting, heartbeats might be in an order of 30min or so, connection is amortized accross multiple applications (Contacts, GMail, Sync, etc), OMA Push service is a different paradigm, and is not integrated wit C2DM. Security: persistent connection is encrypted with SSL. Generally, do not pass STATE in your massages, push the semantics and state up to the application layer. Max size of the message is 1kB, and the message is retained on the servers for weeks (perhaps months).

REST client application patterns

The notes taken from the Google I/O talk on “Android REST client applications“. There is a number of reasons why to implement a native REST application on a mobile device instead of using a mobile website and app in a browser. Some of the reasons:

  • better integration with the content providers of the mobile platform
  • access to sensors and other applications (Services, Intents, etc)
  • ability to augment the functionality of the RESTful application with other applications on a device
  • much better UI integration
  • simply: users prefer native apps to running apps in a browser

Implementation patterns

Service API pattern

Using this pattern, we wrap all the REST interactions in a worker thread on a service.Provide a processor that interacts with a local Content Provider, and provides a local cache for the REST resources. Add two extra columns: status column and result column. Every resource has a transition state, and we need to state (STATE_POSTING, STATE_UPDATING, STATE_DELETING, STATE_OK). The result column contains the HTTP return code for the last REST operation.

Implement a ServiceHelper, singleton, that exposes a simple asynchronous public API to be used by the user interface. Prepare and send the Service request on the forward path:

  • check if the method is already pending (check the state)
  • create the request Intent
  • add the operation type and a unique request id
  • add the method specific parameters
  • add the binder callback
  • call startService(Intent)
  • return the request id

On a return path: Dispatch callbacks to the user interface listeners

Handling REST Method in an Activity:

  • onResume() add the listener
  • onPause() remove the callback
  • consider these cases: callback comes back when (A) activity is active (B) activity is paused, and return AFTER the callback is made (C) Activity is paused/resumed while the method was running.
  • CursorAdapter handles the ContentProvider notification by implementing a ContentObserver.

For performance considerations, use these:

  • Froyo uses org.json API, with much improved performance. Use it.
  • Enable the gzip content encoding when possible.
  • Use the Apache HTTP client (not the Java one)
  • Be nice, shutdown the Service if there is no requests queued
  • Implement a queue for downloads (if you need to download multiple things in parallel)

ContentProvider API pattern

Similar to the previous model, however, the Activity interacts with the ContentProvider, that works as a facade and hides all the REST Service interactions behind the scenes. It is now the ContentProvider that interacts with Service Helper.

In this pattern the retries have to be internally handled by the ContentProvider.

(Simple) ContentProvider + SyncAdapter pattern

Variant of the ContentProvider pattern. SyncAdapter helps to synchronise remote and local content. The SyncManager implements a queue of SyncAdapters, and manages system-wide syncing. SyncAdapter with the help of SyncManager. It will re-try with incremental delays upon sync failures.

This pattern may not update things as fast as the manual approaches above. (And, even though GMail and Calendar are using it, the Google Twitter Client is probably not using it, right?)

Conclusions

  • Do not implement REST methods inside Activities
  • Start long running operations from a Service
  • Persist early and persist often
  • Cursors can only hold of up to 1MB of data. Minimise amount of data you store in Cursor.
  • Minimize network usage
  • Always use paging (hopefully the REST API supports paging)
  • New in Froyo: Android Cloud to Device Messaging, push notifications (will safe lots of battery life)

Android: Performance considerations & profiling

Notes from the Google I/O talk “Writing zippy Android Apps“.  The example code for the talk is at the code.google.com/zippy-android/

Terminology

Jank – chrome team’s term for stalling the event loop thread. Jank – not being immediately responsive to input. Eliminating jank: reacting quickly to events.

ANR – Android not responding. Your application is badly written. This is the ultimate result of jank. ANR will happen:

  • main thread (event/UI thread) doesn’t respond to input event in 5 seconds
  • a BroadcastReceiver doesn’t finish in 10 seconds
  • typically, eg. when doing network operations in main thread, doing slow disk operation, SQL query, in main thread
  • less than 5/10seconds, users experience a janky/sluggish/slow app behaviour

Performance numbers for Nexus One:

  • 0.04ms – writing a byte on pipe process (or reading simple /proc files from dalvik)
  • 0.12ms – void/void Binder RPC call
  • 5-25ms – uncached flash reading a byte
  • 5-200+ms – uncached flash wrting tiny amount. The more full the flash disk is, the slower the writes become.
  • 16ms – one frame of 60pgs video
  • 41ms – one frame of 24fps video
  • 100-200ms – human perception of slow action
  • 100-800ms – ping over 3G. Varies!
  • 1-6seconds – TCP setup + HTTP fetch of 6k over 3G

SQLite performance:

  • lots of writing. avoid any writing in the UI thread
  • use indexes (use: EXPLAIN and EXPLAIN QUERY PLAN in sqlite to see what’s your query doing. use the sqlite_wrapper.pl script to profile your queries)
  • cat /proc/yaffs (check the nBlockErasures – very slow, check nPageWrites nPageReads)
  • log files: often much cheaper to append to a file than use a database

android.os.AsyncTask

  • overwrite with your own task class.
  • constructor takes 3 parameters: task_parameter/typeofwork, update Integer, and final_result. Can be passed with (Void, Void, Void)
  • doBackground(task_parameter) { do your work here. the only non optional method}
  • onProgressUpdate(Integer progress) {optional, updating the progress}
  • onPostExecute(final_result) {}
  • You have to have main thread (Handler/Looper)
  • Don’t use AsyncTask in a library
  • Don’t use AsyncTasks for longish tasks that are important/critical. They may get killed by GC when the activity is closed by the user.

For critical functions use: android.app.IntentService (Android 1.5 and later):

“IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

This work-queue-processor pattern is commonly used to offload tasks from an application’s main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To us it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop th eservice as appropriate.”

Benefits of the IntentService pattern:

  • your activity’s process, while processing that Intent, now has a Service running
  • the Android process killer will try really hard not to kill you
  • but once you’re done handling your piece of work, you’re now properly disposable again
  • very easy way to use a Service

Tips:

  • disable UI elements immediately, before kicking off your AsyncTask to finish the task
  • do some animation (psinner in title bar)
  • progress dialog (for longish operations. use sparingly, can be jarring/interrupting)
  • best of both worlds: start with disabling UI, animating something, start timer, show progressDialog if it’s taking over 200ms, in AsyncTask onPostExecute cancel alarm timer

Profiling your app

Traceview tool

  • toggle in code: dalvik.system.VMDebug#{start,stop}MethodTracing()
  • toggle at runtime: adb shell am profile <process> start|stop <FILE>
  • adb shell getprop (system/build in-memory properties)
  • /data/local.prop overwrite the system-wide properties (phone reboot required)
  • setprop propname value, eg. setprop db.db_operation.threshold_ms 10

print here logging

  • non-public class android.os.PerformanceCollector
  • tracepoint name (start, stop)
  • android.osSystemClock#uptimeMillis()
  • dalvik.system.VMDebug#threadCpuTimeNanos()

New Froyo 2.2 instrumentation

Not a generic debugging feature yet, but almost there

Log vs EventLog

  • Log: text ring-buffer (adb logcat), apps use this one.
  • EventLog: binary structured ring-buffer (adb logcat -b events), intended for low-level platform development work only

EventLog records: db_sample, binder_sample, content_query_sample, content_update_sample, dvm_lock_sample

Instrumentation:

  • Database queries (see system property db.db_operation.threshold_ms)
  • RPC (Binder) calls,
  • ContentResolver,
  • Mutex lock contention in Dalvik.

Other “tricks”:

  • flashing the device cashes, use: echo 3 > /proc/sys/vm/drop_caches
  • change of the phone orientation is equivalent by default to a configuration change.
  • onRetainNonConfigurationInstance()  and   getLastNonConfigurationInstance()

UI design patterns

These are some notes taken from the Google I/O 2010 Android UI design patterns talk.

General recommendations about UI design: clarity should take precedence over simplicity. The click count number is not as important, as the clarity of how to achieve a certain task. The consistency is important, but more on a functional level, and not necessarily on the actual “chrome” or “skin” level. Make the UI elegant, clear, yet engaging. Note also, the first principle of a good design: be fast. Spent lost of time thinking and planning of what is the main story behind your app, what is the primary function, and focus on that.

UI design patterns

Dashboard – “What can I do with this app? What’s new?”

  • A quick intro to an app, revealing capabilities and proactively highlighting new content.
  • Full screen.
  • Can be organized by: features, categories, accounts.
  • DO focus on 3-6 main items for the user to choose from (2 x 3 items).
  • DO highlight what’s new (or provide user with a hint, or something new, specials. Combine the old, stable, fixed with something specific for a given user, or new feature, or something specific, temporal).
  • DO be flavourful (showcase the brand, show companions, use all real estate of the screen).

Action Bar – “How can i do <common action> quickly?”

  • Dedicated real estate at top of the screen to support navigation and frequently used operations.
  • Replaces title bar.
  • Best for actions common across your app (e.g. search, refresh, compose/new).
  • Can provide a quick link back to dashboard (or home screen).
  • It is not toobar, and it is not replacement for menu. Just pick few actions and leverage them.
  • DO use to bring key actions onscreen (up to 3)
  • DO help to convey a sense of place
  • DO use consistently within your app
  • DON’T use for contextual actions (see quick action pattern below)

Quick Actions – “What can I do with this thing here?”

  • Action popup triggered from distinct visual target.
  • Long pressed on certain target.
  • Minimally disruptive to screen context.
  • Actions are straightforward, should be communicated via an icon or a short word. Focus on most obvious things a user might want to do.
  • Fast and fun.
  • DO use when items have competing internal targets
  • DO present only the for most important and obvious actions
  • DO use when the item doesn’t have a meaninfgul detail view
  • DON’T use in contexts which support multiple selection (for selections use e.g. a new menu bar appearing from the bottom of the screen, as in gmail app)

Search Bar - “How can I find something?”

  • Consistent pop-in search form anchored to top of screen
  • Replaces action bar (if present)
  • Should upport suggestions
  • Can use corpora selector to alter search mode
  • Alternatively, can offer suggestions for primary search mode, and additional items for triffering other modes.
  • DO use for simple searches
  • DO use the behaviour consistently

Companion Widget – Can I make this app a fun part of my Home screen?”

  • Supports the app by displaying its content and capabilities on the Home screen
  • Makes Home feel more custom, personalized
  • DO provide value
  • DO be space efficient
  • DON’T make it too complex. DON’T try to provide too much.
  • DO handoff to the app for more complex tasks

Enabling Device Diversity

  • 3.7″, 480×800 252DPI (HDPI)
  • 3.2″, 320×480 180DPI (MDPI)
  • Use the hdpi/mdpi and ldpi for drawable assets.
  • Scale assets yourself. Remember that MDPI to HDPI goes by 150%. Same between LDPI and MDPI.

New Android Icons

  • Tactile, Rendered, Forward Facing, Top-lit, Synecdoche (rethorical device to associate the part to the whole and whole to the part, use a single aspect of the app to represent the entire app), Diverse shapes, materials
  • Icon templates (use them to get started)

Presentation of an example (Google’s twitter client)

  • Do not use tabs, use a dynamic bubble with icons instead
  • Do use search bar and search history properly
  • Do attach context menus (quick actions) within the ListView
  • Twitter App code is going to be open sourced (date not known)

The art of interviewing

Today School of Business at University of Otago organised a workshop about interviewing. There have been 4 speakers from various disciplines sharing their experiences. There are some of the notes that I’ve taken during their presentations.

The first set of guidelines was focused on interviewing children, but some of the recommendations are well applicable to adults too. Avoid power relation, make the participant feel comfortable, perhaps wear clothes and hairstyle that mimicks your participants, choose a neutral location, bench or lawn outdoor may work well, joint lunch or just eating something, and background noise (park, cafe) helps to establish a common platform and keep a conversation flow. Always test your own questions on yourself, to see how comfortable you might be going into some of the topics yourself. Be empathetic about your participant, and do not push the envelope. Keep the conversation flow going, and not be disruptive with the topics and questions. Try to integrate them into the flow as uninterruptedly as possible. If some of the questions or topics are not covered, bring them up at the end of the session. All the themes/questions/topics must be prepared in advanced, but the actual flow of the interview should follow the natural course of the discussion/conversation. Be adaptable. Use dictaphone, and only takes notes about topics to bring up at the end. Focus attention on conversation, not note-taking. Try not to be judgmental in any form or shape, about the topics being covered, or the opinions expressed by the participant. Try not to impose your own opinion on the participant.

Naturalness is very important, but it is rather difficult to achieve. Tape recorded is essential, but use it with care, it tends to skew the participants. Be flexible about the protocol. Try not to follow religiously laddering style interview as it tends to annoy people.  People are quite apt to report when and what they have done in the past, but may not be able to tell the reasons why they have done something or decided on something. Try to explore why people do things: reasons, motivations, objectives. One technique to try out is triadic interviewing (show three elements and ask for similarities and differences, so that the participant is forced to provide a deeper analysis of a subject matter).

Ask for comments and questions at the end. Take photographs, notes and comments about the context in which the interview was conducted: it will help later to reconstruct the context and remember details of the conversation.

Use: audio, notes, drawings, photos, video, etc if appropriate. Always take backup batteries.

There are some interesting issues related to the ethics of the interviews, on record and off record statements. Be open, honest, transparent and personal.