Android: как периодически отправлять местоположение на сервер - PullRequest
49 голосов
/ 05 мая 2010

У меня запущен веб-сервис, который позволяет пользователям записывать свои поездки (например, MyTracks от Google) как часть более крупного приложения. Дело в том, что данные, в том числе координаты и другие элементы, легко передать на сервер, когда пользователь начинает поездку или заканчивает ее. Будучи новичком, я не уверен, как настроить фоновую службу, которая отправляет обновления местоположения один раз в каждый (заранее определенный) период (минимум 3 минуты, максимум 1 час), пока пользователь не отметит окончание поездки, или пока заданный промежуток времени.

Как только поездка запускается с телефона, сервер отвечает периодом опроса, который телефон использует в качестве интервала между обновлениями. Эта часть работает, так как я могу отобразить ответ на телефоне, и мой сервер регистрирует действия пользователя. Точно так же командировка закрывается на стороне сервера после запроса на закрытие.

Однако, когда я попытался запустить метод периодического отслеживания изнутри StartTrack Activity, используя requestLocationUpdates (поставщик String, long minTime, float minDistance, слушатель LocationListener), где minTime - период опроса от сервера, он просто не работал, и я не получаю никаких ошибок. Таким образом, это значит, что я на данный момент не знаю, никогда раньше не пользовался Android.

Я видел много постов здесь об использовании фоновых сервисов с обработчиками, ожидающими намерениями и другими вещами для выполнения подобных вещей, но я действительно не понимаю, как это сделать. Я хотел бы, чтобы пользователь делал другие вещи по телефону во время обновления, так что если вы, ребята, могли бы указать мне учебник, который показывает, как на самом деле писать фоновые службы (может быть, они запускаются как отдельные классы?) Или другие способы делать это было бы замечательно.

Ответы [ 3 ]

73 голосов
/ 06 мая 2010

Я недавно написал один из них и решил, что не стоит оставлять фоновую службу запущенной. Возможно, он все равно будет отключен операционной системой, или это может быть так. Я использовал фильтр для цели загрузки, а затем установил тревогу с помощью диспетчера тревог, чтобы мое приложение перезапускалось через равные промежутки времени, а затем оно отправляло данные. Хорошую информацию об услугах и диспетчере аварийных сигналов вы можете найти в документации Android.

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

public class LaunchReceiver extends BroadcastReceiver {

    public static final String ACTION_PULSE_SERVER_ALARM = 
            "com.proofbydesign.homeboy.ACTION_PULSE_SERVER_ALARM";

    @Override
    public void onReceive(Context context, Intent intent) {
        AppGlobal.logDebug("OnReceive for " + intent.getAction());
        AppGlobal.logDebug(intent.getExtras().toString());
        Intent serviceIntent = new Intent(AppGlobal.getContext(),
                MonitorService.class);
        AppGlobal.getContext().startService(serviceIntent);
    }
}

В манифесте у меня есть:

<receiver
    android:name="LaunchReceiver"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
    <intent-filter>
        <action android:name="com.proofbydesign.homeboy.ACTION_PULSE_SERVER_ALARM" />
    </intent-filter>
</receiver>

Обратите внимание, что у меня есть фильтр для моей собственной тревоги, который позволяет мне отключить службу и перезапустить ее после того, как она выполнит свою работу.

Верх моей службы мониторинга выглядит так:

public class MonitorService extends Service {

    private LoggerLoadTask mTask;
    private String mPulseUrl;
    private HomeBoySettings settings;
    private DataFile dataFile;
    private AlarmManager alarms;
    private PendingIntent alarmIntent;
    private ConnectivityManager cnnxManager;

    @Override
    public void onCreate() {
        super.onCreate();
        cnnxManager = (ConnectivityManager) 
                getSystemService(Context.CONNECTIVITY_SERVICE);
        alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        Intent intentOnAlarm = new Intent(
                LaunchReceiver.ACTION_PULSE_SERVER_ALARM);
        alarmIntent = PendingIntent.getBroadcast(this, 0, intentOnAlarm, 0);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        // reload our data
        if (mPulseUrl == null) {
            mPulseUrl = getString(R.string.urlPulse);
        }
        AppGlobal.logDebug("Monitor service OnStart.");
        executeLogger();
    }

executeLogger запускает asyncTask, что, вероятно, является излишней осторожностью (это было только мое третье приложение для Android). AsyncTask захватывает данные GPS, отправляет их в Интернет и, наконец, устанавливает следующий сигнал тревоги:

private void executeLogger() {
    if (mTask != null
        && mTask.getStatus() != LoggerLoadTask.Status.FINISHED) {
        return;
    }
    mTask = (LoggerLoadTask) new LoggerLoadTask().execute();
}

private class LoggerLoadTask extends AsyncTask<Void, Void, Void> {

    // TODO: create two base service urls, one for debugging and one for live.
    @Override
    protected Void doInBackground(Void... arg0) {
        try {
            // if we have no data connection, no point in proceeding.
            NetworkInfo ni = cnnxManager.getActiveNetworkInfo();
            if (ni == null || !ni.isAvailable() || !ni.isConnected()) {
                AppGlobal
                        .logWarning("No usable network. Skipping pulse action.");
                return null;
            }
            // / grab and log data
        } catch (Exception e) {
            AppGlobal.logError(
                    "Unknown error in background pulse task. Error: '%s'.",
                    e, e.getMessage());
        } finally {
            // always set the next wakeup alarm.
            int interval;
            if (settings == null
                || settings.getPulseIntervalSeconds() == -1) {
                interval = Integer
                        .parseInt(getString(R.string.pulseIntervalSeconds));
            } else {
                interval = settings.getPulseIntervalSeconds();
            }
            long timeToAlarm = SystemClock.elapsedRealtime() + interval
                * 1000;
            alarms.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToAlarm,
                    alarmIntent);
        }
        return null;
    }
}

Я заметил, что я не вызываю stopSelf () после установки будильника, поэтому моя служба будет бездействовать, если не отключена операционной системой. Поскольку я единственный пользователь этого приложения, это не имеет значения, кроме общедоступного приложения, идея заключается в том, что вы устанавливаете будильник на следующий интервал, а затем StopSelf закрывается.

Обновление См. Комментарий @juozas об использовании 'alarms.setRepeating ()'.

17 голосов
/ 05 мая 2010

Вам необходимо создать отдельный класс, который является подклассом класса Service.

Сервисная документация

Ваше основное приложение должно вызывать startService и stopService, чтобы запустить фоновый процесс. Также есть несколько других полезных вызовов в классе контекста для управления сервисом:

Контекстная документация

2 голосов
/ 04 февраля 2014

Я согласен с Робом Кентом, и, кроме того, я думаю, что было бы лучше расширить WakefulBroadcastReceiver в вашем BroadcastReceiver и использовать его статический метод startWakefulService(android.content.Context context,android.content.Intent intent), поскольку он гарантировал, что ваша служба не будет закрыта ОС.

public class YourReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        Intent service = new Intent(context, YourService.class);
        startWakefulService(context, service);
    }
}

Официальная документация

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...