You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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的主线程了。

额外优化建议

除了核心问题,还有两个细节可以优化,避免潜在的线程安全问题:

  1. 线程安全的shouldRunshouldRun变量会在子线程和主线程(比如onDestroy可能修改它)同时访问,建议用volatile修饰,保证多线程下的变量可见性:
    volatile boolean shouldRun = true;
    
  2. 优雅的线程退出逻辑:在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和崩溃的关键,加上volatileonDestroy里的停止逻辑,能让你的Service和线程运行更稳定。

内容的提问来源于stack exchange,提问作者Sriram R

火山引擎 最新活动