Pull to refresh
8
Виталий@lavelas

Пользователь

4
Subscribers
Send message
О, Вы правы. Подправил)
Не ожидал, что народ начнет минусовать мою историю :(

Всем захабренным поясняю, если вам как фрилансеру говорят почини (что равносильно перепиши с нуля), но мы тебе заплатим $100 от силы то ты:
1. Как фрилансер — пошлешь (работы часов на 200)
2. Как человек пожалеешь и постараешься сделать за 20 часов франкинштейна (поверьте, там хуже некуда).

Проверка точности числа выглядит ужасно. if if elseif elseif…

Хотя бы в один блок записали
if ((decInd < 0 && size > integerSize) || (decInd > digsBeforeDot) || (size - decInd - 1 > digsAfterDot)) {
	isValid = false;
}
на возможность ANR при чтении БД

Почему должен быть ANR при чтении с БД?

и то, что это высасывает батарейку будя CPU и радиомодуль каждые две минуты

Второй вариант, держать коннект с бекэндом в сервисе или в потоке постоянно. Разве это не высасывает батарейку?

В любом случае это делалось в приложении для такси и таким образом отслеживалось изменение статуса заказа. А тут очень важно было быстро прислать пуш клиенту, что его машина приехала или взят заказ. GCM тут не удобен
1. Грузить заказы из базы данных в методе
onReceive
очень грубое решение) Этот метод вызывается в UI потоке.

Так это sqlite, внутренняя БД. + там только заказы не в конечном статусе, по которым понадобиться push.

2. Хорошей практикой считается запускать сервис из метода onReceive дабы не делать слишком многого в нем

Как я понял из документации, сервисы, потоки, работающие после завершения работы onRecive могут быть убиты

3. В принципе, как вы сами говорите, решение грубое. Утверждать, что за 2 минут простоя ОС не убьет ваш процесс я бы не стал :)

Так это ж AlarmManager. Он как раз и не висит потоком. Ось же не убивает установки будильника :)

Вот про это не совсем понял, кто будет устанавливать новый запуск аларм менеджера?

Я же и устанавливаю в onRecive. У меня есть интент. Из него получить PendingIntent не проблема. Ну и флаг FLAG_CANCEL_CURRENT заменяет AlarmManager:
pendingIntent = PendingIntent.getBroadcast(context, 0,
                    intent, PendingIntent.FLAG_CANCEL_CURRENT);
Как я понял из документации, после отработки 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. Используется проприетарный протокол.
Пока все тщетно
Я к нему привык довольно быстро. Если будут вопросы, пишите. Будем опытом делиться.
2

Information

Rating
Does not participate
Location
Днепр, Днепропетровская обл., Украина
Registered
Activity