198

我安装了最新版本SDK (API 16)并获得了最新的 ADT。我现在在 logcat 中看到这些消息,我很确定,我以前从未见过。有人对此有任何想法吗?

06-29 23:11:17.796:I/Choreographer(691):跳过 647 帧!应用程序可能在其主线程上做了太多工作。

我进行了搜索,发现了这个链接:http: //developer.android.com/reference/android/view/Choreographer.html。这是 API 16 中引入的一个新类。

我需要知道如何确定我的应用程序可能正在做哪些“太多工作”,因为我的所有处理都是在AsyncTasks 中完成的。

4

5 回答 5

138

Choreographer 允许应用程序将自己连接到 vsync,并适当地计时以提高性能。

Android 视图动画在内部使用 Choreographer 的目的相同:正确计时动画并可能提高性能。

由于 Choreographer 被告知每个 vsync 事件,因此它可以判断 Choreographer.post* api 传递的 Runnable 之一是否没有在一帧的时间内完成,从而导致帧被跳过。

据我了解,Choreographer 只能检测到跳帧。它无法说明为什么会发生这种情况。

消息“应用程序可能在其主线程上做的工作过多。” 可能会产生误导。

于 2012-07-04T12:34:24.377 回答
66

我迟到了,但希望这是对这里其他答案的有用补充......

回答问题 / tl:dr;

我需要知道如何确定我的应用程序可能在做什么“太多工作”,因为我的所有处理都是在 AsyncTasks 中完成的。

以下为所有候选人:

  • 主线程上的 IO 或昂贵的处理(加载可绘制对象、膨胀布局和设置Uri' ImageViews 都构成主线程上的 IO)
  • 渲染大/复杂/深View层次结构
  • View使层次结构的大部分无效
  • onDrawcustom 中View的昂贵方法
  • 动画中的昂贵计算
  • 以太高的优先级运行“工作”线程而不能被视为“后台”(AsyncTask默认情况下是“后台” java.lang.Thread不是
  • 产生大量垃圾,导致垃圾收集器在清理时“停止世界”——包括主线程

要真正确定具体原因,您需要分析您的应用程序。

更多详情

我一直试图通过实验和查看代码来理解 Choreographer 。

Choreographer 的文档以“协调动画、输入和绘图的时间”打开。这实际上是一个很好的描述,但其余部分继续过分强调动画。

Choreographer 实际上负责执行 3 种类型的回调,它们按以下顺序运行:

  1. 输入处理回调(处理用户输入,例如触摸事件)
  2. 用于帧之间补间的动画回调,为任何/所有正在运行的动画提供稳定的帧开始时间。第二次运行这些回调意味着在调用第三种回调时已经进行了任何与动画相关的计算(例如更改视图的位置)......
  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 警告的唯一方法就是在主线程上做太多事情。

于 2013-11-06T12:16:26.117 回答
24

如果在许多情况下可能会在您的 LogCat 中弹出一条 Info 消息。

就我而言,它发生在我以编程方式从 XML 布局文件中扩展多个视图时。该消息本身是无害的,但可能是以后出现问题的迹象,该问题将使用您的应用程序允许使用的所有 RAM 并导致超级邪恶 Force Close 发生。我已经成长为那种喜欢看到他的 Log WARN/INFO/ERROR Free 的开发人员。;)

所以,这是我自己的经验:

我收到消息:

10-09 01:25:08.373: I/Choreographer(11134): Skipped XXX frames!  The application may be doing too much work on its main thread.

...当我通过从 XML 扩展视图并使用来自 REST 响应的数据填充其字段(图像、文本等)来创建自己的自定义“超复杂多节列表”时/ JSON Web 服务(没有分页功能),通过将所有这些以正确的顺序添加到 LinearLayout(在 ScrollView 内具有垂直方向),该视图将充当行、子节标题和节标题。所有这些都是为了模拟带有可点击元素的 listView ......但是,那是另一个问题。

作为负责任的开发人员,您希望使应用程序真正有效地利用系统资源,因此列表的最佳实践(当您的列表不是那么复杂时)是使用带有 Loader 的 ListActivity 或 ListFragment 并使用 Adapter 填充 ListView,这应该是更有效的,事实上它是,你应该一直这样做,再次......如果你的列表不是那么复杂。

解决方案:我在我的 REST/JSON Web 服务上实现了分页以防止“大响应大小”,并且我包装了在 AsyncTask 上添加“行”、“节标题”和“子节标题”视图的代码以保留 Main线程很酷。

所以...我希望我的经验能帮助那些因这条信息消息而大开眼界的人。

快乐黑客!

于 2012-11-09T02:16:29.430 回答
11

这通常在使用模拟器进行调试时发生,众所周知,模拟器总是很慢。

于 2012-08-10T18:57:10.837 回答
11

就我而言,当我显示夏洛克操作栏终止进度条时,我会收到这些消息。由于它不是我的库,我决定隐藏 Choreographer 输出。

您可以使用此过滤器表达式将 Choreographer 输出隐藏到 Logcat 视图中:

标签:^((?!编舞).*)$

我使用了其他地方解释的正则表达式: 正则表达式匹配不包含单词的行?

于 2012-11-16T13:41:36.850 回答