Android:如何定期向服务器发送位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2775628/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 07:18:43  来源:igfitidea点击:

Android: How to periodically send location to a server

android

提问by Mark

I am running a Web service that allows users to record their trips (kind of like Google's MyTracks) as part of a larger app. The thing is that it is easy to pass data, including coords and other items, to the server when a user starts a trip or ends it. Being a newbie, I am not sure how to set up a background service that sends the location updates once every (pre-determined) period (min 3 minutes, max 1 hr) until the user flags the end of the trip, or until a preset amount of time elapses.

我正在运行一个 Web 服务,它允许用户将他们的旅行(有点像 Google 的 MyTracks)记录为更大的应用程序的一部分。问题是,当用户开始旅行或结束旅行时,很容易将数据(包括坐标和其他项目)传递到服务器。作为一个新手,我不知道如何设置一个后台服务,每(预定)时间段(最少 3 分钟,最多 1 小时)发送一次位置更新,直到用户标记旅行结束,或者直到经过预设的时间量。

Once the trip is started from the phone, the server responds with a polling period for the phone to use as the interval between updates. This part works, in that I can display the response on the phone, and my server registers the user's action. Similarly, the trip is closed server-side upon the close trip request.

从电话开始旅行后,服务器将响应一个轮询周期,供电话用作更新之间的间隔。这部分有效,因为我可以在电话上显示响应,并且我的服务器注册用户的操作。类似地,根据关闭行程请求,行程在服务器端关闭。

However, when I tried starting a periodic tracking method from inside the StartTrack Activity, using requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener) where minTime is the poll period from the server, it just did not work, and I'm not getting any errors. So it means I'm clueless at this point, never having used Android before.

但是,当我尝试从 StartTrack Activity 内部启动定期跟踪方法时,使用 requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener) 其中 minTime 是来自服务器的轮询周期,但它不起作用,我我没有收到任何错误。所以这意味着我在这一点上一无所知,以前从未使用过 Android。

I have seen many posts here on using background services with handlers, pending intents, and other things to do similar stuff, but I really don't understand how to do it. I would like the user to do other stuff on the phone while the updates are going on, so if you guys could point me to a tutorial that shows how to actually write background services (maybe these run as separate classes?) or other ways of doing this, that would be great.

我在这里看到了很多关于使用带有处理程序的后台服务、挂起的意图和其他事情来做类似事情的帖子,但我真的不明白如何去做。我希望用户在更新进行时在手机上做其他事情,所以如果你们能指点我一个教程,展示如何实际编写后台服务(也许这些作为单独的类运行?)或其他方式这样做,那会很棒。

采纳答案by Mark

You need to create a separate class that is a subclass of the Serviceclass.

您需要创建一个单独的类,它是该类的子Service类。

Service Documentation

服务文件

Your primary application should can call startServiceand stopServiceto start up the background process. Theres also some other useful calls in the context class to manage the service:

您的主应用程序应该可以调用startServicestopService启动后台进程。在上下文类中还有一些其他有用的调用来管理服务:

Context Documentation

上下文文档

回答by Rob Kent

I recently wrote one of these and decided it is not a good idea to leave a background service running. It will probably be shut down by the operating system anyway, or it could be. What I did was use a filter for the boot intent and then set an alarm using the alarm manager so that my app was restarted at regular intervals, and then it sent the data. You can find good info on services and the alarm manager in the Android documentation.

我最近写了其中一个,并决定让后台服务运行不是一个好主意。无论如何,它可能会被操作系统关闭,或者可能是。我所做的是对启动意图使用过滤器,然后使用警报管理器设置警报,以便我的应用程序定期重新启动,然后发送数据。您可以在 Android 文档中找到有关服务和警报管理器的良好信息。

First I created a broadcast receiver that simply starts my service when an internet connection is opened (I'm only interested if there is a connection - you might want to filter for the boot event as well). The launch receiver must be short-lived, so just start your service:

首先,我创建了一个广播接收器,它在打开 Internet 连接时简单地启动我的服务(我只对是否有连接感兴趣 - 您可能还想过滤启动事件)。发射接收器必须是短暂的,所以只需启动您的服务:

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);
    }
}

In the manifest I have:

在清单中,我有:

<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>

Notice how I have a filter for my own alarm, which is what allows me to shut the service and have it restarted after it's done its work.

请注意我如何为自己的警报设置过滤器,这使我可以关闭服务并在完成工作后重新启动它。

The top of my monitor service looks like:

我的监控服务的顶部看起来像:

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 starts an asyncTask, which is probably me being excessively cautious (this was only my third Android app). The asyncTask grabs the GPS data, sends it to the internet and finally sets the next alarm:

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;
    }
}

I notice that I am not calling stopSelf() after setting the alarm, so my service will sit around doing nothing unless shut down by the op sys. Since I am the only user of this app, that doesn't matter but for a public app, the idea is you set the alarm for the next interval then stopSelf to close down.

我注意到在设置警报后我没有调用 stopSelf(),所以我的服务将无所事事,除非被 op sys 关闭。由于我是这个应用程序的唯一用户,这无关紧要,但对于公共应用程序,想法是您设置下一个间隔的警报,然后 stopSelf 关闭。

UpdateSee the comment from @juozas about using 'alarms.setRepeating()'.

更新请参阅@juozas 关于使用“alarms.setRepeating()”的评论。

回答by brucemax

I agree with Rob Kent, and in additional I think could be beter to extends WakefulBroadcastReceiver in your BroadcastReceiver and use it's static method startWakefulService(android.content.Context context,android.content.Intent intent), because it garanted your service will not shut by os.

我同意 Rob Kent 的观点,另外我认为在您的 BroadcastReceiver 中扩展 WakefulBroadcastReceiver 并使用它的静态方法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);
    }
}

Official documentation

官方文档