我迟到了,但希望这是对这里其他答案的有用补充......
回答问题/tl:dr;
我需要知道如何确定我的应用程序可能正在做哪些“太多工作”,因为我的所有处理都是在 AsyncTasks 中完成的。
以下都是候选人:
- 主线程上的 IO 或昂贵的处理(加载可绘制对象、膨胀布局以及在
ImageView 上设置 Uri 都构成主线程上的 IO)
- 渲染大/复杂/深
View层次结构
- 使
View 层次结构的大部分无效
- 自定义
View 中的昂贵onDraw 方法
- 动画中的昂贵计算
- 以太高的优先级运行“工作”线程而不能被视为“后台”(
AsyncTask 默认为“后台”,java.lang.Thread 为不是)
- 产生大量垃圾,导致垃圾收集器在清理时“停止世界”(包括主线程)
要真正确定您需要分析您的应用的具体原因。
更多详情
我一直在尝试通过尝试和查看 code 来了解 Choreographer。
Choreographer 的文档以“协调动画、输入和绘图的时间”打开。这实际上是一个很好的描述,但其余部分继续过分强调动画。
Choreographer 实际上负责执行 3 种类型的回调,它们按以下顺序运行:
- 输入处理回调(处理用户输入,例如触摸事件)
- 用于在帧之间进行补间的动画回调,为任何/所有正在运行的动画提供稳定的帧开始时间。第二次运行这些回调意味着任何与动画相关的计算(例如,更改视图的位置)在调用第三种回调时已经完成...
- 用于绘制视图层次结构的视图遍历回调。
目的是使重新绘制无效视图(和补间动画)的速率与屏幕垂直同步(通常为 60fps)相匹配。
关于跳帧的警告看起来像是事后才想到的:如果单个通过 3 个步骤需要超过预期帧持续时间的 30 倍,则记录该消息,因此您可以期望看到的最小数字在日志消息中是“跳过 30 帧”;如果 每个 次通过的时间比应有的时间长 50%,您仍然会跳过 30 帧(淘气!),但您不会收到警告。
从涉及的 3 个步骤中可以清楚地看出,触发警告的不仅仅是动画:使用复杂的 onDraw 方法使大型 View 层次结构或 View 的重要部分无效可能就足够了。
例如这会反复触发警告:
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) {}
}
});
}
}
... 产生这样的日志记录:
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.
您可以在onDraw 期间从堆栈中看到,无论您是否制作动画,都会涉及编舞:
在 example.AnnoyTheChoreographerActivity$1.onDraw(AnnoyTheChoreographerActivity.java:25)
在 android.view.View.draw(View.java:13759)
...相当多的重复...
在 android.view.ViewGroup.drawChild(ViewGroup.java:3169)
在 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3039)
在 android.view.View.draw(View.java:13762)
在 android.widget.FrameLayout.draw(FrameLayout.java:467)
在 com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2396)
在 android.view.View.getDisplayList(View.java:12710)
在 android.view.View.getDisplayList(View.java:12754)
在 android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:1144)
在 android.view.ViewRootImpl.draw(ViewRootImpl.java:2273)
在 android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2145)
在 android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1956)
在 android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1112)
在 android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4472)
在 android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
在 android.view.Choreographer.doCallbacks(Choreographer.java:555)
在 android.view.Choreographer.doFrame(Choreographer.java:525)
在 android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
在 android.os.Handler.handleCallback(Handler.java:615)
在 android.os.Handler.dispatchMessage(Handler.java:92)
在 android.os.Looper.loop(Looper.java:137)
在 android.app.ActivityThread.main(ActivityThread.java:4898)
最后,如果来自其他线程的争用会减少主线程可以完成的工作量,那么即使您实际上并没有在主线程上执行工作,跳帧的机会也会显着增加。
在这种情况下,建议应用程序在主线程上执行过多操作可能会被认为具有误导性,但Android really wants worker threads to run at low priority 是为了防止它们使主线程处于饥饿状态。如果你的工作线程是低优先级的,那么触发 Choreographer 警告的唯一方法就是在主线程上做太多事情。