2

设想

第 1 步:初始化位置管理器以每 50 米读取一次 GPS 位置:

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

第 2 步:每次读取位置时:

@Override
    public void onLocationChanged(Location location) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                sendLocation(location);
            }
        }).start();
    }

第 3 步:在 sendLocation 上,我做了几件事:

  • 查询本地sqlite数据库发送失败记录
  • 如果有,将它们与当前位置一起发送到 Web 服务
  • 如果没有,只发送当前位置
  • 如果发送失败(主要是因为数据连接),则在数据库中插入位置以供将来读取
  • 如果发送成功,则从数据库中删除所有行

问题

所有这些都是在服务的后台完成的。对于每个 sendLocation 调用,我都会创建一个新线程。虽然连接正常,但一切正常。但是,当发送失败并且用户正在开车时,位置读取会经常发生,并且很有可能有 2-3 个线程都试图发送相同的未发送位置。如果 Thread1 收到列表并尝试发送它,Thread2 和 Thread3 应该无法读取它并尝试发送它,因为 Thread1 可能会成功发送它。我怎样才能防止这种情况发生?如何确保 Thread2 不读取列表?

从我现在的想法来看,我可以在“处理”表中添加一个新字段,并且对于检索到的所有发送行,将该字段更新为 true。在这种情况下,Thread2 只会得到 processing=false 行。这是一个解决方案吗?还有其他建议吗?我仍然认为 Thread2 获取数据有细微的变化,而 Thread1 正在更新处理...谢谢。

稍后编辑:我尝试过这种方法的额外想法和想法

private ExecutorService threadPool;

@Override
public void onCreate() {
    scheduleTaskExecutor = Executors.newSingleThreadScheduledExecutor();
    threadPool = Executors.newSingleThreadExecutor();

     //also need to send location every 60 seconds if no other location was read
    scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
              sendLocation(lastLocation);
                  }
    }, 60, 60, TimeUnit.SECONDS);
}


@Override
    public void onLocationChanged(Location location) {
         threadPool.execute(new Runnable() {
         @Override
         public void run() {
             sendLocation(location);
         }
     });
    }

@Override
public void onDestroy() {
    threadPool.shutdownNow();
}

根据我的阅读,这个 threadPool 应该强制线程一个接一个地执行,对吧?(即使我确实感觉我误解了它的目的)如果是这样,如果我一个小时没有连接会发生什么?对于读取的每个位置,都会添加一个新线程......但是这个线程持续多长时间?我担心如果用户开得非常快会发生什么,我可以每 1-2 秒读取一次位置,这种机制是否会将我的 Web 访问保持在一个队列中,一个线程接一个线程?

在另一个想法中,如果我创建一个新线程的服务的 onCreate 方法会怎样。就像是:

 @Override
    public void onCreate() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                     startLocationListener();
                }
            }).start();
        }

在 startLocationListener() 中,我开始读取 GPS 位置。onLocationChanged 会在这个线程上执行并且不会干扰我的 UI 线程吗?

使用在自己的线程中运行的服务会更明智吗?所以我不必担心线程?

使用当前的方法,应用程序完成了工作,但是随机发生了一些错误并且无法找出原因:我的一个活动绑定到服务以接收更新,当应用程序启动时我小心地取消绑定它 onPause.. . 但有时服务会继续运行,因为我可以看到它的通知图标显示。我将对此进行更多调查,但我需要确定一种强大/可靠的方式来处理位置读取和发送。

稍后稍后编辑

这种方法怎么样:

private ExecutorService scheduleTaskExecutor;

    @Override
    public void onCreate() {
        scheduleTaskExecutor = Executors.newSingleThreadScheduledExecutor();

         //also need to send location every 60 seconds if no other location was read
        scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                  sendLocation(lastLocation);
                      }
        }, 60, 60, TimeUnit.SECONDS);
    }


    @Override
        public void onLocationChanged(Location location) {
             scheduleTaskExecutor.submit(new Runnable() {
             @Override
             public void run() {
                 sendLocation(location);
             }
         });
        }

    @Override
    public void onDestroy() {
        scheduleTaskExecutor.shutdownNow();
    }
4

2 回答 2

1

对于每个 sendLocation 调用,我都会创建一个新线程。

为什么?

但是,当发送失败并且用户正在开车时,位置读取会经常发生,并且很有可能有 2-3 个线程都试图发送相同的未发送位置。

这就是为什么我问“为什么?” 多于。

如果 Thread1 收到列表并尝试发送它,Thread2 和 Thread3 应该无法读取它并尝试发送它,因为 Thread1 可能会成功发送它。我怎样才能防止这种情况发生?如何确保 Thread2 不读取列表?

恕我直言,首先没有 Thread2 和 Thread3。一次使用一个线程,发送所有未发送的数据。这可能是一个长期存在的线程,在工作队列中工作(加上某种计时器机制来处理您之前未能更新的情况,并希望确保您在 X 时间段后再次尝试,如果没有其他事件迫使你早点尝试)。我不明白为什么你需要更多的东西来实现你的目标。

于 2012-08-20T20:58:07.200 回答
1

所以让我直截了当地说:你想从后台线程一个接一个地发送位置。实现此目的的一个简单方案是(类似于您编辑的代码,但我看不出原因ScheduledExecutor):

private ExecutorService exec;

@Override
public void onCreate() {
    exec = Executors.newSingleThreadExecutor();
}


@Override
public void onLocationChanged(Location location) {
    exec.submit(new Runnable() {
         @Override
         public void run() {
             sendLocation(location);
         }
    });
}

@Override
public void onDestroy() {
    exec.shutdownNow();
}

这在幕后所做的基本上是创建一个后台线程和一个任务队列。每次读取位置时,都会将新任务放入队列中。线程不断地轮询队列并按顺序执行任务。

于 2012-09-04T07:40:45.757 回答