创建 SurfaceView 时,通常还会创建一个单独的线程来绘制到表面上。在活动的同时创建和销毁线程,还是同时创建和销毁表面,是更好的编程实践?
这两种方式的优点/陷阱是什么?
Activity
和View
基本上是同时创建的。Surface
是稍后创建的,这就是SufaceHolder回调的用途。
您无法在Surface
存在之前或销毁之后在它上渲染,因此在此之前启动渲染线程或让它在之后运行是没有意义的。 棘手的部分是回调发生在主 UI 线程上(因为这是您设置它的地方),因此可以在渲染线程工作时调用surfaceDestroyed()
回调。
编辑:
下面包含有关 SurfaceView/活动生命周期的一些说明。 这些现在是官方 Android 文档的一部分;请参阅系统级图形文档中的附录 B。 原始帖子可在下面找到,用于历史目的。
您可以在 Grafika 中看到这两种方法的示例。 方法#1(在onResume/onPause中创建/销毁线程)可以在TextureFromCameraActivity中看到,方法#2(在surfaceCreated/surfaceDestroy中创建/销毁线程)可以在HardwareScalerActivity和RecordFBOActivity中看到。
关于应用程序生命周期和
SurfaceView
的一些想法。
有两件有点独立的事情正在发生:
- 应用程序创建/恢复/暂停
- 表面创建/更改/销毁
活动启动时,您会收到以下顺序的回调:
- 创建
- 在简历上
- 曲面已创建
- 表面已更改
如果您点击"返回",您将获得:
- 暂停时 表面
- 已销毁(在表面消失之前调用)
如果您旋转屏幕,Activity
将被拆除并重新创建,因此您可以获得 整个周期。 (您可以通过检查isFinishing()
来判断这是"快速"重新启动。有可能如此快速地启动/停止一项活动,以至于surfaceCreated()
可能会在onPause()
之后发生,但我不确定。
但是,如果您点击电源按钮使屏幕空白,则只会得到onPause()
- 没有surfaceDestroyed()
.Surface
保持活动状态,渲染可以继续(您 如果您继续请求它们,甚至可以继续获得编舞活动)。 如果你有 强制特定方向的锁定屏幕可能会被踢Activity
,但是 如果没有,你可以用你以前一样的Surface
从屏幕空白出来。
当使用单独的渲染器线程时,这引发了一个基本问题SurfaceView
:螺纹的寿命应该绑在Surface
还是Activity
? 答案是:这取决于您希望在屏幕时发生什么 变为空白。 有两种基本方法:(1) 在Activity
上启动/停止线程 启动/停止;(2) 启动/停止线程Surface
创建/销毁。
#1 与应用生命周期交互良好。 我们在onResume()
中启动渲染器线程,然后 在onPause()
停止它. 创建和配置线程时有点尴尬 因为有时 Surface 已经存在,有时不会。 我们不能简单地 将Surface
回调转发到线程,因为如果Surface
已经存在。 所以我们需要查询或缓存Surface
状态,并转发它 到渲染器线程。 请注意,我们必须在这里小心地在传递对象之间 线程 - 最好通过Handler
消息传递Surface
或SurfaceHolder
,而不是 不仅仅是将其填充到线程中,以避免多核系统上出现问题(参见 安卓SMP入门)。
#2 具有一定的吸引力,因为Surface
和渲染器在逻辑上是交织在一起的。 我们在创建Surface
后启动线程,从而避免了线程间 沟通问题。Surface
创建/更改的消息只需转发即可。 我们 需要确保渲染在屏幕变为空白时停止,并在屏幕变为空白时恢复 非空白;这可能是一个简单的问题,告诉编舞停止调用 帧绘制回调。 我们的onResume()
将需要恢复回调,当且仅当 渲染器线程正在运行。 不过,这可能不是那么微不足道 - 如果我们基于动画 在帧之间经过的时间上,当下一个事件发生时,我们可能会有非常大的差距 到达,因此可能需要显式暂停/恢复消息。
以上主要关注渲染器线程的配置方式以及渲染器线程是否 它正在执行。 一个相关的问题是在Activity
被杀(onPause()
或onSaveInstanceState()
)。 方法#1将起作用最好这样做,因为一旦渲染器线程加入,其状态可以是 在没有同步基元的情况下访问。