Продолжить обслуживание, даже если приложение удалено из приложения Недавние
У меня небольшая проблема.
В моем приложении служба запускается после успешного входа пользователя. Ранее сервис необходимо было остановить, если приложение было убито. (скажем, удалили из списка Недавние приложения, проведя пальцем.) Таким образом, мы использовали android:stopWithTask="true"
, Теперь нам нужно, чтобы Служба работала как есть, даже если Задача, которая ее запустила, удаляется из списка Недавние приложения. Поэтому я изменил Сервис, чтобы включить android:stopWithTask="false"
, Но это не похоже на работу.
Связанный код:
Вот часть манифеста, относящаяся к Сервису:
<service
android:enabled="true"
android:name=".MyService"
android:exported="false"
android:stopWithTask="false" />
В MyService.java:
public class MyService extends AbstractService {
@Override
public void onStartService() {
Intent intent = new Intent(this, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new Notification(R.drawable.ic_launcher, "My network services", System.currentTimeMillis());
notification.setLatestEventInfo(this, "AppName", "Message", pendingIntent);
startForeground(MY_NOTIFICATION_ID, notification);
}
@Override
public void onTaskRemoved(Intent rootIntent) {
Toast.makeText(getApplicationContext(), "onTaskRemoved called", Toast.LENGTH_LONG).show();
System.out.println("onTaskRemoved called");
super.onTaskRemoved(rootIntent);
}
}
AbstractService.java - это пользовательский класс, который расширяет Sevrice
:
public abstract class AbstractService extends Service {
protected final String TAG = this.getClass().getName();
@Override
public void onCreate() {
super.onCreate();
onStartService();
Log.i(TAG, "onCreate(): Service Started.");
}
@Override
public final int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStarCommand(): Received id " + startId + ": " + intent);
return START_STICKY; // run until explicitly stopped.
}
@Override
public final IBinder onBind(Intent intent) {
return m_messenger.getBinder();
}
@Override
public void onDestroy() {
super.onDestroy();
onStopService();
Log.i(TAG, "Service Stopped.");
}
public abstract void onStartService();
public abstract void onStopService();
public abstract void onReceiveMessage(Message msg);
@Override
public void onTaskRemoved(Intent rootIntent) {
Toast.makeText(getApplicationContext(), "AS onTaskRemoved called", Toast.LENGTH_LONG).show();
super.onTaskRemoved(rootIntent);
}
}
Теперь, если я вхожу в приложение, MyService запускается. После этого я нажимаю кнопку "Домой", чтобы приложение переместилось в фоновый режим. Теперь я удаляю приложение из списка последних приложений. В то время я должен увидеть сообщение Toast и console, как описано в этом методе:
public void onTaskRemoved (намерение rootIntent)
Добавлено в API уровень 14
Это вызывается, если служба в данный момент работает, и пользователь удалил задачу, которая поступает из приложения службы. Если вы установили ServiceInfo.FLAG_STOP_WITH_TASK, вы не получите этот обратный вызов; вместо этого служба будет просто остановлена.
Параметры rootIntent Исходное корневое намерение, которое использовалось для запуска удаляемой задачи.
Но я не вижу ничего из этого. Служба возвращается START_STICKY в onStartCommand
, Я так думаю onTaskRemoved
должен быть уволен вместе с флагом android:stopWithTask="false"
,
Я что-то пропустил?
Дайте мне знать, если мне нужно добавить код, который может быть важен, чтобы выяснить, в чем дело.
PS: я проверял это на 4.2.2 до сих пор.
PS: я только что протестировал тот же код в 4.1.2, на котором служба продолжает работать, и я получаю сообщение "onTaskRemoved named" в журнале тоже.
Что я должен сделать, чтобы это работало во всех версиях?
5 ответов
Просто следуйте этим сценариям, ваш сервис и процессы (потоки внутри вашего сервиса) будут оставаться непрерывными.
Создайте сервис и используйте START_STICKY в качестве возвращаемого значения в методе onStartCommand, как показано ниже
@Override public int onStartCommand(final Intent intent, final int flags, final int startId) { //your code return START_STICKY; }
Приведенный выше код перезапустит службу, если она будет уничтожена и всегда останется запущенной, но процесс (потоки), запущенный из службы, перестанет работать, если ваше приложение будет удалено из последних приложений. Чтобы ваши процессы (потоки) всегда оставались в рабочем состоянии, вы должны переопределить метод onTaskRemoved() и добавить код для перезапуска задач, как показано ниже.
@Override public void onTaskRemoved(Intent rootIntent){ Intent restartServiceTask = new Intent(getApplicationContext(),this.getClass()); restartServiceTask.setPackage(getPackageName()); PendingIntent restartPendingIntent =PendingIntent.getService(getApplicationContext(), 1,restartServiceTask, PendingIntent.FLAG_ONE_SHOT); AlarmManager myAlarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE); myAlarmService.set( AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, restartPendingIntent); super.onTaskRemoved(rootIntent); }
- Запустите сервис, как показано ниже
startService(новый Intent(это, YourService.class));
В вашем сервисе добавьте следующий код. Это нормально работает для меня в 4.4.2
Вот обходной путь, с которым я столкнулся и хорошо работающий для перезапуска службы, если ее процесс прерывается при закрытии приложения.
@Override public void onTaskRemoved(Intent rootIntent){
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
PendingIntent restartServicePendingIntent = PendingIntent.getService(
getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmService.set(ELAPSED_REALTIME, elapsedRealtime() + 1000,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
Если вы привязываетесь к своему сервису из подкласса класса Application и удерживаете соединение IBinder, сервис останется активным даже после удаления приложения из последних приложений.
Если можно разместить уведомление во время работы службы, вы можете использовать startForegroundService и startForeground для этого.
Есть три важных приема:
- Вызовите startForegroundService, который создает долго работающую службу, не ограниченную привязанным контекстом, и обещает вызвать startForeground позже.
- Верните START_STICKY в onStartComand
- Вызовите startForeground с уведомлением, как обещано в (1).
Например, если вы хотите запустить TimerService, в своем TimerActivity вы сделаете:
private var timerService: TimerService? = null
private val timerServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as TimerService.Binder
timerService = binder.getService()
}
override fun onServiceDisconnected(arg0: ComponentName) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
...
startButton.setOnClickListener {
timerService?.startTimer(60L, 0L)
}
}
override fun onStart() {
super.onStart()
Intent(this, TimerService::class.java).also {
ContextCompat.startForegroundService(this, it) // that's the first trick
bindService(it, timerServiceConnection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
unbindService(timerServiceConnection)
timerService?.updateNotification(secondsRemaining)
}
Ваш TimerService будет примерно таким:
class TimerService : Service() {
private val binder = Binder()
private var serviceLooper: Looper? = null
private var serviceHandler: ServiceHandler? = null
private var timer: CountDownTimer? = null
private val notificationUtil by lazy {
NotificationUtil(this)
}
override fun onCreate() {
HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
start()
serviceLooper = looper
serviceHandler = ServiceHandler(looper)
}
}
override fun onBind(intent: Intent?): IBinder? = binder
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val timerRemaining = intent?.getLongExtra(EXTRA_REMAINING, 0) ?: 0L
if (timerRemaining != 0L) {
serviceHandler?.obtainMessage()?.also { msg ->
msg.arg1 = startId
msg.data.putLong(EXTRA_REMAINING, timerRemaining)
serviceHandler?.sendMessage(msg)
}
}
return START_STICKY // that's the second trick
}
override fun onDestroy() {
super.onDestroy()
timer?.cancel()
}
fun startTimer(secondsRemaining: Long, id: Long) {
updateNotification(secondsRemaining)
Intent(this, TimerService::class.java).apply {
putExtra(EXTRA_REMAINING, secondsRemaining)
}.also {
onStartCommand(it, 0, id.toInt())
}
}
fun stopTimer() {
timer?.cancel()
}
fun updateNotification(secondsRemaining: Long){
val notification = NotificationCompat.Builder(this, NotificationUtil.CHANNEL_ID_TIMER)
.setSmallIcon(R.drawable.ic_timer)
.setAutoCancel(true)
.setDefaults(0)
.setContentTitle(secondsRemaining.formatSeconds())
.setContentText("Timer")
.setContentIntent(notificationUtil.getPendingIntentWithStack(this, TimerActivity::class.java))
.setOngoing(true)
.build()
startForeground(NotificationUtil.NOTIFICATION_ID, notification) // that's the last trick
}
private fun sendMessage(remaining: Long) {
Intent(TimerService::class.java.simpleName).apply {
putExtra(EXTRA_REMAINING, remaining)
}.also {
LocalBroadcastManager.getInstance(this).sendBroadcast(it)
}
}
private inner class ServiceHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
val secondsRemaining = msg.data.getLong(EXTRA_REMAINING)
notificationUtil.showTimerStarted(secondsRemaining)
timer = object : CountDownTimer(secondsRemaining * 1000, 1000) {
override fun onTick(millisUntilFinished: Long) {
Log.i(this::class.java.simpleName, "tick ${(millisUntilFinished / 1000L).formatSeconds()}")
updateNotification(millisUntilFinished / 1000)
sendMessage(millisUntilFinished / 1000)
}
override fun onFinish() {
Log.i(this::class.java.simpleName, "finish")
notificationUtil.showTimerEnded()
sendMessage(0)
stopSelf()
}
}.start()
}
}
inner class Binder : android.os.Binder() {
// Return this instance of LocalService so clients can call public methods
fun getService(): TimerService = this@TimerService
}
companion object {
const val EXTRA_REMAINING = "EXTRA_REMAINING"
const val NOTIFICATION_ID = 1 // cannot be 0
fun Long.formatSeconds(): String {
val s = this % 60
val m = this / 60 % 60
val h = this / (60 * 60) % 24
return if (h > 0) String.format("%d:%02d:%02d", h, m, s)
else String.format("%02d:%02d", m, s)
}
}
}
Похоже, что удаление приложения из "недавних задач" убивает все, что к нему прикреплено.
Возможно, вам следует поискать способ перезапустить службу, если она остановится: /questions/40366702/fonovaya-sluzhba-ostanavlivaetsya-kogda-aktivnost-prekraschaetsya-ostanavlivaetsya-ili-prinuditelno-zakryivaetsya/40366709#40366709
Напишите 5 строк, которые я добавил в oncreate()
класса обслуживания
Как это:
public class AlarmService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Intent iHeartBeatService = new Intent(AlarmService.this,
AlarmService.class);
PendingIntent piHeartBeatService = PendingIntent.getService(this, 0,
iHeartBeatService, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(piHeartBeatService);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(), 1000, piHeartBeatService);
}
}
или же
Попробуй это
public class MyService extends Service{
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
System.out.println("service created");
}
@SuppressLint("NewApi")
@Override
public void onTaskRemoved(Intent rootIntent) {
// TODO Auto-generated method stub
System.out.println("onTaskRemoved");
super.onTaskRemoved(rootIntent);
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
System.out.println("Service started");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Service is running");
}
}, 5000);
}
}