Android线程销毁的最佳时机与实现方法?SurfaceView线程问题咨询
处理SurfaceView绘制线程的销毁时机与方法
这个问题我在做自定义SurfaceView动画的时候踩过坑,确实Android不会自动帮你销毁这个绘制线程——毕竟线程是你自己启动的,系统没法感知它的生命周期绑定关系。下面说下我实践下来最稳妥的处理方式:
一、最佳销毁时机
不要只依赖onDestroy()!因为在某些场景下(比如系统内存紧张时直接回收Activity),onDestroy()可能不会被执行。正确的时机是分层处理:
- 第一优先级:
SurfaceHolder的surfaceDestroyed()回调——当Surface被销毁时,再绘制已经没有意义,这时候就该停止线程; - 第二优先级:Activity的
onPause()——当Activity进入后台,用户看不到界面了,应该立刻停止绘制以节省资源; - 最终兜底:Activity的
onDestroy()——在这里做最后的资源清理,确保线程引用被释放。
二、具体销毁实现(核心是用标志位+中断,禁用废弃的stop())
绝对不要调用线程的stop()方法,这个API早就被废弃了,会导致线程资源无法正常释放,甚至引发死锁。我们用volatile标志位来控制线程循环,配合线程中断来实现优雅停止:
1. 在自定义SurfaceView中定义控制变量与线程
public class CustomSurfaceView extends SurfaceView implements Runnable { // 用volatile保证多线程下的可见性 private volatile boolean isRunning = true; private Thread drawThread; public CustomSurfaceView(Context context) { super(context); init(); } private void init() { // 初始化SurfaceHolder并设置回调 getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { // Surface创建后启动绘制线程 drawThread = new Thread(CustomSurfaceView.this); isRunning = true; drawThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 尺寸变化时的处理(比如调整绘制区域) } @Override public void surfaceDestroyed(SurfaceHolder holder) { // Surface销毁时停止线程 stopDrawingThread(); } }); } }
2. 编写run()方法的循环逻辑
@Override public void run() { while (isRunning) { Canvas canvas = null; try { // 锁定Canvas进行绘制 canvas = getHolder().lockCanvas(); if (canvas != null) { // 这里写你的绘制逻辑 drawContent(canvas); } // 控制帧率,避免过度绘制 Thread.sleep(16); } catch (InterruptedException e) { // 捕获中断异常,重置中断状态 Thread.currentThread().interrupt(); break; } catch (Exception e) { e.printStackTrace(); } finally { // 必须解锁Canvas,否则会导致Surface卡死 if (canvas != null) { getHolder().unlockCanvasAndPost(canvas); } } } }
3. 实现线程停止与清理的方法
// 停止绘制线程 private void stopDrawingThread() { isRunning = false; // 如果线程还在休眠,中断它让它立刻退出循环 if (drawThread != null && drawThread.isAlive()) { drawThread.interrupt(); } } // 对外暴露的销毁方法,供Activity调用 public void destroyThread() { stopDrawingThread(); // 释放线程引用,帮助GC回收 drawThread = null; }
4. 在Activity的生命周期中调用
public class MainActivity extends AppCompatActivity { private CustomSurfaceView customSurfaceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); customSurfaceView = new CustomSurfaceView(this); setContentView(customSurfaceView); } @Override protected void onPause() { super.onPause(); // Activity后台时停止绘制 customSurfaceView.stopDrawingThread(); } @Override protected void onDestroy() { super.onDestroy(); // 最终销毁线程,释放资源 customSurfaceView.destroyThread(); } }
三、关键注意点
- 一定要用
volatile修饰isRunning:保证主线程修改这个变量时,绘制线程能立刻看到最新值; - 必须在
finally块中解锁Canvas:如果不解锁,Surface会被锁住,后续无法再使用; - 捕获
InterruptedException后要重置中断状态:避免线程的中断标记被清除,影响后续逻辑; - 不要让线程持有Activity的强引用:如果SurfaceView是内部类,记得改成静态内部类,或者用弱引用持有Activity,避免内存泄漏。
内容的提问来源于stack exchange,提问作者TheEngineer
相关产品推荐
相关产品推荐

