Android 向停止的服务传递意图
Android delivers intent to stopped service
我有一项服务根据 onStartCommand
中的 intent
调度一些 Completable
,如果所有启动 Completable
完成,则调用 stopSelf
。
我还 dispose
onDestroy
中所有未完成的,以清理所有内容。
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;
@Override
public void onCreate() {
super.onCreate();
pendingCommands = new SparseArray<>();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
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());
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);
}
代码似乎非常安全和干净。从概念上讲,它的工作方式类似于 IntentService
,但采用 RxJava
方式,因此它不是单线程的。
但是,我观察到一个奇怪的行为:就在调用 stopSelf
之后和 onDestroy
之前,android 向这个要停止的服务传递了另一个意图。它按预期分派了另一个 Completable
,但由于 onDestroy
由于 stopSelf
而被调用,所以最近的 Completable
在完成之前被释放。
我有 ProfileService
用以下日志扩展 RxIntentService
:
D/RxIntentService: onStartCommand: ProfileService, 1
D/RxIntentService: onStartCommand: ProfileService, 2
D/RxIntentService: commandCompleted: ProfileService, 1
D/RxIntentService: commandCompleted: ProfileService, 2
D/RxIntentService: commandCompleted: will stop self
D/RxIntentService: onStartCommand: ProfileService, 3
D/RxIntentService: onDestroy: ProfileService
如何防止这种行为?我可以检查 onStartCommand
它是否即将被销毁,但是我该如何处理这个特定的意图?
看来我需要使用 stopSelfResult(startId)
而不是 stopSelf()
,如 here:
所述
Stop the service if the most recent time it was started was startId. This is the same as calling stopService(Intent) for this particular service but allows you to safely avoid stopping if there is a start request from a client that you haven't yet seen in onStart(Intent, int).
它直接解决了我关于 onStartCommand
中未观察到的意图的问题。
我有一项服务根据 onStartCommand
中的 intent
调度一些 Completable
,如果所有启动 Completable
完成,则调用 stopSelf
。
我还 dispose
onDestroy
中所有未完成的,以清理所有内容。
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;
@Override
public void onCreate() {
super.onCreate();
pendingCommands = new SparseArray<>();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
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());
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);
}
代码似乎非常安全和干净。从概念上讲,它的工作方式类似于 IntentService
,但采用 RxJava
方式,因此它不是单线程的。
但是,我观察到一个奇怪的行为:就在调用 stopSelf
之后和 onDestroy
之前,android 向这个要停止的服务传递了另一个意图。它按预期分派了另一个 Completable
,但由于 onDestroy
由于 stopSelf
而被调用,所以最近的 Completable
在完成之前被释放。
我有 ProfileService
用以下日志扩展 RxIntentService
:
D/RxIntentService: onStartCommand: ProfileService, 1
D/RxIntentService: onStartCommand: ProfileService, 2
D/RxIntentService: commandCompleted: ProfileService, 1
D/RxIntentService: commandCompleted: ProfileService, 2
D/RxIntentService: commandCompleted: will stop self
D/RxIntentService: onStartCommand: ProfileService, 3
D/RxIntentService: onDestroy: ProfileService
如何防止这种行为?我可以检查 onStartCommand
它是否即将被销毁,但是我该如何处理这个特定的意图?
看来我需要使用 stopSelfResult(startId)
而不是 stopSelf()
,如 here:
Stop the service if the most recent time it was started was startId. This is the same as calling stopService(Intent) for this particular service but allows you to safely avoid stopping if there is a start request from a client that you haven't yet seen in onStart(Intent, int).
它直接解决了我关于 onStartCommand
中未观察到的意图的问题。