如何确保我的Android应用不会同时访问文件?

时间:2015-12-18 07:43:06

标签: java android multithreading android-service square-tape

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

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

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

我正在考虑使用IntentService基本上充当所有这些操作的队列。从那以后 - 至少我已经阅读了多少 - IntentServices在单个工作人员Intent中按顺序处理Thread,这两个操作不应该同时发生,对吗?

目前我想安排PeriodicTask GcmNetworkManager<div class="row"> <div class="col-md-6" style="text-align:center;"> {% if lessons %} <div class="form-group"> <label for="sel1" class="yekan-small" style="text-align:center; color:#CC0033;">select lesson</label> <select multiple class="form-control yekan-small" id="lesson_select"> {% for lesson in lessons %} <option>{{lesson.Code}} - {{lesson.Name}}</option> {% endfor %} </select> </div> {% else %} <div class="alert alert-danger yekan-small">Error</div> {% endif %} </div> <div class="col-md-6" style="text-align:center;"> {% if students %} <div class="form-group"> <label for="sel1" class="yekan-small" style="text-align:center; color:#CC0033;">select student</label> <select multiple class="form-control yekan-small" id="student_select"> {% for student in students %} <option>{{student.First_Name}} {{student.Last_Name}} - {{student.STNO}}</option> {% endfor %} </select> </div> {% else %} <div class="alert alert-danger yekan-small">Error</div> {% endif %} </div> <div class="row"> <div class="col-md-6" style="text-align:center;"> <label class="yekan-small">student</label><br/> {{form.student_code }} </div> <div class="col-md-6" style="text-align:center;"> <label class="yekan-small">lesson</label><br/> {{form.lesson_code}} </div> </div> </div> 负责将数据发送到服务器。有没有更好的方法来做这一切?

3 个答案:

答案 0 :(得分: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
  • 执行
  • 您将具有可配置的自动重试和电池效率退避模式
  • 任务将在您指定的时间窗口内执行。

我建议初学者阅读this以了解有关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());

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

顺便说一句:是的,IntentService在一个工作人员Intent中依次处理每个Thread。您可以查看相关的实施here。它实际上非常简单而且很直接。

答案 1 :(得分:0)

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

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

另请注意,主线程上可能禁止执行I / O和/或网络请求(具体取决于Android版本,请参阅How to fix android.os.NetworkOnMainThreadException?)。

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

进行并发时需要考虑的另一点是后台线程需要持有WakeLock,否则CPU可能会进入休眠状态。

答案 2 :(得分:-2)

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