android - 在 Android 中长期运行的服务上读取、存储和发送读取的 GPS 位置

标签 android multithreading sqlite gps locationlistener

场景

第 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数据库是否有发送失败的记录
  • 如果有,请将它们与当前位置一起发送到网络服务
  • 如果没有,则仅发送当前位置
  • 如果发送失败(主要是由于数据连接),请在数据库中插入位置以供将来读取
  • 如果发送成功,则从数据库中删除所有行

问题

所有这些都是在服务的后台完成的。对于每个 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();
}

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

换个思路,如果我在服务的 onCreate 方法中创建一个新线程会怎样。像这样的东西:

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

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

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

使用当前的方法,应用程序可以完成工作,但出现了一些错误,随机发生,并且无法找出原因:我的一项 Activity 绑定(bind)到服务以接收更新,当应用程序获取更新时,我小心地取消绑定(bind)它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();
    }

最佳答案

所以让我说清楚:您想从后台线程一个接一个地发送位置。实现此目的的一个简单方案是(类似于您编辑的代码,但我没有看到 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();
}

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

关于android - 在 Android 中长期运行的服务上读取、存储和发送读取的 GPS 位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12044952/

相关文章:

c++ - 带有 Valgrind 的 Sqlite3 SIGSEGV

c# - 正确包含 x32 或 x64 System.Data.SQLite.dll 文件

android - 通过蓝牙将数据从 Android Wear watch 传输到手机

c# - 在与服务线程不同的线程上运行服务操作

android - 图像数据使用

python - python 中是否可以从子线程中杀死父线程?

multithreading - putStrLn 是线程安全的吗?

android - 错误 :android. database.sqlite.SQLiteException : near "WHERE": syntax error: , 编译时:

android - Dagger Hilt 'Assisted' 和 'ViewModelInject' 已弃用。在 Dagger Hilt View Model 1.0.0-alpha03

当视频在 KitKat 上结束时,Android WebView 崩溃