Уведомления из сервиса, в котором крутится таймер

Ответить
Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Уведомления из сервиса, в котором крутится таймер

Сообщение powercat » 02 дек 2012, 19:09

Сервис работает, в нем есть таймер, в котором выполняются следующие действия: 1. Запись в базу 2. Запись в файл 3.Выдача уведомления
1 и 2 - работают нормально.
А с 3 - проблема - не хочет выдаваться уведомление из таймера, ругается на exception timer-0.
Поиск на стековерфлоу показал, что это (скорее всего) связанно с тем, что "из потока нельзя изменить UI"...это то, что мне удалось понять с буржуйского языка, хотя и непонятно, что это значит. Собственно два вопроса - что это за UI (пользовательский интерфейс, как я понял), который нельзя изменить (а разве уведомления = изменению)? И второй вопрос - как решить эту проблему?

Ранее уведомления выдавались нормально, правда, перед выдачей использовались таймер.пурж и таймер.кансел.... (но предполагалось их убрать)...

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

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение rezak90 » 02 дек 2012, 21:03

могу лишь сказать что ui поток это главный поток в котором запущенно приложение, по всему остальному у меня тёмный лес =)
R.id.team
Политика на форуме запрещена

Аватара пользователя
neoksi
Сообщения: 712
Зарегистрирован: 26 июл 2012, 10:42
Контактная информация:

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение neoksi » 03 дек 2012, 01:06

Через Хендлер сбрось в UI поток и оттуда уже выводи сообщение.

AndreyI
Сообщения: 372
Зарегистрирован: 14 май 2012, 16:18

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение AndreyI » 03 дек 2012, 03:57

powercat писал(а):Собственно два вопроса - что это за UI (пользовательский интерфейс, как я понял), который нельзя изменить (а разве уведомления = изменению)? И второй вопрос - как решить эту проблему?
UI-это основной поток в котором происходит прорисовка графического интерфейса. Его изменять можно (иначе какой смысл?) только нельзя это делать когда заблагорассудится из другого потока, который выполняется параллельно, вообще трудно определить в какой момент можно менять данные, которые используются в другом потоке, для этого и придумали всякие синхронизации. Представь UI поток прорисовывает какой-то TextView и уже вывел половину текста, а в этот момент другой поток меняет значение переменной, где хранится этот текст, что из этого выйдет? (Вы садитесь на стул, а в это момент ваш друг, в шутку, убирает этот стул :) ) Поэтому в Android и ввели полный запрет (исключения) на подобные действия и правильно сделали, в JAVA Swing подобного запрета нет, но если мы будем напрямую что-то менять в графических элементах из другого потока, поведение будет непредсказуемым и все может закончится крахом, т.е. типа мины замедленного действия, то вроде все нормально работает, то неожиданно будет падать (возможно уже у ваших пользователей).
И чтобы программист не заморачивался по поводу синхронизации своих потоков с потоками отвечающими за прорисовку UI интерфейса поступают просто - ставим свой код в очередь сообщений UI-потока через Handler и пусть сама система решает, когда ей будет удобно и безопасно его выполнить. Мы как-бы оставляем заявку на выполнение кода, а не заставляем систему выполнять код немедленно.

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение powercat » 03 дек 2012, 09:25

Понятно...а как узнать, что работают разные потоки? Т.е. как заранее знать, что вот то, что в таймере - это не UI-поток, чтобы изначально вводить хандлер, а не недоумевать, почему вылезает ошибка? Есть ли какой-нить...не знаю, инструмент, чтобы знать, что ты уже пишешь код, который будет в другом потоке работать?

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение powercat » 03 дек 2012, 10:05

neoksi писал(а):Через Хендлер сбрось в UI поток и оттуда уже выводи сообщение.
В UI...у меня работа идет в сервисе, основное приложение отключено, у юзера может быть открыта любае его программа...где мне прописывать тогда хандлер?

AndreyI
Сообщения: 372
Зарегистрирован: 14 май 2012, 16:18

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение AndreyI » 03 дек 2012, 10:14

У класса Thread есть статические методы, можно, к примеру, узнать имя текущего потока:
Thread.currentThread().getName() - даст имя текущего потока, для UI потока это обычно "main".
Можно свои потоки как-то называть.
Или еще советуют Looper.getMainLooper().getThread() == Thread.currentThread() должен дать true на UI потоке.


Но обычно программист знает какой участок кода в каком потоке выполняется и если есть вероятность, что код для UI потока может быть выполнен в каком-то другом потоке, то можно на всякий случай пропустить его через Handler. А способов в Android хватает, Handler UI потока можно получить даже из любой View (getHandler()), или даже сразу вызывать его метод post к примеру:

Вместо tv.setText("Hello, World!");
можно всегда написать:

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

tv.post(new Runnable() {
  public void run() {
     tv.setText("Hello, World!");
  }
});
Кода чуть побольше, но зато гарантированно его выполнение в UI потоке.
Если есть доступ контексту Activity, то можно использовать его метод runOnUiThread

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

runOnUiThread(new Runnable() {
    public void run() {
        tv.setText("Hello, World!");
    }
});
У меня, к примеру, была такая ситуация, я написал интерфейс с функцией обратного вызова, хотя реализация интерфейса и был прописана в Activity, но вызов этой функции делал другой поток, в результате тоже получил исключение, пришлось реализовывать вызов колбэков так же через Handler.

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение powercat » 03 дек 2012, 10:21

Не очень понял ((
если надо выполнить код, содержащийся в сервисе, когда у юзера запущено другое приложение, то где мне описывать сам хандлер?
В уроках везде описывался хандлер в MainActivity, т.е. он был "прилеплен" к нему, и все раннейблы сидели там же...тут все было понятно.
А у меня - нет MainActivity, а есть сервис, в нем таймер ))
Раннейбл я пропишу, но как сказать системе, что надо бы выполнить код из сервиса, если она не знает, что там есть хандлер?

AndreyI
Сообщения: 372
Зарегистрирован: 14 май 2012, 16:18

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение AndreyI » 03 дек 2012, 10:22

powercat писал(а): В UI...у меня работа идет в сервисе, основное приложение отключено, у юзера может быть открыта любае его программа...где мне прописывать тогда хандлер?
Тем не менее, у вашего сервиса, как и у любого компонента приложения всегда есть UI поток (со своим Looper-ом, Handler-ом и очередью сообщений), сервис всегда стартует в нем, даже если он в другом процессе запускается (в этом процессе правда будет создан свой UI-поток), а потом уже вы создаете свои потоки.
Последний раз редактировалось AndreyI 03 дек 2012, 10:26, всего редактировалось 1 раз.

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение powercat » 03 дек 2012, 10:25

AndreyI писал(а):
powercat писал(а): В UI...у меня работа идет в сервисе, основное приложение отключено, у юзера может быть открыта любае его программа...где мне прописывать тогда хандлер?
Тем не менее, у вашего сервиса, как и у любого компонента приложения всегда есть UI поток (со своим Looper-ом, Handler-ом и очередью сообщений), сервис всегда стартует в нем, даже если он в другом процессе запускается, а потом уже вы создаете свои потоки.
Т.е. сервис, стартуя в UI-потоке, все же имеет доступ к интерфейсу?? :shock: Что-то я тогда запутался )))

AndreyI
Сообщения: 372
Зарегистрирован: 14 май 2012, 16:18

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение AndreyI » 03 дек 2012, 10:29

powercat писал(а):
Т.е. сервис, стартуя в UI-потоке, все же имеет доступ к интерфейсу?? :shock: Что-то я тогда запутался )))
Ну если он его сам создаст (к примеру, диалоговое окно какое выведет), то конечно, а к чужому приложению, которое работает в другом процессе конечно нет.

AndreyI
Сообщения: 372
Зарегистрирован: 14 май 2012, 16:18

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение AndreyI » 03 дек 2012, 10:32

Вообще правильней этот поток "обзывать" главным потоком процесса (Main Thread), а не UI потоком, т.к. это понятие несколько шире чем UI Любой процесс имеет главный поток, но не всякий процесс может иметь UI-интерфейс

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение powercat » 03 дек 2012, 10:36

у меня сервис должен сформировать и отправить уведомление, через NotificationManager...т.е. все же получается, что в "Главном потоке" экран менятеся, т.к. появлятеся иконка сообщения?
Значит что мне надо сделать? В сервисе отправку сообщения помещаю в раннейбл, там где его надо выполнить - использую хандлер?

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

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение rezak90 » 03 дек 2012, 10:40

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

AndreyI
Сообщения: 372
Зарегистрирован: 14 май 2012, 16:18

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение AndreyI » 03 дек 2012, 10:43

когда вы обращаетесь к NotificationManager вы обращаетесь к системному процессу, а по всей видимости, все обращения к нему нужно делать из главного потока сервиса.

AndreyI
Сообщения: 372
Зарегистрирован: 14 май 2012, 16:18

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение AndreyI » 03 дек 2012, 10:56

При создании сервиса, к примеру, в его onCreate или в конструкторе просто создайте Handler (Handler h = new Handler();). А в своих потоках посылайте Notification через post

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

public class MyService extends Service {
private final Handler h = new Handler();



...



//Где-то в вашем потоке
h.post(new Runnable() {
  public void run() {
     nm.notify(...); //Отправляем Notification (nm - ваш NotificationManager)
  }
});

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение powercat » 03 дек 2012, 13:38

Не, не хочет, ни просто через хендлер, ни через хендлер+другой поток.. вот код из сервиса - формирование уведомления

Описание хандлера на уровне класса сервиса

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

public class PlService extends Service {
	
	Handler handler=new Handler();

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

			if (numberNotif>0){     Если в базе есть записи
				String str=getText(R.string.notif_numbers).toString()+numberNotif;     Строка из текста+количество записей в базе
				notif.setLatestEventInfo(PlantService.this, getText(R.string.app_name), str, pi);
				notif.flags|=Notification.FLAG_AUTO_CANCEL;
				notif.number=numberNotif;     Показ количества записей в уведомлении
				Thread t=new Thread(new Runnable(){
					public void run(){
						handler.post(SendNotif);
					}
				});
				t.start();
			}
Раннейбл уведомления

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

	Runnable SendNotif=new Runnable() {
		
		public void run() {
			nm.notify(0,notif);
		}
	};
Вот логи

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

12-04 14:25:52.351: W/dalvikvm(1433): threadid=1: thread exiting with uncaught exception (group=0x40015560)
12-04 14:25:52.361: E/AndroidRuntime(1433): FATAL EXCEPTION: main
12-04 14:25:52.361: E/AndroidRuntime(1433): java.lang.NullPointerException
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at com.example.myplant.PlService$1.run(PlService.java:185)
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at android.os.Handler.handleCallback(Handler.java:587)
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at android.os.Handler.dispatchMessage(Handler.java:92)
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at android.os.Looper.loop(Looper.java:123)
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at android.app.ActivityThread.main(ActivityThread.java:3683)
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at java.lang.reflect.Method.invokeNative(Native Method)
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at java.lang.reflect.Method.invoke(Method.java:507)
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-04 14:25:52.361: E/AndroidRuntime(1433): 	at dalvik.system.NativeStart.main(Native Method)
Где я делаю хрень? )))

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение powercat » 03 дек 2012, 13:50

Тема закрыта....я забыл nm=(NotificationManager) getSystemService(NOTIFICATION_SERVICE); БЛЯ!!
Потоки и хандлер не требуется тут - просто отправляется

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

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение damager82 » 07 дек 2012, 09:49

powercat писал(а):Тема закрыта....я забыл nm=(NotificationManager) getSystemService(NOTIFICATION_SERVICE); БЛЯ!!
Потоки и хандлер не требуется тут - просто отправляется
Давайте без матов
Добро пожаловать на форум сайта StartAndroid
ИзображениеИзображение

Аватара пользователя
powercat
Сообщения: 508
Зарегистрирован: 20 июл 2012, 11:31

Re: Уведомления из сервиса, в котором крутится таймер

Сообщение powercat » 12 дек 2012, 10:02

надо ввести модераторов, шоб терли лишнее...

Ответить