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