Android开发:如何通过Intent.ACTION_OPEN_DOCUMENT的URI获取文件修改时间并监听变更
Great question—let’s break this down step by step since dealing with document URIs in Android can be tricky, especially when you need to track changes without draining resources.
1. Get Last Modified Time Directly from the URI
Yes, you absolutely can retrieve the last modified timestamp directly from the content:// URI you get from ACTION_OPEN_DOCUMENT—no need to copy the entire file first. Use the ContentResolver to query the document's metadata, specifically the COLUMN_LAST_MODIFIED field from DocumentsContract.Document.
Here’s a quick Kotlin example:
fun getFileLastModified(uri: Uri, context: Context): Long? { val projection = arrayOf(DocumentsContract.Document.COLUMN_LAST_MODIFIED) context.contentResolver.query(uri, projection, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { val columnIndex = cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_LAST_MODIFIED) return cursor.getLong(columnIndex) } } return null }
This works reliably starting from API 19 (KitKat)—which is exactly the minimum API level for ACTION_OPEN_DOCUMENT. It’s lightweight, avoids copying files, and gives you the exact timestamp you need to compare changes.
2. Why Avoid Trying to Get the Absolute File Path
Let’s cut to the chase: getting an absolute path from a content:// URI is almost never worth the effort.
- For files in scoped storage (Android 10+), the system intentionally hides absolute paths to enforce privacy and security. Any workaround you find will likely break on newer OS versions or different device manufacturers.
- Files from cloud storage apps (like Google Drive) or other third-party document providers don’t even have a local absolute path to begin with.
Sticking with the content:// URI is the only future-proof, compatible approach here.
3. Better Alternatives to 5-Second Polling
Instead of copying the file every 5 seconds (which is indeed resource-heavy), use these more efficient methods:
Option A: ContentObserver (Recommended)
Register a ContentObserver with the ContentResolver to listen for changes to the URI. The system will notify you automatically when the file is modified, so you don’t need to poll at all.
Example implementation:
class FileChangeObserver( private val targetUri: Uri, private val onFileChanged: () -> Unit ) : ContentObserver(Handler(Looper.getMainLooper())) { override fun onChange(selfChange: Boolean) { super.onChange(selfChange) // Trigger your file update logic here onFileChanged.invoke() } } // To register the observer (e.g., in your Activity/Fragment) val observer = FileChangeObserver(selectedUri) { // Read the updated file content here readUpdatedFileContent(selectedUri, this) } contentResolver.registerContentObserver(selectedUri, true, observer) // Don't forget to unregister when you're done (e.g., in onDestroy) // contentResolver.unregisterContentObserver(observer)
Note: Some third-party document apps might not properly trigger ContentObserver callbacks. As a fallback, you can combine this with a low-frequency poll (e.g., every 30 seconds) using the getFileLastModified method above to catch any missed changes.
Option B: FileDescriptor + Inotify (Advanced)
If you need even more granular monitoring, you can open a ParcelFileDescriptor from the URI and use Linux’s inotify system to watch for changes. This is more complex but offers real-time updates. However, it requires handling native code or using a library, so it’s overkill for most use cases.
Final Recommendation
Stick with the ContentObserver + occasional last modified check combo. It’s efficient, compatible, and avoids the pitfalls of trying to get absolute file paths. The getContentResolver().query method for last modified time is your go-to for quick, lightweight checks when needed.
内容的提问来源于stack exchange,提问作者hubbabubba

