本文介绍了SurfaceView 的 renderThread 是否应该与视图或活动具有相同的生命周期?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在创建 SurfaceView 时,通常还会创建一个单独的线程来绘制到表面上.在活动或表面同时创建和销毁线程是更好的编程实践吗?

When creating a SurfaceView it's normal to also create a separate thread to draw onto the surface. Is it better programming practice to have the thread be created and destroyed at the same time the activity is, or at the same time the surface is?

这两种方式有哪些优点/缺点?

What are some of the advantages/pitfalls of either way?

推荐答案

ActivityView 基本上是同时创建的.Surface 是稍后创建的,这就是 SufaceHolder 回调 是为了.

The Activity and the View are created at essentially the same time. The Surface is created later, and that's what the SufaceHolder callbacks are for.

您不能在 Surface 存在之前或被销毁之后渲染它,因此在此之前启动渲染线程或在此之后继续运行是没有意义的.棘手的部分是回调发生在主 UI 线程上(因为这是您设置它的地方),因此可以在渲染线程工作时调用 surfaceDestroyed() 回调.

You can't render on the Surface before it exists or after it's destroyed, so there's no point in starting your rendering thread before then or leaving it running after. The tricky part is that the callbacks happen on the main UI thread (since that's where you set it up), so the surfaceDestroyed() callback could be called while your render thread is doing work.

下面包含有关 SurfaceView/Activity 生命周期的一些说明.这些现在是官方 Android 文档的一部分;请参阅系统级图形文档中的附录 B.出于历史目的,原始帖子可在下面找到.

Some notes about the SurfaceView / Activity lifecycle are included below. These are now part of the official Android documentation; see Appendix B in the System-Level Graphics doc. The original post is available below for historical purposes.

您可以在 Grafika 中查看这两种方法的示例.方法 #1(在 onResume/onPause 中创建/销毁线程)可以在 TextureFromCameraActivity,方法#2(在surfaceCreated/surfaceDestroyed 中创建/销毁线程)可以在HardwareScalerActivityRecordFBOActivity.

You can see examples of both approaches in Grafika. Approach #1 (create/destroy thread in onResume/onPause) can be seen in TextureFromCameraActivity, approach #2 (create/destroy thread in surfaceCreated/surfaceDestroyed) can be seen in HardwareScalerActivity and RecordFBOActivity.

关于应用生命周期和 SurfaceView 的一些想法.

有两件独立的事情正在发生:

There are two somewhat independent things going on:

  1. 应用程序 onCreate/onResume/onPause
  2. 表面创建/更改/销毁

当 Activity 启动时,您会按以下顺序获得回调:

When the Activity starts, you get callbacks in this order:

  • onCreate
  • onResume
  • 表面创建
  • 表面变化

如果你点击返回",你会得到:

If you hit "back", you get:

  • 暂停
  • surfaceDestroyed(在 Surface 消失之前调用)

如果你旋转屏幕,Activity 被拆除并重新创建,所以你得到整个循环.(您可以通过检查 isFinishing() 判断这是一个快速"重启.)可能如此快速地启动/停止一个活动,以至于 surfaceCreated() 可能发生在 onPause() 之后,但我不确定.

If you rotate the screen, the Activity is torn down and recreated, so you getthe full cycle. (You can tell it's a "quick" restart by checking isFinishing().) It might be possible to start / stop an activity so quickly that surfaceCreated() might happen after onPause(), but I'm not sure about that.

但是,如果您点击电源按钮使屏幕变黑,您只会得到 onPause() --没有 surfaceDestroyed().Surface 保持活跃,渲染可以继续(你如果您继续要求,甚至可以继续获得编舞活动).如果你有一个锁定屏幕,强制你的 Activity 可以被踢到一个特定的方向,但是如果没有,您可以使用与之前相同的 Surface 从空白屏幕中出来.

If you tap the power button to blank the screen, however, you only get onPause() --no surfaceDestroyed(). The Surface remains alive, and rendering can continue (youeven keep getting Choreographer events if you continue to request them). If you havea lock screen that forces a specific orientation your Activity can get kicked, butif not you can come out of screen-blank with the same Surface you had before.

这在使用单独的渲染器线程时提出了一个基本问题SurfaceView:线程的生命周期应该绑定到 Surface 还是绑定到活动?答案是:这取决于你想在屏幕上发生什么变为空白.有两种基本方法:(1)在Activity上启动/停止线程开始/停止;(2) 在Surface create/destroy 上启动/停止线程.

This raises a fundamental question when using a separate renderer thread withSurfaceView: should the lifespan of the thread be tied to the Surface or to theActivity? The answer is: it depends on what you want to have happen when the screengoes blank. There are two basic approaches: (1) start/stop the thread on Activitystart/stop; (2) start/stop the thread on Surface create/destroy.

#1 与应用程序生命周期交互良好.我们在 onResume() 中启动渲染器线程,然后在 onPause() 中停止它.创建和配置线程的时候有点尴尬因为有时 Surface 已经存在,有时则不会.我们不能简单地将 Surface 回调转发给线程,因为如果Surface 已经存在.所以我们需要查询或缓存Surface状态,并转发到渲染器线程.注意我们在这里传递对象时要小心一点线程 -- 最好通过 Handler 消息传递 SurfaceSurfaceHolder,而不是而不仅仅是将其塞入线程中,以避免在多核系统上出现问题(参见Android SMP 入门).

#1 interacts well with the app lifecycle. We start the renderer thread in onResume() andstop it in onPause(). It gets a bit awkward when creating and configuring the threadbecause sometimes the Surface will already exist and sometimes it won't. We can't simplyforward the Surface callbacks to the thread, because they won't fire again if theSurface already exists. So we need to query or cache the Surface state, and forward itto the renderer thread. Note we have to be a little careful here passing objects betweenthreads -- best to pass the Surface or SurfaceHolder through a Handler message, ratherthan just stuffing it into the thread, to avoid issues on multi-core systems (cf.Android SMP Primer).

#2 有一定的吸引力,因为 Surface 和渲染器在逻辑上是交织在一起的.我们在 Surface 创建后启动线程,避免了线程间沟通问题.Surface 创建/更改的消息被简单地转发.我们需要确保渲染在屏幕变黑时停止,并在屏幕变黑时恢复非空白;这可能是告诉编舞者停止调用帧绘制回调.我们的 onResume() 需要恢复回调当且仅当渲染器线程正在运行.不过,这可能不是那么简单——如果我们基于动画在帧之间经过的时间上,当下一个事件发生时,我们可能会有一个非常的差距到达,因此可能需要明确的暂停/恢复消息.

#2 has a certain appeal because the Surface and the renderer are logically intertwined.We start the thread after the Surface has been created, which avoids the inter-threadcommunication concerns. Surface created / changed messages are simply forwarded. Weneed to make sure rendering stops when the screen goes blank, and resumes when itun-blanks; this could be a simple matter of telling Choreographer to stop invoking theframe draw callback. Our onResume() will need to resume the callbacks if and only ifthe renderer thread is running. It may not be so trivial though -- if we animate basedon elapsed time between frames, we could have a very large gap when the next eventarrives, so an explicit pause/resume message may be desirable.

以上主要关注渲染器线程是如何配置的,以及它正在执行.一个相关的问题是从线程中提取状态 Activity 被终止(在 onPause()onSaveInstanceState() 中).方法#1 会奏效最好的办法是,因为一旦加入了渲染器线程,它的状态就可以是无需同步原语即可访问.

The above is primarily concerned with how the renderer thread is configured and whetherit's executing. A related concern is extracting state from the thread when theActivity is killed (in onPause() or onSaveInstanceState()). Approach #1 will workbest for that, because once the renderer thread has been joined its state can beaccessed without synchronization primitives.

这篇关于SurfaceView 的 renderThread 是否应该与视图或活动具有相同的生命周期?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-26 05:46