Android向待停止服务传Intent致RxJava任务销毁的解决问询
这个问题其实是Android Service生命周期和系统任务调度的典型冲突——当你调用stopSelf()后,系统并不会立刻触发onDestroy(),这段间隙里如果还有待处理的Intent,系统依然会分发给你的服务,但此时服务已经进入销毁流程,新任务刚启动就会被onDestroy()里的dispose()干掉,根本无法完成。
核心原因分析
从你的日志就能清晰看到这个时序问题:
D/RxIntentService: commandCompleted: will stop self
D/RxIntentService: onStartCommand: ProfileService, 3
D/RxIntentService: onDestroy: ProfileService
调用stopSelf()后,系统先把队列里剩下的Intent(startId=3)分发给服务,然后才执行onDestroy()。这时候新创建的Completable还没来得及执行,就被销毁逻辑强制终止了。
解决方案:增加销毁状态拦截
我们可以通过一个线程安全的标志位来标记服务是否正在销毁,在onStartCommand()里先检查这个标志,拦截掉销毁过程中的新Intent,避免无效任务的创建和资源浪费。
步骤1:添加销毁状态标志
在RxIntentService类中添加一个volatile修饰的布尔变量,确保多线程下的状态可见性:
private volatile boolean isDestroying = false;
步骤2:在onDestroy中标记销毁状态
修改onDestroy()方法,先把标志位设为true,再执行清理逻辑:
@Override public void onDestroy() { Log.d(TAG, "onDestroy: " + getClass().getSimpleName()); // 先标记服务进入销毁流程 isDestroying = true; // 原有清理逻辑 for (int i = 0; i < pendingCommands.size(); i++) { pendingCommands.valueAt(i).dispose(); } pendingCommands.clear(); pendingCommands = null; super.onDestroy(); }
步骤3:在onStartCommand中拦截销毁中的Intent
在处理Intent前先检查isDestroying标志,如果服务正在销毁,直接返回START_NOT_STICKY(或者根据需求重新投递Intent):
@Override public int onStartCommand(Intent intent, int flags, int startId) { // 拦截销毁过程中的新Intent if (isDestroying) { Log.w(TAG, "Ignoring intent - service is destroying: " + getClass().getSimpleName() + ", startId: " + startId); // 可选:如果希望这个Intent能被重新处理,在这里调用startService(intent) // 系统会启动一个新的服务实例来处理它 // if (intent != null) { // startService(intent); // } return START_NOT_STICKY; } Log.d(TAG, "onStartCommand: " + getClass().getSimpleName() + ", " + startId); // 原有处理逻辑... }
修改后的完整RxIntentService代码
import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; import android.util.SparseArray; import androidx.annotation.Nullable; import io.reactivex.Completable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; public abstract class RxIntentService extends Service { private static final String TAG = "RxIntentService"; private SparseArray<Disposable> pendingCommands; private volatile boolean isDestroying = false; // 新增销毁状态标志 @Override public void onCreate() { super.onCreate(); pendingCommands = new SparseArray<>(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // 拦截销毁过程中的新Intent if (isDestroying) { Log.w(TAG, "Ignoring intent - service is destroying: " + getClass().getSimpleName() + ", startId: " + startId); // 可选:重新投递Intent到新服务实例 // if (intent != null) { // startService(intent); // } return START_NOT_STICKY; } Log.d(TAG, "onStartCommand: " + getClass().getSimpleName() + ", " + startId); Completable completable = handleIntent(intent); if (completable == null) { commandCompleted(startId); return START_NOT_STICKY; } else { pendingCommands.append(startId, completable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( () -> commandCompleted(startId), throwable -> { Log.w(TAG, getClass().getSimpleName() + " command failed: " + (intent == null ? "intent is null" : intent.getAction()), throwable); commandCompleted(startId); }) ); return START_REDELIVER_INTENT; } } private void commandCompleted(int startId) { Log.d(TAG, "commandCompleted: " + getClass().getSimpleName() + ", " + startId); pendingCommands.remove(startId); if (pendingCommands.size() == 0) { Log.d(TAG, "commandCompleted: will stop self"); stopSelf(); } } @Override public void onDestroy() { Log.d(TAG, "onDestroy: " + getClass().getSimpleName()); isDestroying = true; // 标记服务进入销毁流程 for (int i = 0; i < pendingCommands.size(); i++) { pendingCommands.valueAt(i).dispose(); } pendingCommands.clear(); pendingCommands = null; super.onDestroy(); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } protected abstract Completable handleIntent(Intent intent); }
额外说明
- 如果你希望被拦截的Intent能正常执行,可以在拦截分支里调用
startService(intent),系统会启动一个新的RxIntentService实例处理该任务。 volatile修饰符确保isDestroying的状态变化能被所有线程立刻感知,避免线程安全问题。
内容的提问来源于stack exchange,提问作者ram

