Service中线程运行后主线程负载过高致应用崩溃问题排查
问题根源分析与解决
你的应用崩溃和ANR(应用无响应)的核心原因非常明确:你没有真正启动新线程,而是把耗时循环放到了Service的主线程里执行!
具体问题点
看这段启动线程的代码:
new Thread(runnable).run();
Thread类的run()方法只是直接调用了Runnable对象的run()逻辑,并不会创建新线程——这段代码本质上是把循环、Sleep这些耗时操作直接塞到了Service的主线程里。主线程长时间被占用,系统就会抛出你看到的Skipped 1207 frames提示,最终触发ANR并生成traces.txt栈信息。
核心修复方案
把run()换成start()方法,这才是真正启动新线程的正确姿势:
new Thread(runnable).start();
start()方法会让JVM创建一个独立的操作系统线程,然后在这个新线程里执行Runnable的run()逻辑,这样耗时操作就不会阻塞Service的主线程了。
额外优化建议
除了核心问题,还有两个细节可以优化,避免潜在的线程安全问题:
- 线程安全的
shouldRun:shouldRun变量会在子线程和主线程(比如onDestroy可能修改它)同时访问,建议用volatile修饰,保证多线程下的变量可见性:volatile boolean shouldRun = true; - 优雅的线程退出逻辑:在Service销毁时,主动把
shouldRun设为false,让子线程能及时停止循环,避免资源泄漏:@Override public void onDestroy() { Log.v(TAG,"Service is destroyed"); shouldRun = false; // 通知子线程停止循环 super.onDestroy(); }
修改后的完整代码
public class CheckService extends Service { long startTime; long stopTime; volatile boolean shouldRun = true; // 加volatile保证线程可见性 private static final String TAG = "CheckService"; final AppChecker appChecker = new AppChecker(); @Nullable @Override public IBinder onBind(Intent intent) { return null; } Runnable runnable = new Runnable() { @Override public void run() { Log.v(TAG,"Thread is still running"); while(shouldRun){ Log.v(TAG,"Process running is "+ appChecker.getForegroundApp(getApplicationContext())); if (System.currentTimeMillis() - startTime >= stopTime){ shouldRun = false; stopSelf(); return; } try{ Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); shouldRun = false; // 线程被中断时也停止循环 } } } }; @Override public int onStartCommand(Intent intent, int flags, final int startId) { startTime = System.currentTimeMillis(); stopTime = intent.getLongExtra("stop-time",0); if (stopTime == 0){ stopSelf(); }else{ Log.v(TAG," Thread is started at "+ String.valueOf(startTime)); new Thread(runnable).start(); // 这里改成start()启动新线程 } return Service.START_NOT_STICKY; } @Override public void onDestroy() { Log.v(TAG,"Service is destroyed"); shouldRun = false; // 销毁时通知子线程停止 super.onDestroy(); } }
总结
替换run()为start()是解决当前ANR和崩溃的关键,加上volatile和onDestroy里的停止逻辑,能让你的Service和线程运行更稳定。
内容的提问来源于stack exchange,提问作者Sriram R




