Не ожидал, что народ начнет минусовать мою историю :(
Всем захабренным поясняю, если вам как фрилансеру говорят почини (что равносильно перепиши с нуля), но мы тебе заплатим $100 от силы то ты:
1. Как фрилансер — пошлешь (работы часов на 200)
2. Как человек пожалеешь и постараешься сделать за 20 часов франкинштейна (поверьте, там хуже некуда).
и то, что это высасывает батарейку будя CPU и радиомодуль каждые две минуты
Второй вариант, держать коннект с бекэндом в сервисе или в потоке постоянно. Разве это не высасывает батарейку?
В любом случае это делалось в приложении для такси и таким образом отслеживалось изменение статуса заказа. А тут очень важно было быстро прислать пуш клиенту, что его машина приехала или взят заказ. GCM тут не удобен
Как я понял из документации, после отработки onReceive(), Андроид может убить процесс, если даже мы запустили где-то свой поток (AsyncTask).
Эту проблему можно побороть так:
if (orders.size() == 0) {
// если ничего нет, отменяем новый запуск
am.cancel(pendingIntent);
} else {
// вот тут у меня AsyncTask
getNewStatusesFromServer(orders);
// вот тут мы пересоздаем AlarmManager
am.cancel(pendingIntent);
am.set(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + repeat, pendingIntent);
}
private void getNewStatusesFromServer(List<String> orders) {
// некоторая реализация
}
Перенеся am.cancel am.set в onReceive() мы будем уверенны, что новый запуск AlarmManager будет установлен, даже если в какой-то момент андроид прибил наш AsyncTask.
Когда нужно запустить сервис по получению данных с бекэнда
// устанавливаем AlarmManager
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, OrderChecker.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
// отменяем все установки, если они были
am.cancel(pendingIntent);
// устанавливаем на разовое выполнение
int repeat = Integer.parseInt(profile_settings.getString("upd_period", "120000"));
am.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + repeat, pendingIntent);
А вот BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent) {
// сохраняю контекст для других методов класса
myApp = context;
db = new DatabaseHelper(myApp);
profile_settings = PreferenceManager.getDefaultSharedPreferences(context);
repeat = Integer.parseInt(profile_settings.getString("upd_period", "120000"));
am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
pendingIntent = PendingIntent.getBroadcast(context, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
try {
// взяли из БД заказы
orders = db.getOrdersFromDB(db.getReadableDatabase(),"");
if (orders.size() == 0) {
// если ничего нет, отменяем новый запуск
am.cancel(pendingIntent);
} else {
// вот тут у меня AsyncTask
getNewStatusesFromServer(orders);
}
} catch (Exception e) {
// если словил любую ошибку, устанавливаю новый запуск AlarmManager
am.cancel(pendingIntent);
am.set(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + repeat, pendingIntent);
}
}
private void getNewStatusesFromServer(List<String> orders) {
// некоторая реализация
// и в конце я устанавливаю новый запуск AlarmManager
am.cancel(pendingIntent);
am.set(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + repeat, pendingIntent);
}
получается, что onReceive отработал и установил новый таймер — сработать через 2 минуты. И так пока orders.size() != 0
Если телефон перезагрузили
public class CheckerStarter extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent i) {
if ("android.intent.action.BOOT_COMPLETED".equals(i.getAction()) || "android.intent.action.QUICKBOOT_POWERON".equals(i.getAction())) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, OrderChecker.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.cancel(pendingIntent);
int repeat = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(context).getString("upd_period", "120000"));
am.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + repeat, pendingIntent);
}
}
}
З.Ы. Я заранее говорил, что метод очень грубый. Но это позволило не держать постоянный коннект и не держать отдельный поток с Thread.sleep(120000). За 2 минуты простоя ось не отменит AlarmManager из-за нехватки ресурсов.
Ну, тестов провели много, запрещая фоновые процессы, ограничивая их число. Не убивался. Делал так: в onRecive первым делом переустанавливал AlarmManager и делал асинктаск.
Могу завтра привести пример кода, если интересно.
Мне однажды необходимо было реализовать постоянное соединение с бекэндом для получения свежих данных в приложении.
В качестве решения был выбран довольно грубый, но действенный способ. При старте приложения создавал AlarmManager без повторения, который срабатывал через 2 минуты. В BroadcastReciver выполнял асинхронный запрос серверу, отменял AlarmManager и ставил заново. И так до бесконечности, пока не сработает метод остановки.
Ещё один BroadcastReciver ловил запуск ОС и проверял, нужно ли переустановить AlarmManager.
В результате получаем ничем не убиваемый процесс, который не держет постоянное соединение и точно достучаться к данным. Ну и батарею не садит сильно.
Жаль, что Tomcat не понимает proxy protocol. Ищу возможность балансировки через tcp mode для Tomcat. Используется проприетарный протокол.
Пока все тщетно
Всем захабренным поясняю, если вам как фрилансеру говорят почини (что равносильно перепиши с нуля), но мы тебе заплатим $100 от силы то ты:
1. Как фрилансер — пошлешь (работы часов на 200)
2. Как человек пожалеешь и постараешься сделать за 20 часов франкинштейна (поверьте, там хуже некуда).
Хотя бы в один блок записали
Почему должен быть ANR при чтении с БД?
Второй вариант, держать коннект с бекэндом в сервисе или в потоке постоянно. Разве это не высасывает батарейку?
В любом случае это делалось в приложении для такси и таким образом отслеживалось изменение статуса заказа. А тут очень важно было быстро прислать пуш клиенту, что его машина приехала или взят заказ. GCM тут не удобен
Так это sqlite, внутренняя БД. + там только заказы не в конечном статусе, по которым понадобиться push.
Как я понял из документации, сервисы, потоки, работающие после завершения работы onRecive могут быть убиты
Так это ж AlarmManager. Он как раз и не висит потоком. Ось же не убивает установки будильника :)
Я же и устанавливаю в onRecive. У меня есть интент. Из него получить PendingIntent не проблема. Ну и флаг FLAG_CANCEL_CURRENT заменяет AlarmManager:
Эту проблему можно побороть так:
Перенеся am.cancel am.set в onReceive() мы будем уверенны, что новый запуск AlarmManager будет установлен, даже если в какой-то момент андроид прибил наш AsyncTask.
Если ошибаюсь, поправьте…
Когда нужно запустить сервис по получению данных с бекэнда
А вот BroadcastReceiver
получается, что onReceive отработал и установил новый таймер — сработать через 2 минуты. И так пока orders.size() != 0
Если телефон перезагрузили
З.Ы. Я заранее говорил, что метод очень грубый. Но это позволило не держать постоянный коннект и не держать отдельный поток с Thread.sleep(120000). За 2 минуты простоя ось не отменит AlarmManager из-за нехватки ресурсов.
Могу завтра привести пример кода, если интересно.
В качестве решения был выбран довольно грубый, но действенный способ. При старте приложения создавал AlarmManager без повторения, который срабатывал через 2 минуты. В BroadcastReciver выполнял асинхронный запрос серверу, отменял AlarmManager и ставил заново. И так до бесконечности, пока не сработает метод остановки.
Ещё один BroadcastReciver ловил запуск ОС и проверял, нужно ли переустановить AlarmManager.
В результате получаем ничем не убиваемый процесс, который не держет постоянное соединение и точно достучаться к данным. Ну и батарею не садит сильно.
Пока все тщетно