【问题标题】:Meaning of Choreographer messages in Logcat [duplicate]Logcat中Choreographer消息的含义[重复]
【发布时间】:2012-07-01 06:22:16
【问题描述】:

我安装了最新版本的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 中完成。

【问题讨论】:

  • 来自您的链接:协调动画、输入和绘图的时间。看起来它用于在 UI 中制作动画。
  • 你在模拟器中运行吗?
  • 是的,我是。我了解 Choreographer 可能是处理动画的组件,当它没有获得足够的 cpu 周期时,它会跳过一些帧并输出此调试消息。让我担心的是我的主线程中的什么可能导致这个以及我如何跟踪它。 AFAIK 我所有的处理都发生在后台线程中。
  • 我有一些代码在工作线程中运行,执行一些繁重的计算,它会定期将更新发布到 UI 线程。虽然这不会阻塞 UI 线程,但用户的输入会排队,直到当前工作计算增量结束时才会显示在显示屏上。发生这种情况时,我不断收到来自编舞师的消息,例如“跳过 37 帧!”等。有人试图让 Jelly Bean 变得“黄油般光滑”,我怀疑这些消息是鼓励我们不要有任何我们的显示器有点抖动。
  • @Elenasys 您已将此问题标记为与 2013 年 2 月 3 日很久以后提出的问题的重复。对我来说听起来不公平:)

标签: android android-logcat


【解决方案1】:

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

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

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

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

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

【讨论】:

  • +1 表示“可能具有误导性”。我的应用程序不使用任何动画,实际上也不使用任何图形,但我收到了这些消息。在某些时候谷歌地图(不是我的应用程序)强制关闭......还有另一个谷歌应用程序服务(不是我的应用程序!)冻结......我不知道谷歌在引入这种新的果冻豆东西时会怎么想仍然在 Froyo 上工作得很好。
  • 如果在主线程上完成的工作过多,Android 可能不会像它预期的那样具有确定性,但不会杀死任何任务吧?
  • 我认为“我可以判断 Choreographer.post* api 传递的 Runnable 之一是否没有在一帧的时间内完成,从而导致帧被跳过。”不是那么正确。我刚刚查看了 Choreographer 的代码,当它收到 VSYNC 时,它会执行所有到期时间
  • 更重要的是,我怀疑它是否会跳帧或只是由于三重缓冲区而滞后。
【解决方案2】:

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

回答问题/tl:dr;

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

以下都是候选人:

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

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

更多详情

我一直在尝试通过尝试和查看 code 来了解 Choreographer。

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

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

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

【讨论】:

  • 这是一个很好的答案,就我而言,我确实有 maulti-threads 来处理其他工作,并且我反复从那些其他线程访问 main-ui-thread。这些访问是必要的,您认为有什么解决方案?
  • “我反复访问主用户界面线程”——你的意思是你正在使用View.postActivity.runOnUiThread,还是直接使用Handler 将工作发送到主线程?如果它不是 UI 工作,不要把它交给主线程来做:)。如果是 UI 工作,您别无选择,如果它导致丢帧,您可能需要寻找其他方法来优化您的 UI 代码。也许您有一个值得作为新问题发布的案例?
  • 其实我已经发过了,现在我将添加一些代码。 stackoverflow.com/questions/29767997/…
【解决方案3】:

如果在许多情况下可能会在您的 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……但是,那是另一个问题了。

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

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

所以...我希望我的经验可以帮助那些对这条信息消息感到头疼的人。

黑客愉快!

【讨论】:

  • 如何使用 AsyncTask 来避免这个问题?
【解决方案4】:

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

【讨论】:

  • 我有时也会在手机日志中看到它。
  • 是的,当这条消息打印在 logcat 中时,这个过程非常慢。如果我们通过将 debugvalue 更改为 false 以在发布模式下运行,dvm 似乎会忽略。
  • 这发生在我的真实手机上:摩托罗拉 Droid Bionic
  • 对于我的应用程序,即使在我最慢的旧硬件设备上,我也从未收到“已跳过”的日志消息。但是在模拟器(特别是 Genymotion 模拟器)上运行时,我总是会收到这些消息。
【解决方案5】:

在我的情况下,当我显示 sherlock 操作栏终止进度条时,我会收到这些消息。由于它不是我的库,我决定隐藏 Choreographer 输出。

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

标签:^((?!Choreographer).*)$

我使用了其他地方解释的正则表达式:Regular expression to match a line that doesn't contain a word?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-22
    • 2015-07-22
    • 1970-01-01
    • 2018-12-19
    • 1970-01-01
    • 2013-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多