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

Posted

技术标签:

【中文标题】Android Service 和线程中运行的重复性任务【英文标题】:Android Service and repetitive task running in threads 【发布时间】:2012-06-29 09:14:36 【问题描述】:

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

在后台运行而不会在应用重启时中断,并尽可能保持其活着而不被杀死(这在 Merlin 的评论帮助下得到了解决)

在收到新位置时,调用 Web 服务并发送读取的位置

每 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 调用并将其放在与主应用程序线程不同的线程中。 scheduleTaskExecutor 我相信这不是要走的路。有时,即使在我调用 stopService() 之后,服务仍然存在,即使我告诉 TaskExecutor 关闭。我找不到服务没有停止的其他解释。 回顾一下:我需要在每次听众收到新位置时发送位置,并每 60 秒重新发送一次。我还需要能够通过取消活动线程来快速停止服务。

你会建议我如何处理我的案子?

【问题讨论】:

需要开始粘吗? 【参考方案1】:

你应该使用AsynchTask:

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



【讨论】:

不要使用AsyncTask 处理此类问题。根据 API 版本,这将阻止所有其他 AsyncTask。【参考方案2】:

Service 与主应用程序运行相同的进程,而不是线程。另外,如果你想在另一个进程中运行服务,那么你可以使用android:process 标签。

我不确定为什么要每 60 秒调用一次 WebService,因为 60 秒太少了。此外,您应该在位置未更改时跳过调用 WebService,因为它需要网络通信并且是一项昂贵的操作。

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

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

【讨论】:

【参考方案3】:

为了防止您的服务被破坏,您可以以foreground service 的身份启动您的服务。

从 onLocationChanged() 方法获取位置后,您可以使用 asynctask 将位置发送到网络服务,这样它就不会阻塞您的 UI。

编辑

    您可以在 requestLocationUpdates 方法中设置最短时间和最短距离。因此,我认为您不应该使用调度程序任务将位置发送到服务器。根据关于最小时间和最小距离的参数,位置管理器将检查位置。如果位置发生更改,那么它将使用新位置调用 onLocationChanged() 方法。 现在,您关于用户的解决方案保持不变。您可以将一些逻辑更改为服务器端,例如两个连续位置 location1 和 location2 之间是否相差 1 小时意味着用户在 location1 停留了 1 小时。

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

    当您在 onLocationChanged() 方法中获取位置时,您可以使用异步任务发送该位置。 获取位置后,您可以将该位置保存在首选项或数据库中,以检查 GPS 和网络提供商向您发送相同位置的天气,因此如果您要跟踪,则可以保存您的 webAPI 调用,这样您就可以保存部分电池。

【讨论】:

如您所见,有来自 gps 和网络的读取,并且还按计划发送。这意味着我将同时对 asynctask 进行 3 次调用。我将如何取消它们? 我必须发送位置,即使司机保持在同一位置,因为这是项目规范的一部分。所以我无法改变这一点。但是,我不认为 asynctask 对我的情况来说是一个好的解决方案。想象一下,如果用户开车四处走动,我可以每 1 秒接收一次新位置。这意味着我必须每 1 秒触发一次 asynctask,这样我就可以运行 2-3 个任务......当服务停止时如何取消它们? 想想你想每 1 秒发送一次位置的情况。您希望每 1 秒处理一次 webAPI 调用(不是每次,而是在连续位置更改的情况下)。如果您取消了最后一个位置并更新了新位置,那么当然最后一个位置将被忽略。 当然你可以取消 Asynctask 但这不是那么简单。你将不得不通过变量来跟踪它。 不,什么都不做,每次位置改变时创建一个异步任务并执行它。完成该过程后,它将自动完成。您不必跟踪所有异步任务。【参考方案4】:

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

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

更新

另一种有趣的方法可以在https://***.com/a/7709140/808940中找到

【讨论】:

那么 onLocationChanged 调用呢?它是否有自己的线程或在那里触发 IntentService ?我还需要知道对网络的调用是否成功。这部分我认为使用 IntentService 制作起来并不容易。我相信应该有一个广播发送,对吧?

以上是关于Android Service 和线程中运行的重复性任务的主要内容,如果未能解决你的问题,请参考以下文章

android:当Activity和Service 都被销毁后,如何控制其中生成的线程?

android中当Service在运行时怎么重启Service?

Android学习——Service

Android平台调用Web Service:线程返回值

Android 之 Service(一)启动,绑定服务

Android Service的启动过程