【问题标题】:Android: How to periodically send location to a serverAndroid:如何定期将位置发送到服务器
【发布时间】:2010-05-05 18:02:05
【问题描述】:

我正在运行一项网络服务,该服务允许用户记录他们的行程(有点像 Google 的 MyTracks)作为更大应用程序的一部分。问题是当用户开始或结束旅行时,很容易将数据(包括坐标和其他项目)传递给服务器。作为一个新手,我不知道如何设置一个后台服务,在用户标记旅行结束或直到经过预设的时间。

一旦从手机启动行程,服务器就会以轮询周期进行响应,手机将用作更新之间的间隔。这部分有效,因为我可以在手机上显示响应,并且我的服务器会注册用户的操作。同样,行程在关闭行程请求时在服务器端关闭。

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

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

【问题讨论】:

    标签: android


    【解决方案1】:

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

    首先,我创建了一个广播接收器,它仅在打开 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);
        }
    }
    

    在清单中我有:

    <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(),所以我的服务将无所事事,除非被 op sys 关闭。由于我是这个应用程序的唯一用户,这并不重要,但对于一个公共应用程序来说,你的想法是你设置下一个间隔的警报然后 stopSelf 关闭。

    更新查看@juozas 关于使用“alarms.setRepeating()”的评论。

    【讨论】:

    • 您的解决方案非常棒。我终于让我的工作了,但我肯定从你的方法中记下了一些笔记。例如,我根本没有检查现有的网络连接,所以当没有连接时我会收到错误消息。竖起大拇指!
    • 很好的例子,谢谢!但我有个问题。为什么要不断地重新安排警报,alarms.setRepeating() 不也是这样做的吗?
    • @Juozas,是的,你可能是对的。如果是这样,那将是一个改进。谢谢
    • 回复:setRepeating() 推荐的方式是setinexactRepeating()(并且需要简化您的代码) - 我的问题:即使手机睡着了,这是否有效?
    • @Mawg 这只是我自己的应用程序类,我在其中保留了各种辅助方法,例如日志记录等。您可以忽略它。事实上,我应该去掉所有无关的代码。
    【解决方案2】:

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

    Service Documentation

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

    Context Documentation

    【讨论】:

    • 谢谢一百万。我已经能够构建服务,启动和停止它。我现在必须确保系统不会杀死它,并且在需要时调用它。
    • 请记住,操作系统会根据需要定期停止(并重新启动)您的服务:developer.android.com/reference/android/app/…
    • jscharf,我如何确保服务被保证定期调用,然后才运行,直到用户执行特定操作来停止调用?我正在寻找使用警报并阅读有关如何使用它们的文档...
    • 您可以让 ALARM_SERVICE 定期向您的服务发送 Intent,或者您可以为您的服务生成一个新线程并让它检查时间和 sleep() 以确保您的正确时间想要在醒来之间。您的主应用程序负责调用 stopService 将其关闭,因此它需要捕获用户事件。还要记住,该服务在您的主应用程序的进程中运行。您的主应用程序应确保它在其 onStop() 方法中释放尽可能多的资源,以确保它不会加载系统。
    • 马克,我已经使用报警服务设置了。使用服务器的轮询周期可以正常触发警报,但是现在当我尝试使用服务中的位置更新数据创建soap 包时出现序列化运行时错误。这真是令人沮丧...
    【解决方案3】:

    我同意 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-28
      相关资源
      最近更新 更多