I'm late to the party, but hopefully this is a useful addition to the other answers here...
I need to know how I can determine what "too much work" my application may be doing as all my processing is done in AsyncTasks.
The following are all candidates:
Uri
's on ImageView
's all constitute IO on the main thread)View
hierarchiesView
hierarchyonDraw
methods in custom View
'sAsyncTask
's are "background" by default, java.lang.Thread
is not)To actually determine the specific cause you'll need to profile your app.
I've been trying to understand Choreographer by experimenting and looking at the code.
The documentation of Choreographer opens with "Coordinates the timing of animations, input and drawing." which is actually a good description, but the rest goes on to over-emphasize animations.
The Choreographer is actually responsible for executing 3 types of callbacks, which run in this order:
The aim is to match the rate at which invalidated views are re-drawn (and animations tweened) with the screen vsync - typically 60fps.
The warning about skipped frames looks like an afterthought: The message is logged if a single pass through the 3 steps takes more than 30x the expected frame duration, so the smallest number you can expect to see in the log messages is "skipped 30 frames"; If each pass takes 50% longer than it should you will still skip 30 frames (naughty!) but you won't be warned about it.
From the 3 steps involved its clear that it isn't only animations that can trigger the warning: Invalidating a significant portion of a large View
hierarchy or a View
with a complicated onDraw method might be enough.
For example this will trigger the warning repeatedly:
public class AnnoyTheChoreographerActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_linear_layout);
ViewGroup root = (ViewGroup) findViewById(R.id.root);
root.addView(new TextView(this){
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
long sleep = (long)(Math.random() * 1000L);
setText("" + sleep);
try {
Thread.sleep(sleep);
} catch (Exception exc) {}
}
});
}
}
... which produces logging like this:
11-06 09:35:15.865 13721-13721/example I/Choreographer? Skipped 42 frames! The application may be doing too much work on its main thread.
11-06 09:35:17.395 13721-13721/example I/Choreographer? Skipped 59 frames! The application may be doing too much work on its main thread.
11-06 09:35:18.030 13721-13721/example I/Choreographer? Skipped 37 frames! The application may be doing too much work on its main thread.
You can see from the stack during onDraw
that the choreographer is involved regardless of whether you are animating:
at example.AnnoyTheChoreographerActivity$1.onDraw(AnnoyTheChoreographerActivity.java:25) at android.view.View.draw(View.java:13759)
... quite a bit of repetition ...
at android.view.ViewGroup.drawChild(ViewGroup.java:3169) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3039) at android.view.View.draw(View.java:13762) at android.widget.FrameLayout.draw(FrameLayout.java:467) at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2396) at android.view.View.getDisplayList(View.java:12710) at android.view.View.getDisplayList(View.java:12754) at android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:1144) at android.view.ViewRootImpl.draw(ViewRootImpl.java:2273) at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2145) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1956) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1112) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4472) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725) at android.view.Choreographer.doCallbacks(Choreographer.java:555) at android.view.Choreographer.doFrame(Choreographer.java:525) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711) at android.os.Handler.handleCallback(Handler.java:615) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4898)
Finally, if there is contention from other threads that reduce the amount of work the main thread can get done, the chance of skipping frames increases dramatically even though you aren't actually doing the work on the main thread.
In this situation it might be considered misleading to suggest that the app is doing too much on the main thread, but Android really wants worker threads to run at low priority so that they are prevented from starving the main thread. If your worker threads are low priority the only way to trigger the Choreographer warning really is to do too much on the main thread.