Android服务和线程中运行的重复任务

时间:2012-06-27 20:55:09

标签: android service background scheduled-tasks

我正在后台运行一个读取GPS /网络位置的服务,需要执行以下操作:

  • 在后台运行,不会因应用程序重启而中断,并尽可能保持活着而不会被杀死(这可以通过Merlin的评论帮助解决)

  • 在收到的新位置,调用网络服务并发送阅读位置

  • 每60秒运行一次重复任务,并将最后一个位置重新发送到Web服务。这将有助于用户保持在相同的位置。

我考虑过一些事情,我不确定我是否理解正确。该服务在与主应用程序相同的线程中运行,因此在与UI线程相同的线程上将位置发送到服务器可能会触发UI冻结,这并不好。此外,我不确定GPS /网络侦听器是否拥有自己的线程或使用与应用程序相同的线程。

这是一个缩短的服务代码,使事情变得更加清晰:

public class GPSLoggerService extends Service {
@Override
public void onCreate() {
    locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 50, locationListenerNetwork);
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 50, locationListenerGps);

    scheduleTaskExecutor = Executors.newScheduledThreadPool(5);
    scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
                updateLocation(lastLocation);
        }, 60, 60, TimeUnit.SECONDS);

    return START_STICKY;
}

LocationListener locationListenerGps = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
        updateLocation(location);
    }
...
}    

LocationListener locationListenerNetwork = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
        updateLocation(location);
    }
...
}

private void updateLocation(Location readLocation) {
    //web service call
    String response = WebServiceCalls.updateLocation(readLocation);

    //log data in a local Sqlite database
    saveLocation(readLocation) 
}

我主要关心的是如何处理updateLocation调用并将其放在与主app线程不同的线程中。 scheduleTaskExecutor我相信它不是要走的路。有时候,即使我告诉TaskExecutor关闭了,即使在我调用stopService()之后服务仍然存活。我找不到服务没有停止的另一种解释。 所以回顾一下:每次听众收到一个新位置并每隔60秒重新发送一次,我需要发送该位置。我还需要能够在活动线程取消的情况下快速停止服务。

你怎么建议我处理我的案子?

4 个答案:

答案 0 :(得分:4)

我使用IntentService并使用AlarmManager来触发意图。

这样做的主要优点是没有线程代码可以担心,因为它在后台工作

<强>更新

另一种有趣的方法可以在https://stackoverflow.com/a/7709140/808940

中找到

答案 1 :(得分:2)

  • 服务运行与主应用程序相同的进程,而不是线程。此外,如果您想在其他流程中运行服务,则可以使用android:process代码。

  • 我不确定你为什么要每60秒调用一次WebService,因为60秒太少了。此外,您应该在位置未更改时跳过调用WebService,因为它需要网络通信,而且操作成本很高。

  • 无需使用执行程序。您应该尽可能减少线程数。要以特定间隔执行任务,请使用AlarmManager在特定时间传递意图。检查setRepeating()方法以设置闹钟。

  • 另一件事是,您应该尽量避免在Listener中执行任何任务。因为在考虑接收器/侦听器被阻挡以及被杀死的候选者之前系统允许有10秒的timeout。您应该使用Handler在后台线程中执行任务(即,每当您从侦听器接收更新时,向Handler队列添加一条消息,当Handler线程空闲时它将被选中)。

答案 2 :(得分:1)

为防止您的服务遭到破坏,您可以foreground service启动服务。

从onLocationChanged()方法获取位置后,您可以使用asynctask向Web服务发送位置,以便它不会阻止您的用户界面。

修改

  1. 您可以在 requestLocationUpdates 方法中设置最短时间和最短距离。所以我认为您不应该使用调度程序任务将位置发送到服务器。根据关于最小时间和最小距离的论点,位置经理将检查位置。如果位置已更改,则会使用新位置调用onLocationChanged()方法。 现在为您提供关于用户停留在同一位置的解决方案。您可以将一些逻辑更改为服务器端,如果两个连续位置之间存在1小时差异location1和location2表示用户在location1停留了1小时。

  2. 您可以使用单个LocationListener类来收听GPS和NETWORK位置。

  3. 当您在onLocationChanged()方法中获得位置时,您可以使用asynctask发送该位置。
  4. 获取位置后,您可以将该位置保存在首选项或数据库中,以检查天气GPS和网络提供商向您发送相同的位置,这样如果您将跟踪,那么您可以保存您的webAPI呼叫,这样您就可以节省一部分电池。

答案 3 :(得分:-1)

你应该使用AsyncTask:

public class RefreshTask extends AsyncTask<Integer, Void, Integer> {



    /**
     * The system calls this to perform work in a worker thread and delivers
     * it the parameters given to AsyncTask.execute()
     */

    public RefreshTask() {

    }

    protected Integer doInBackground(Integer... millis) {
        try{
            int waitTime = 0;
            while(waitTime<60000){
                Thread.sleep(100);
                waitTime += 100;
            }

            //update location here

        }catch(InterruptedException e){

        }



        return 1;
    }

    /**
     * The system calls this to perform work in the UI thread and delivers
     * the result from doInBackground()
     */
    protected void onPostExecute(Integer result) {
        new RefreshTask.execute();
    }


}