Android中如何停止使用HandlerThread的Service内的ServiceHandler
问题根源
你遇到的线程无法停止的核心原因有2个:
HandlerThread的quitSafely()仅会清空待处理消息、终止Looper的消息循环,不会主动中断当前正在执行的handleMessage任务,你在handleMessage中写了while(true)死循环,该任务会一直执行不会退出Thread.sleep()被中断后仅会抛出InterruptedException,你捕获异常后没有跳出循环的逻辑,相当于中断后还是会继续跑死循环
修复步骤
- 新增线程安全的停止标志位,控制循环的执行状态
- 在Service销毁时,除了调用
quitSafely(),还要主动中断HandlerThread、修改停止标志位 - 补充资源释放逻辑,退出时主动注销传感器监听器、关闭文件输出流
修复后的核心代码
首先修改Service内的变量定义:
// 新增volatile修饰的停止标志位,保证多线程可见性 private volatile boolean isCollecting = true; private HandlerThread thread; // 保留之前的其他变量即可
然后修改handleMessage里的循环逻辑:
@Override public void handleMessage(Message msg) { try { currTime = java.time.LocalDateTime.now(); String newFileName = currTime.toString()+FILENAME+".csv"; fos = openFileOutput(newFileName, Context.MODE_APPEND); } catch (FileNotFoundException e) { System.out.println("did not open file"); e.printStackTrace(); } sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); mLight = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); try { // 把while(true)改成判断停止标志位 do { if (!isCollecting) break; // 标志位为false直接退出循环 sensorManager.registerListener(this, mLight, 10000000); Thread.sleep(2000); System.out.println("thread sleeping"); sensorManager.unregisterListener(this); if (!isCollecting) break; Thread.sleep(5000); } while (isCollecting); }catch (InterruptedException e) { // 收到中断异常直接退出循环,保留中断状态即可 Thread.currentThread().interrupt(); } finally { // 退出前统一释放资源 sensorManager.unregisterListener(this); try { if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } stopSelf(msg.arg1); }
最后修改onDestroy销毁逻辑:
@Override public void onDestroy() { super.onDestroy(); Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); System.out.println("Stopping service"); // 先修改停止标志位 isCollecting = false; // 中断线程,打断sleep的阻塞状态 if (thread != null) { thread.interrupt(); thread.quitSafely(); } // 不需要额外调用stopSelf,onDestroy本身就是Service销毁的回调 }
额外优化建议
- 你当前返回的
START_STICKY会让Service被系统杀死后自动重启,如果不需要这个特性可以改成START_NOT_STICKY避免不必要的重启 - 传感器采样频率不要写固定的硬编码值,可以用
SensorManager.SENSOR_DELAY_NORMAL之类的官方常量,兼容性更好
内容的提问来源于stack exchange,提问作者Brotchu
相关产品推荐
相关产品推荐

