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