File.lastModified() никогда не был установлен с file.setLastModified()

У меня проблема с набором миллисов и чтение на Android 2.3.4 на Nexus One. Это код:

File fileFolder = new File(Environment.getExternalStorageDirectory(), appName + "/"
    + URLDecoder.decode(folder.getUrl()));
if (fileFolder != null && !fileFolder.exists()) {
  fileFolder.setLastModified(1310198774);
  fileFolder.mkdirs();
  fileFolder.setLastModified(1310198774);
}

if (fileFolder != null && fileFolder.exists()) {
  long l = fileFolder.lastModified();
}

В этом небольшом тесте я пишу 1310198774, но результат, который возвращается из lastModified(), равен 1310199771000.

Даже если я вырезаю конечный "000", разница в несколько минут.

Мне нужно синхронизировать файлы между веб-сервисом и устройством Android. Минуты последней модификации являются частью данных, отправленных этой службой. Я устанавливаю миллионы для созданных/скопированных файлов и папок, чтобы проверить, нужно ли перезаписывать файл/папку.

Все работает, но миллины, которые возвращаются из файловой системы, отличаются от значений, которые были установлены.

Я уверен, что с моим кодом что-то не так, но я не могу его найти.

Большое спасибо заранее. HJW

Ответ 1

Так что, возможно, я что-то пропустил, но вижу некоторые проблемы с вашим кодом выше. Ваша конкретная проблема может быть вызвана (как указано в @JB) на проблемы с Android, но для потомков я думал, что дам ответ.

Во-первых, File.setLastModified() занимает время в миллисекундах. Вот javadocs. Кажется, вы пытаетесь установить его за считанные секунды. Таким образом, ваш код должен выглядеть примерно так:

fileFolder.setLastModified(1310198774000L);

Как упоминалось в javadocs, многие файловые системы поддерживают только секундную детализацию секунд для последней модификации. Поэтому, если вам нужно увидеть такое же время модификации в файле, вы должны сделать что-то вроде следующего:

private void changeModificationFile(File file, long time) {
    // round the value down to the nearest second
    file.setLastModified((time / 1000) * 1000);
}

Ответ 2

На желе Bean + он отличается (в основном, на устройствах Nexus еще и другими, которые используют новый слой плавкого предохранителя для эмуляции эмуляции /mnt/shell/emulated sdcard):

Это проблема разрешения VFS, syscall utimensat() терпит неудачу с EPERM из-за несоответствующих разрешений (например, права собственности).

в платформе /system/core/sdcard/sdcard.c:

/* все файлы, принадлежащие root.sdcard */
attr- > uid = 0;
attr- > gid = AID_SDCARD_RW;

Из страницы utimensat() syscall man:

2. the caller effective user ID must match the owner of the file; or  
3. the caller must have appropriate privileges.  

To make any change other than setting both timestamps to the current time  
(i.e., times is not NULL, and both tv_nsec fields are not UTIME_NOW and both  
tv_nsec fields are not UTIME_OMIT), either condition 2 or 3 above must apply.  

Старый FAT предлагает переопределить флаг iattr- > valid с помощью опции mount, чтобы разрешить изменение временных меток кому-либо, FUSE + Android sdcard-FUSE не делает этого в данный момент (поэтому сбой inode_change_ok() завершается с ошибкой) и попытка отклоняется с помощью -EPERM. Здесь FAT./fs/fat/file.c:

/* Check for setting the inode time. */  
ia_valid = attr->ia_valid;  
if (ia_valid & TIMES_SET_FLAGS) {  
    if (fat_allow_set_time(sbi, inode))  
        attr->ia_valid &= ~TIMES_SET_FLAGS;  
}  

error = inode_change_ok(inode, attr);

Я также добавил эту информацию в эту открытую ошибку.

Ответ 3

Если это не работает, попробуйте это (уродливое) обходное решение, указанное в https://code.google.com/p/android/issues/detail?id=18624:

//As a workaround, this ugly hack will set the last modified date to now:      
RandomAccessFile raf = new RandomAccessFile(file, "rw");
long length = raf.length();
raf.setLength(length + 1);
raf.setLength(length);
raf.close();

Ответ 4

Работает на некоторых устройствах, но не на других. Не разрабатывайте решение, основанное на его работе. См. https://code.google.com/p/android/issues/detail?id=18624#c29

Вот простой тест, чтобы увидеть, работает ли он.

public void testSetLastModified() throws IOException {
    long time = 1316137362000L;
    File file = new File("/mnt/sdcard/foo");
    file.createNewFile();
    file.setLastModified(time);
    assertEquals(time, file.lastModified());
}

Ответ 5

Если вы хотите изменить дату/время каталога на текущую дату/время (т.е. "сейчас" ), вы можете создать какой-то временный файл внутри этого каталога, записать что-нибудь в него, а затем сразу удали это. Это приводит к изменению даты/времени "lastModified()" для текущей даты/времени. Однако это не сработает, если вы хотите изменить дату/время каталога на какое-либо другое случайное значение и не можете быть применены к файлу, очевидно.