Logcat中Choreographer消息的含义

Cod*_*oet 194 android android-logcat

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

小智 133

编排器允许应用程序将自己连接到vsync,并正确地计时以提高性能.

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

由于Choreographer被告知每个vsync事件,我可以判断其中一个Runnables是否被Choreographer.post*apis传递,并且在一帧的时间内没有完成,导致帧被跳过.

在我的理解中,编舞者只能检测跳帧.它无法说明为什么会发生这种情况.

消息"应用程序可能在其主线程上做了太多工作." 可能会误导.

  • +1的"可能会误导".我的应用程序不使用任何动画,实际上不使用任何图形,但我收到这些消息.在某些时候谷歌地图(不是我的应用程序)强行关闭...另外一个谷歌应用程序服务(不是我的应用程序!)冻结...我不知道谷歌在介绍这个新的果冻豆的东西,打破的东西是什么仍然在Froyo上工作得非常好. (41认同)

Ste*_*vie 64

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

回答问题/ tl:dr;

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

以下是所有候选人:

  • 主线程上的IO或昂贵的处理(加载drawables,膨胀布局和设置Uri's ImageView'都构成主线程上的IO)
  • 渲染大型/复杂/深View层次结构
  • 使View层次结构的大部分无效
  • onDraw定制中View的昂贵方法
  • 动画中昂贵的计算
  • 运行在过高的优先级"工人"线程被认为是"背景"(AsyncTask的是'背景’在默认情况下,java.lang.Thread不是)
  • 生成大量垃圾,导致垃圾收集器"停止世界" - 包括主线程 - 同时清理它

要真正确定您需要配置应用的具体原因.

更多详情

我一直试图通过试验和查看代码来理解编舞.

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

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

  1. 输入处理回调(处理用户输入,如触摸事件)
  2. 动画回调帧之间的补间,为正在运行的任何/所有动画提供稳定的帧启动时间.运行这些回调第二意味着任何与动画相关的计算(例如,改变View的位置)已经在调用第三种类型的回调时进行了...
  3. 查看用于绘制视图层次结构的遍历回调.

目的是匹配重新绘制无效视图(和补间动画)与屏幕vsync的速率 - 通常为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) {}
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

...产生这样的记录:

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.
Run Code Online (Sandbox Code Playgroud)

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)在安卓.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)的机器人. 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确实希望工作线程以低优先级运行,以防止它们使主线程挨饿.如果你的工作线程是低优先级,那么触发Choreographer警告的唯一方法就是在主线程上做太多.


Osc*_* S. 24

如果在很多情况下可能会在LogCat中弹出一条Info消息.

在我的例子中,它发生在我以编程方式从XML布局文件中膨胀多个视图时.这条消息本身是无害的,但可能是后来问题的标志,它将使用你的应用程序允许使用的所有RAM并导致巨大的邪恶力量关闭发生.我已经成长为喜欢看他的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.
Run Code Online (Sandbox Code Playgroud)

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

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

解决方案:我在我的REST/JSON Web服务上实现了分页以防止"大响应大小",并且我在AsyncTask上包含了添加"rows","section headers"和"sub-section headers"视图的代码以保持Main线程很酷.

所以...我希望我的经验可以帮助其他人用这条信息消息打开他们的头脑.

快乐的黑客!


小智 11

这通常发生在使用模拟器进行调试时,无论如何都知道它很慢.

  • 我有时会在手机的日志中得到这个. (35认同)
  • 是的,当此消息在logcat中打印时,进程非常慢.如果我们通过将debugvalue更改为false来运行在发布模式下,dvm似乎会忽略. (3认同)

Jul*_*ien 11

在我的情况下,当我显示sherlock操作栏不确定进度条时,我有这些消息.由于它不是我的图书馆,我决定隐藏Choreographer的输出.

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

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

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