Tag Archives: froyo

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).

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()