網站首頁 編程語言 正文
前言
假如你做了一個云盤類的app,或者可以保存用戶導入的配置。用戶在未來肯定需要獲取這些文件,一個辦法是寫一個Activity,向一個文件管理軟件一樣把他們列出來。但是這個有一個問題是用戶必須進入app 才能訪問。
現在有一個解決方案是實現一個DocumentProvider
步驟
DocumentProvider 繼承自Content Provider。
首先在Manifest 中注冊這個Provider
<provider android:name=".StorageProvider" android:authorities="com.storyteller_f.ping.documents" android:grantUriPermissions="true" android:exported="true" android:permission="android.permission.MANAGE_DOCUMENTS"> <intent-filter> <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> </intent-filter> </provider>
創建這個Provider
class StorageProvider : DocumentsProvider() {
companion object {
private val DEFAULT_ROOT_PROJECTION: Array<String> = arrayOf(
DocumentsContract.Root.COLUMN_ROOT_ID,
DocumentsContract.Root.COLUMN_MIME_TYPES,
DocumentsContract.Root.COLUMN_FLAGS,
DocumentsContract.Root.COLUMN_ICON,
DocumentsContract.Root.COLUMN_TITLE,
DocumentsContract.Root.COLUMN_SUMMARY,
DocumentsContract.Root.COLUMN_DOCUMENT_ID,
DocumentsContract.Root.COLUMN_AVAILABLE_BYTES
)
private val DEFAULT_DOCUMENT_PROJECTION: Array<String> = arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_MIME_TYPE,
DocumentsContract.Document.COLUMN_FLAGS,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_LAST_MODIFIED,
DocumentsContract.Document.COLUMN_SIZE
)
private const val TAG = "StorageProvider"
}
override fun onCreate(): Boolean {
return true
}
}
重寫queryRoot
在我們的需求下只有一種情況,訪問的路徑應該是/data/data/packagename/,不過如果一個app 支持多用戶,那就應該有多個目錄。所以需要一個方法告知文件管理應用我們的app 有多少個根。
override fun queryRoots(projection: Array<out String>?): Cursor {
Log.d(TAG, "queryRoots() called with: projection = $projection")
val flags = DocumentsContract.Root.FLAG_LOCAL_ONLY or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
return MatrixCursor(projection ?: DEFAULT_ROOT_PROJECTION).apply {
newRow().apply {
add(DocumentsContract.Root.COLUMN_ROOT_ID, DEFAULT_ROOT_ID)
add(DocumentsContract.Root.COLUMN_MIME_TYPES, DocumentsContract.Document.MIME_TYPE_DIR)
add(DocumentsContract.Root.COLUMN_FLAGS, flags)
add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_launcher_foreground)
add(DocumentsContract.Root.COLUMN_TITLE, context?.getString(R.string.app_name))
add(DocumentsContract.Root.COLUMN_SUMMARY, "your data")
add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, "/")
}
}
}
返回值是Cursor 就像一個數據庫連接查詢數據時一樣,不過我們沒有操作數據庫,返回的數據是文件系統。所以我們返回一個MatrixCursor。
返回的數據沒有什么真實數據,僅僅作為一個入口。
此時打開Android 系統的文件管理,就能看到了
此時點擊的話就會崩潰,因為還沒有重寫queryDocument
重寫queryDocument
/**
* Return metadata for the single requested document. You should avoid
* making network requests to keep this request fast.
*
* @param documentId the document to return.
* @param projection list of {@link Document} columns to put into the
* cursor. If {@code null} all supported columns should be
* included.
* @throws AuthenticationRequiredException If authentication is required from
* the user (such as login credentials), but it is not guaranteed
* that the client will handle this properly.
*/
public abstract Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException;
這個方法才是真正返回數據的地方,上面返回root 更像是盤符,這里是返回root 盤符對應的根文件夾。數據結構是一個樹形結構,queryDocument 返回根文件夾,根只有一個,所以返回的數據也是只有一個。如果你返回了多個數據應該也是無效的,系統會忽略掉。
一般我們需要根據documentId (盤符)返回,不過這里只有一個,就不做區分了。
override fun queryDocument(documentId: String?, projection: Array<out String>?): Cursor {
Log.d(TAG, "queryDocument() called with: documentId = $documentId, projection = $projection")
return MatrixCursor(projection ?: DEFAULT_DOCUMENT_PROJECTION).apply {
val root = context?.filesDir?.parentFile ?: return@apply
newRow().apply {
add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, "/")
add(DocumentsContract.Document.COLUMN_MIME_TYPE, DocumentsContract.Document.MIME_TYPE_DIR)
val flags = 0
add(DocumentsContract.Document.COLUMN_FLAGS, flags)
add(DocumentsContract.Document.COLUMN_DISPLAY_NAME, root.name)
add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, root.lastModified())
add(DocumentsContract.Document.COLUMN_SIZE, 0)
}
}
}
重寫getChildDocument
override fun queryChildDocuments(parentDocumentId: String?, projection: Array<out String>?, sortOrder: String?): Cursor {
Log.d(TAG, "queryChildDocuments() called with: parentDocumentId = $parentDocumentId, projection = $projection, sortOrder = $sortOrder")
return MatrixCursor(projection ?: DEFAULT_DOCUMENT_PROJECTION).apply {
handleChild(parentDocumentId)
}
}
我們只需要根據parentDocumentId 來搜索就可以。就像一個遍歷出一個文件夾的子文件夾和子文件一樣。
除了上面介紹的,還可以繼承打開document,創建document,顯示document thumbnail 等功能。
代碼可以在這里看 github.com/storyteller…
原文鏈接:https://juejin.cn/post/7180568940174082106
相關推薦
- 2022-12-21 Docker教程之dockerfile構建centos鏡像_docker
- 2022-10-23 在Asp.net?core項目中使用WebSocket_實用技巧
- 2022-12-05 Spark中的數據讀取保存和累加器實例詳解_相關技巧
- 2022-07-30 Linux常見命令-壓縮和解壓類 一、gzip/gunzip 壓縮 二、zip/unzip 壓縮 三
- 2023-01-11 ubuntu如何搭建vsftpd服務器_FTP服務器
- 2022-03-28 Python中三種條件語句示例介紹_python
- 2022-11-07 使用react-native-doc-viewer實現文檔預覽_React
- 2021-12-09 VS2017開發C語言出現“no_init_all“的解決辦法_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支