我正在运行一项网络服务,允许用户记录他们的行程(有点像Google的MyTracks)作为更大的应用程序的一部分。问题在于,当用户开始旅行或结束旅行时,很容易将数据(包括坐标和其他项目)传递到服务器。作为一个新手,我不知道如何设置一个后台服务,每隔(预定)一段时间(最少3分钟,最多1小时)发送一次位置更新,直到用户标记旅行结束,或直到预设的时间量过去了。
从手机开始旅行后,服务器会以手机的轮询周期作为更新之间的间隔进行响应。这部分工作,因为我可以在手机上显示响应,我的服务器注册用户的操作。同样,在关闭旅行请求时,旅程在服务器端关闭。
但是,当我尝试从StartTrack活动内部启动定期跟踪方法时,使用requestLocationUpdates(String provider,long minTime,float minDistance,LocationListener listener),其中minTime是来自服务器的轮询周期,它只是不起作用,而且我没有收到任何错误。所以这意味着我在这一点上一无所知,从来没有使用过Android。
我在这里看到很多关于使用后台服务与处理程序,待定意图和其他类似事情的帖子,但我真的不明白该怎么做。我希望用户在更新过程中在手机上做其他的事情,所以如果你们可以指点我的教程,说明如何实际编写后台服务(也许这些作为单独的类运行?)或其他方式这样做,那将是伟大的。
答案 0 :(得分: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);
}
}
在清单中我有:
<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关闭,否则我的服务将无所事事。由于我是这个应用程序的唯一用户,这并不重要,但对于公共应用程序,我们的想法是为下一个间隔设置警报,然后关闭自动关闭。
更新请参阅@juozas关于使用'alarms.setRepeating()'的评论。
答案 1 :(得分:17)
您需要创建一个单独的类,它是Service
类的子类。
您的主要应用程序应致电startService
和stopService
以启动后台进程。还有上下文类中的一些其他有用的调用来管理服务:
答案 2 :(得分:2)
我同意Rob Kent,另外我认为可以更好地在你的BroadcastReceiver中扩展WakefulBroadcastReceiver并使用它的静态方法startWakefulService(android.content.Context context,android.content.Intent intent)
,因为它保证你的服务不会被os关闭。
public class YourReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, YourService.class);
startWakefulService(context, service);
}
}