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

Mar*_*ark 49 android

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

一旦从电话开始旅行,服务器将以电话的轮询周期作为响应,作为更新之间的间隔.这部分工作,因为我可以在手机上显示响应,我的服务器注册用户的操作.类似地,在关闭行程请求时,行程在服务器端关闭.

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

我在这里看到很多关于使用后台服务与处理程序,待定意图和其他类似东西的帖子,但我真的不明白该怎么做.我希望用户在更新过程中在手机上做其他的事情,所以如果你们可以指点我的教程,说明如何实际编写后台服务(也许这些作为单独的类运行?)或其他方式这样做,那将是伟大的.

Rob*_*ent 73

我最近写了其中一个,并认为让后台服务运行不是一个好主意.它可能会被操作系统关闭,或者可能是.我所做的是使用过滤器作为启动意图,然后使用警报管理器设置警报,以便我的应用程序定期重新启动,然后发送数据.您可以在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在清单中我有:

<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>
Run Code Online (Sandbox Code Playgroud)

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

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

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();
    }
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我注意到在设置警报后我没有调用stopSelf(),所以除非通过操作系统关闭,否则我的服务将无所事事.由于我是这个应用程序的唯一用户,这对于公共应用程序无关紧要,我们的想法是为下一个时间间隔设置警报,然后关闭stopSelf.

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

  • 非常好的例子,谢谢!但我有个问题.你为什么要连续报警重新安排,不报警.setRepeating()做同样的事情? (2认同)
  • @Mawg这只是我自己的应用程序类,我在其中保存各种帮助方法,例如日志记录等.您可以忽略它.事实上,我应该删除所有无关的代码. (2认同)

Mar*_*ark 17

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

服务文档

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

上下文文档

  • 请记住,操作系统会定期停止(并重新启动)您认为合适的服务:http://developer.android.com/reference/android/app/Service.html#ProcessLifecycle (2认同)
  • 您可以让ALARM_SERVICE在某个定期时间向您的服务发送一个Intent,或者您可以为您的服务生成一个新线程,让它检查时间和sleep(),以获得您在唤醒之间所需的正确时间.您的主应用程序负责调用stopService将其关闭,以便捕获用户事件.还要记住,该服务在主应用程序的进程中运行.您的主应用程序应确保它在onStop()方法中释放尽可能多的资源,以确保它不会加载系统. (2认同)