3

我正在构建一个健身应用程序,它会不断记录设备上的活动。我需要经常登录,但我也不想不必要地耗尽用户的电池,这就是为什么我正在考虑将网络呼叫批处理并在无线电激活后立即将它们全部传输,设备是连接到 WiFi 或正在充电。

我正在使用基于文件系统的方法来实现它。我首先将数据保存到一个File- 最终我可能会使用Square 的 Tape来做到这一点 - 但这是我遇到第一个问题的地方。

我不断将新的日志数据写入 . File,但我还需要定期将所有记录的数据发送到我的后端。发生这种情况时,我会删除File. 现在的问题是如何防止这两个操作同时发生?当然,如果我尝试将日志数据写入File,同时其他进程正在读取File并尝试删除其内容,则会导致问题。

我正在考虑使用IntentService本质上充当所有这些操作的队列。而且因为 - 至少我已经阅读了很多 -在单个工作人员中按顺序IntentServices处理s ,因此其中两个操作不可能同时发生,对吧?IntentThread

目前我想安排一个PeriodicTask负责GcmNetworkManager将数据发送到服务器的。有没有更好的方法来做这一切?

4

3 回答 3

5

1)你在想这整件事!

你的方法比它必须的要复杂得多!出于某种原因,其他答案都没有指出这一点,但GcmNetworkManager已经做了你想要实现的一切!你不需要自己实现任何东西。


2)实现您正在尝试做的事情的最佳方式。

您似乎没有意识到GcmNetworkManager已经以最省电的方式通过自动重试等方式对调用进行批处理,并且它还可以在设备启动期间保留任务,并且可以确保它们在电池高效且您的应用程序需要时立即执行。

每当您有数据可以保存计划时,OneOffTask就像这样:

final OneoffTask task = new OneoffTask.Builder()

        // The Service which executes the task.
        .setService(MyTaskService.class) 

        // A tag which identifies the task
        .setTag(TASK_TAG) 

        // Sets a time frame for the execution of this task in seconds.
        // This specifically means that the task can either be
        // executed right now, or must have executed at the lastest in one hour.
        .setExecutionWindow(0L, 3600L)  

        // Task is persisted on the disk, even across boots                          
        .setPersisted(true) 

        // Unmetered connection required for task
        .setRequiredNetwork(Task.NETWORK_STATE_UNMETERED) 

        // Attach data to the task in the form of a Bundle
        .setExtras(dataBundle)

        // If you set this to true and this task already exists
        // (just depends on the tag set above) then the old task
        // will be overwritten with this one.
        .setUpdateCurrent(true)

        // Sets if this task should only be executed when the device is charging
        .setRequiresCharging(false)

        .build();
mGcmNetworkManager.schedule(task);

这将做你想做的一切:

  • 任务将被持久化在磁盘上
  • 任务将以批量和电池高效的方式执行,最好通过 Wifi
  • 您将具有可配置的自动重试和电池高效的退避模式
  • 任务将在您可以指定的时间窗口内执行。

我建议初学者阅读本文以了解有关GcmNetworkManager.


所以总结一下:

您真正需要做的就是在一个Service扩展中实现您的网络调用,GcmTaskService然后当您需要执行这样的网络调用时,您安排一个OneOffTask其他一切都会为您处理!

当然,您不需要OneOffTask.Builder像我在上面所做的那样调用每个设置器 - 我这样做只是为了向您展示您拥有的所有选项。在大多数情况下,调度任务看起来就像这样:

mGcmNetworkManager.schedule(new OneoffTask.Builder()
                .setService(MyTaskService.class)
                .setTag(TASK_TAG)
                .setExecutionWindow(0L, 300L)
                .setPersisted(true)
                .setExtras(bundle)
                .build());

如果你把它放在一个辅助方法中,或者更好地为你需要做的所有不同任务创建工厂方法,而不是你试图做的所有事情都应该归结为几行代码!


顺便说一句:是的, an在一个 worker 中一个接一个地依次IntentService处理。你可以在这里查看相关的实现。它实际上非常简单而且非常直接。IntentThread

于 2015-12-18T11:08:55.393 回答
0

默认情况下,所有 UI 和 Service 方法都在同一个主线程上调用。除非您显式创建线程或使用,否则AsyncTaskAndroid 应用程序本身没有并发性。

这意味着默认情况下,所有意图、警报、广播都在主线程上处理。

另请注意,在主线程上可能会禁止执行 I/O 和/或网络请求(取决于 Android 版本,请参阅例如如何修复 android.os.NetworkOnMainThreadException?)。

使用AsyncTask或创建自己的线程会给您带来并发问题,但它们与任何多线程编程相同,Android 没有什么特别之处。

在进行并发时要考虑的另一点是后台线程需要保持 aWakeLock否则 CPU 可能会进入睡眠状态。

于 2015-12-18T08:28:03.790 回答
-2

只是一些想法。您可以尝试为您的文件使用串行执行器,因此一次只能执行一个线程。 http://developer.android.com/reference/android/os/AsyncTask.html#SERIAL_EXECUTOR

于 2015-12-18T08:34:07.880 回答