Урок 93. Service. Передача данных в сервис. Методы остановки сервиса

Обсуждение уроков
Ответить
Аватара пользователя
damager82
Администратор
Сообщения: 1383
Зарегистрирован: 07 янв 2012, 11:32
Контактная информация:

Урок 93. Service. Передача данных в сервис. Методы остановки сервиса

Сообщение damager82 » 04 июл 2012, 23:00

В этом уроке:
- передаем данные в сервис
- рассматриваем методы остановки сервиса stopSelf и stopSelfResult


Click here to read this article!
Последний раз редактировалось damager82 22 май 2017, 23:42, всего редактировалось 4 раза.
Добро пожаловать на форум сайта StartAndroid
ИзображениеИзображение

brucemax
Сообщения: 117
Зарегистрирован: 01 апр 2012, 16:09
Откуда: Минск
Контактная информация:

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение brucemax » 29 окт 2012, 23:25

Несмотря на обилие нового материла (чего стоит только экзэкутер), всё классно объяснено. Делаю сейчас приложение, основным элементом которого будет сервис, который запускается из главного активити и через заданный (опять же в активити) интервал спрашивает координаты у геолокационного сервиса и отсылает их гет-запросом на сервер (естественно не в потоке активити). Наверное неделю уже курю тему Сервисы на разных сайтах и никак не могу определиться по некоторым вопросам ( где лучше создавать слушатель координат, с помощью чего удобнее и рациональнее общаться с сервисом (хэндлы,биндинг или AIDL )?) Вы не наставите на путь истинный? Или возможно я поспешил и после следующих уроков мне всё станет ясно.. тогда прошу простить за нетерпение.

Аватара пользователя
rezak90
Сообщения: 3422
Зарегистрирован: 26 июн 2012, 13:22
Откуда: UA
Контактная информация:

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение rezak90 » 29 окт 2012, 23:55

на мой взгляд проще и удобней общаться при помощи AIDL, давно с ним не работал, но в проектах активно его используем.
R.id.team
Политика на форуме запрещена

brucemax
Сообщения: 117
Зарегистрирован: 01 апр 2012, 16:09
Откуда: Минск
Контактная информация:

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение brucemax » 30 окт 2012, 19:59

rezak90 писал(а):на мой взгляд проще и удобней общаться при помощи AIDL, давно с ним не работал, но в проектах активно его используем.
Спасибо! Интересно в уроках он есть..? Ладно так или иначе буду уроки смотреть здесь.

Аватара пользователя
damager82
Администратор
Сообщения: 1383
Зарегистрирован: 07 янв 2012, 11:32
Контактная информация:

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение damager82 » 31 окт 2012, 09:45

brucemax писал(а):
rezak90 писал(а):на мой взгляд проще и удобней общаться при помощи AIDL, давно с ним не работал, но в проектах активно его используем.
Спасибо! Интересно в уроках он есть..? Ладно так или иначе буду уроки смотреть здесь.
Не, в уроках только локальный биндинг. AIDL чуть сложнее,но смысл тот же.
Добро пожаловать на форум сайта StartAndroid
ИзображениеИзображение

dubok79
Сообщения: 12
Зарегистрирован: 24 ноя 2012, 13:41

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение dubok79 » 02 дек 2012, 12:04

Уроки все прочитал по сервисам, но пишу тут, потому что тут раскрывается моя проблема.
А все-таки как решать задачу, когда последний сервис сработав раньше первого более долгоиграющего убьет сервис и что делать? Никак не могу понять.

Если я в сервисе стартую таймер на разовое выполнение, мне сервис нужно убивать сразу (он по сути мне не нужен более) или после срабатывания таймера? Спасибо

brucemax
Сообщения: 117
Зарегистрирован: 01 апр 2012, 16:09
Откуда: Минск
Контактная информация:

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение brucemax » 03 дек 2012, 09:37

dubok79 писал(а):Уроки все прочитал по сервисам, но пишу тут, потому что тут раскрывается моя проблема.
А все-таки как решать задачу, когда последний сервис сработав раньше первого более долгоиграющего убьет сервис и что делать? Никак не могу понять.
Использовать binding.
Если я в сервисе стартую таймер на разовое выполнение, мне сервис нужно убивать сразу (он по сути мне не нужен более) или после срабатывания таймера? Спасибо
Первое что приходит, что лучше оставить до завершения таймера, ибо это будет гарантом, что система ничего не удалит.. Но я могу ошибаться.

Аватара пользователя
kondra007
Сообщения: 91
Зарегистрирован: 23 янв 2013, 14:49

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение kondra007 » 30 мар 2013, 20:16

Друзья! Не убивается уведомление =( Ставил везде, где мог stopService(...) - не работает.
Смотрите код, пожалуйста.

public class MyService extends Service {
NotificationManager nm;
ExecutorService es;

final String TAG = "ServiceLog";

public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
es = Executors.newFixedThreadPool(1);
}

public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
Log.d(TAG, "startID = " + startId);
srv s = new srv(startId);
es.execute(s);
new Thread(s).start();
sendNotif();

return START_STICKY;
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}


public void onDestroy() {
super.onDestroy();
// stopSelf();
stopService(new Intent(this, MyService.class));
Log.d(TAG, "onDestroy");
}


@SuppressLint("NewApi")
void sendNotif() {

Intent in = new Intent(this, MainActivity.class);
PendingIntent pin = PendingIntent.getActivity(this, 0, in, 0);
Notification notif = new Notification.Builder(this)
.setContentTitle("Программа запущена")
.setContentText("Нажмите, чтобы открыть приложение")
.setSmallIcon(R.drawable.ic_launcher)
.addAction(0, "Открыть приложение", pin)
.build();


// ставим флаг, чтобы уведомление пропало после нажатия
notif.flags |= Notification.FLAG_ONGOING_EVENT;

// отправляем
nm.notify(1, notif);
}


public class srv implements Runnable {
final String TAG = "SrvLog";
int startID;

public srv(int startID){
this.startID = startID;
}

public void run() {
Log.d(TAG, "Service started with id = " + startID);
stop();
}

public void stop() {
stopSelf(startID);

}
}
}

JaguaR
Сообщения: 3
Зарегистрирован: 08 июл 2013, 14:12

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение JaguaR » 09 июл 2013, 14:03

Привет всем!

Есть Service, в onStartCommand возвращаю START_REDELIVER_INTENT для восстановления незавершенных вызовов, сохраняя Intent. В onStartCommand создаю новый поток и запихиваю его в пул ExecutorService для поочередного выполнения (executor = Executors.newFixedThreadPool(1)), сохраняя startId в классе потока. По завершению работы потока (в run()) вызываю stopSelfResult(startId). Поток знает свой startId.

Из Activity делаю 2 вызова запуска сервиса, соответственно в сервисе 2 раза срабатывает onStartCommand и создает 2 потока с startId 1 и 2. По завершению первого потока (startId = 1) stopselfResult = false, по завершению второго потока stopselfResult = true, т.к. startId=2 является последним вызовом и сервис останавливается. Все ок.

Проблема в следующем :
Ничего не меняю в вызовах. Специально убиваю приложение сразу после получения двух вызовов onStartCommand и запуска первого потока (startId = 1). Жду когда сервис восстановится и восстановит незавершенные вызовы.. так и происходит.. Сервис создается (onCreate()), 2 раза срабатывает onStartCommand (на входе имею startId = 1 и startId = 2 и восстановленные Intent-ы), создаются потоки и отсылаются на выполнение в ExecutorService. По завершению первого потока (startId = 1) stopselfResult = false, по завершению второго потока (startId = 2)... внимание... stopselfResult = false... и соответственно сервис НЕ ОСТАНАВЛИВАЕТСЯ.. Как так?? Сервис останавливает только принудительный вызов stopselfResult(3).... Но откуда взялся третий вызов onStartCommand ? StartId были только 1 и 2.

Друзья, подскажите, почему так происходит..

Аватара пользователя
Isaev
Сообщения: 145
Зарегистрирован: 03 сен 2013, 09:39
Откуда: Германия
Контактная информация:

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение Isaev » 06 ноя 2013, 14:56

Т.е. мы в данном примере не имеем никакой возможности "сказать" чтобы при вызове второго stopSelf сервис не убивался?
Почему нельзя проверять счётчик и отменять stop?

Аватара пользователя
trew
Сообщения: 450
Зарегистрирован: 28 сен 2013, 17:34

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение trew » 05 дек 2013, 19:21

Поэкспериментировал с кодом, сделал чтобы сервис не убивался.
[syntax=java]import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {

final String TAG = "mylog";
ExecutorService es;
Object someRes;
ArrayList<Integer> userPool;
ArrayList<Integer> userPoolFinish;

public void onCreate() {
super.onCreate();
Log.d(TAG, "MyService onCreate");
es = Executors.newFixedThreadPool(3);
someRes = new Object();

userPool = new ArrayList<Integer>();
userPoolFinish = new ArrayList<Integer>();
}

public void onDestroy() {
super.onDestroy();
Log.d(TAG, "MyService onDestroy");
someRes = null;
}

public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "MyService onStartCommand");

userPool.add(startId);

int time = intent.getIntExtra("time", 1);
MyRun mr = new MyRun(time, startId);
es.execute(mr);
return super.onStartCommand(intent, flags, startId);
}

public IBinder onBind(Intent arg0) {
return null;
}

class MyRun implements Runnable {

int time;
int startId;

public MyRun(int time, int startId) {
this.time = time;
this.startId = startId;
Log.d(TAG, "MyRun#" + startId + " create");
}

public void run() {
Log.d(TAG, "MyRun#" + startId + " start, time = " + time);
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Log.d(TAG,
"MyRun#" + startId + " someRes = " + someRes.getClass());
} catch (NullPointerException e) {
Log.d(TAG, "MyRun#" + startId + " error, null pointer");
}
stop();
}

void stop() {
//Log.d(TAG, "MyRun#" + startId + " end, stopSelfResult("
// + startId + ") = " + stopSelfResult(startId));

userPoolFinish.add(startId);
Log.d(TAG, "startId= " + startId);

if (userPool.size() == userPoolFinish.size())
{
for (int i = 0 ; i < userPool.size(); i++) {

Log.d(TAG, "userPool.get(i): " + userPool.get(i));
Log.d(TAG, "MyRun#" + userPool.get(i) + " end, stopSelf(" + userPool.get(i)
+ ")");

stopSelf(userPool.get(i));

Log.d(TAG, "MyRun ------ " + userPool.get(i) + " end, stopSelfResult("
+ userPool.get(i) + ") = " + stopSelfResult(userPool.get(i)));
}

Log.d(TAG, "stopSelfResult(1) = " + stopSelfResult(1));
Log.d(TAG, "stopSelfResult(2) = " + stopSelfResult(2));
Log.d(TAG, "stopSelfResult(3) = " + stopSelfResult(3));
}
}
}
}[/syntax]

Лог
[syntax=xml]12-05 16:09:07.721: D/mylog(985): MyService onCreate
12-05 16:09:07.730: D/mylog(985): MyService onStartCommand
12-05 16:09:07.730: D/mylog(985): MyRun#1 create
12-05 16:09:07.741: D/mylog(985): MyService onStartCommand
12-05 16:09:07.741: D/mylog(985): MyRun#2 create
12-05 16:09:07.741: D/mylog(985): MyRun#1 start, time = 7
12-05 16:09:07.741: D/mylog(985): MyService onStartCommand
12-05 16:09:07.752: D/mylog(985): MyRun#3 create
12-05 16:09:07.752: D/mylog(985): MyRun#2 start, time = 2
12-05 16:09:07.761: D/mylog(985): MyRun#3 start, time = 4
12-05 16:09:09.784: D/mylog(985): MyRun#2 someRes = class java.lang.Object
12-05 16:09:09.784: D/mylog(985): startId= 2
12-05 16:09:11.791: D/mylog(985): MyRun#3 someRes = class java.lang.Object
12-05 16:09:11.791: D/mylog(985): startId= 3
12-05 16:09:14.766: D/mylog(985): MyRun#1 someRes = class java.lang.Object
12-05 16:09:14.766: D/mylog(985): startId= 1
12-05 16:09:14.766: D/mylog(985): userPool.get(i): 1
12-05 16:09:14.766: D/mylog(985): MyRun#1 end, stopSelf(1)
12-05 16:09:14.784: D/mylog(985): MyRun ------ 1 end, stopSelfResult(1) = false
12-05 16:09:14.784: D/mylog(985): userPool.get(i): 2
12-05 16:09:14.784: D/mylog(985): MyRun#2 end, stopSelf(2)
12-05 16:09:14.784: D/mylog(985): MyRun ------ 2 end, stopSelfResult(2) = false
12-05 16:09:14.791: D/mylog(985): userPool.get(i): 3
12-05 16:09:14.791: D/mylog(985): MyRun#3 end, stopSelf(3)
12-05 16:09:14.791: D/mylog(985): MyService onDestroy
12-05 16:09:14.801: D/mylog(985): MyRun ------ 3 end, stopSelfResult(3) = false
12-05 16:09:14.801: D/mylog(985): stopSelfResult(1) = false
12-05 16:09:14.801: D/mylog(985): stopSelfResult(2) = false
12-05 16:09:14.801: D/mylog(985): stopSelfResult(3) = false[/syntax]
Видим, что stopSelfResult() вернул false, для всех потоков.
Кто остановил сервис?
Когда выкладываете код на форум - код оформляйте. Редактор - поищите слова Geshi Syntax -Java. (или xml)
Свои сообщения можно редактировать - кнопка edit.

kot_droid
Сообщения: 1
Зарегистрирован: 30 май 2014, 17:20

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение kot_droid » 30 май 2014, 17:42

штука смотрю совершенно не предсказуемая, вот еще пример:)
создаю сервис и запускаю на 30 секунд, во время работы делаю stopService - сервис продолжает работать.
закрываю активити - сервис работает, но даже после того как отработал не уничтожился.
опять открываю активити, запускаю сервис, делаю stopService и выхожу из активити
сервис как и в прошлый рас работает
запускаю опять активити, запускаю сервис и у меня уже 2 сервиса спамят лог:)
в общем успешно уничтожился, только когда дождался как он отработает и после этого сделал stopService

Stormer
Сообщения: 39
Зарегистрирован: 13 сен 2013, 20:18

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение Stormer » 21 июл 2014, 12:51

Это как в Java, если запустить поток с while(true), то interrupt его ни фига не прервет. Кстати, кто знает, как остановить такой поток?

Простите за тупую аналогию))

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение Mikhail_dev » 21 июл 2014, 19:51

interrupt - это флаг потоку, который надо проверять и думать, когда остановить поток. Раньше был stop, но останавливать в произвольный момент времени поток не есть хорошо, поэтому придумали этот флаг. Каждый раз при начале цикла, либо логической цепочки, можно проверять данный флаг.
Еще как вариант, бросать return false внутри цикла и выходить с него.
еще как вариант, если используется Thread.sleep, то тот де вроде interrupt, Либо join (не помню). Тогда вылетет исключение и поток завершится. То, что вылетет исключение - это нормально в данном случае.

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение Mikhail_dev » 21 июл 2014, 19:53

штука смотрю совершенно не предсказуемая, вот еще пример:)
создаю сервис и запускаю на 30 секунд, во время работы делаю stopService - сервис продолжает работать.
закрываю активити - сервис работает, но даже после того как отработал не уничтожился.
опять открываю активити, запускаю сервис, делаю stopService и выхожу из активити
сервис как и в прошлый рас работает
запускаю опять активити, запускаю сервис и у меня уже 2 сервиса спамят лог:)
в общем успешно уничтожился, только когда дождался как он отработает и после этого сделал stopService
У вас утечка. Почитайте данную статью - http://habrahabr.ru/post/222199/

Аватара пользователя
Nialon
Сообщения: 22
Зарегистрирован: 12 ноя 2013, 19:19

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение Nialon » 28 янв 2015, 16:58

Стало любопытно. Идея с вводом счетчика для отлова потоков правильная,
только мне не нужен НЕ убиваемый сервис как у пользователя выше.
Вот мой вариант кода полностью годный для примера. Там много тэгов для разностороннего учета.

Код: Выделить всё

public class MyService extends Service {

    final String TAG = "mylog";
    ExecutorService es;
    Object someRes;
    int EndId = 0;
    int WorkCount = 0;
    Boolean OnStop = false;

    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "MyService onCreate");
        es = Executors.newFixedThreadPool(3);
        someRes = new Object();
    }

    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "MyService onDestroy");
        OnStop = true;
        someRes = null;
    }

    public int onStartCommand(Intent intent, int flags, int startId) {

        EndId++;
        WorkCount += startId;
        Log.d(TAG, "MyService onStartCommand");
        int time = intent.getIntExtra("time", 1);
        MyRun mr = new MyRun(time, startId);
        es.execute(mr);
        return super.onStartCommand(intent, flags, startId);
    }

    public IBinder onBind(Intent arg0) {
        return null;
    }

    class MyRun implements Runnable {

        int time;
        int startId;

        public MyRun(int time, int startId) {
            this.time = time;
            this.startId = startId;
            Log.d(TAG, "MyRun#" + startId + " create");
        }

        public void run() {

            if (OnStop) {
                Log.d(TAG, "MyRun#" + startId + " start, service destroyed, Exit.");
                return;
            }

            Log.d(TAG, "MyRun#" + startId + " start, time = " + time);

            try { TimeUnit.SECONDS.sleep(time); }
            catch (InterruptedException e) { e.printStackTrace(); }

            if (OnStop) {
                Log.d(TAG, "MyRun#" + startId + " after SLEEP, service destroyed, Exit.");
                return;
            }

            try {Log.d(TAG,"MyRun#" + startId + " someRes = " + someRes.getClass());
            } catch (NullPointerException e) { Log.d(TAG, "MyRun#" + startId + " error, null pointer"); }

            stop();
        }

        void stop() {

            if (OnStop) {
                Log.d(TAG, "MyRun#" + startId + " end, + service destroyed..");
            } else
            if (WorkCount == startId) {
                Log.d(TAG, "MyRun#" + startId + " last end, stopSelfResult("+ EndId + ") = " + stopSelfResult(EndId));
            } else {
                WorkCount -= startId;
                Log.d(TAG, "MyRun#" + startId + " end, Wait for stop previus");
            }
        }

    }
}
Самое забавное если не занулять объект в блоке уничтожения сервиса, он доступен после. Как-то неправильно...

Аватара пользователя
Kirill
Сообщения: 19
Зарегистрирован: 09 сен 2015, 13:53

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение Kirill » 21 сен 2015, 16:52

Nialon писал(а): int WorkCount = 0;

WorkCount += startId;
1. странно видеть имена переменных с большой буквы
2. WorkCount увеличивается не на +1, а на значение startId, то есть +1 +2 +3, выглядит подозрительно

danek130995
Сообщения: 42
Зарегистрирован: 25 янв 2015, 18:57

Re: Урок 93. Service. Передача данных в сервис. Методы остан

Сообщение danek130995 » 15 дек 2017, 12:10

К слову о том, почему автор пишет, что "Но на самом деле алгоритм чуть другой. Сервис останавливается, когда последний полученный (а не последний обработанный) вызов выполняет метод stopSelf(startId). А при этом могут продолжать работать ранее полученные вызовы. Почему так сделано – я не знаю."

Согласно официальным докам(https://developer.android.com/guide/com ... l#Stopping),
"If your service handles multiple requests to onStartCommand() concurrently, you shouldn't stop the service when you're done processing a start request, as you might have received a new start request (stopping at the end of the first request would terminate the second one). To avoid this problem, you can use stopSelf(int) to ensure that your request to stop the service is always based on the most recent start request. That is, when you call stopSelf(int), you pass the ID of the start request (the startId delivered to onStartCommand()) to which your stop request corresponds. Then, if the service receives a new start request before you are able to call stopSelf(int), the ID does not match and the service does not stop."

"Если ваш сервис обрабатывает множество запросов одновременно (onStartCommand()), вам не следует останавливать сервис, когда завершилась обработка запроса, так как сервис может принять новый запрос (остановка сервиса в конце первого запроса завершит и второй). Чтобы избежать этого, можно использовать stopSelf(int) чтобы удостовериться, что ваш запрос на остановку сервиса всегда базируется на самом последнем запросе старта. Поэтому, когда вы вызываете метод stopSelf(int), вы передаете id запроса старта(которое потом попадает в onStartCommand(), к которому относится ваш запрос на остановку. Тогда, если сервис получит новый запрос на старт перед тем как у вас есть возможность вызвать stopSelft(int), ID не совпадет и сервис не остановится)."
Надеюсь, кому-то помог.

Rolik
Сообщения: 14
Зарегистрирован: 05 апр 2021, 06:42

Re: Урок 93. Service. Передача данных в сервис. Методы остановки сервиса

Сообщение Rolik » 11 сен 2021, 11:49

Не смог заменит логи на тосты и вывести в UI, жаль...

Ответить