Android如何實(shí)現(xiàn)一個(gè)DocumentProvider示例詳解
前言
假如你做了一個(gè)云盤類的app,或者可以保存用戶導(dǎo)入的配置。用戶在未來(lái)肯定需要獲取這些文件,一個(gè)辦法是寫一個(gè)Activity,向一個(gè)文件管理軟件一樣把他們列出來(lái)。但是這個(gè)有一個(gè)問(wèn)題是用戶必須進(jìn)入app 才能訪問(wèn)。
現(xiàn)在有一個(gè)解決方案是實(shí)現(xiàn)一個(gè)DocumentProvider
步驟
DocumentProvider 繼承自Content Provider。
首先在Manifest 中注冊(cè)這個(gè)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>
創(chuàng)建這個(gè)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
在我們的需求下只有一種情況,訪問(wèn)的路徑應(yīng)該是/data/data/packagename/,不過(guò)如果一個(gè)app 支持多用戶,那就應(yīng)該有多個(gè)目錄。所以需要一個(gè)方法告知文件管理應(yīng)用我們的app 有多少個(gè)根。
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 就像一個(gè)數(shù)據(jù)庫(kù)連接查詢數(shù)據(jù)時(shí)一樣,不過(guò)我們沒(méi)有操作數(shù)據(jù)庫(kù),返回的數(shù)據(jù)是文件系統(tǒng)。所以我們返回一個(gè)MatrixCursor。
返回的數(shù)據(jù)沒(méi)有什么真實(shí)數(shù)據(jù),僅僅作為一個(gè)入口。
此時(shí)打開(kāi)Android 系統(tǒng)的文件管理,就能看到了

此時(shí)點(diǎn)擊的話就會(huì)崩潰,因?yàn)檫€沒(méi)有重寫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;
這個(gè)方法才是真正返回?cái)?shù)據(jù)的地方,上面返回root 更像是盤符,這里是返回root 盤符對(duì)應(yīng)的根文件夾。數(shù)據(jù)結(jié)構(gòu)是一個(gè)樹(shù)形結(jié)構(gòu),queryDocument 返回根文件夾,根只有一個(gè),所以返回的數(shù)據(jù)也是只有一個(gè)。如果你返回了多個(gè)數(shù)據(jù)應(yīng)該也是無(wú)效的,系統(tǒng)會(huì)忽略掉。
一般我們需要根據(jù)documentId (盤符)返回,不過(guò)這里只有一個(gè),就不做區(qū)分了。
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)
}
}
我們只需要根據(jù)parentDocumentId 來(lái)搜索就可以。就像一個(gè)遍歷出一個(gè)文件夾的子文件夾和子文件一樣。
除了上面介紹的,還可以繼承打開(kāi)document,創(chuàng)建document,顯示document thumbnail 等功能。
代碼可以在這里看 github.com/storyteller…
以上就是Android如何實(shí)現(xiàn)一個(gè)DocumentProvider示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Android實(shí)現(xiàn)DocumentProvider的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android開(kāi)發(fā)實(shí)現(xiàn)根據(jù)字母快速定位側(cè)邊欄
這篇文章主要為大家詳細(xì)介紹了Android開(kāi)發(fā)實(shí)現(xiàn)根據(jù)字母快速定位側(cè)邊欄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Android下拉列表(Spinner)效果(使用C#和Java分別實(shí)現(xiàn))
這篇文章主要介紹了Android下拉列表(Spinner)效果(使用C#和Java分別實(shí)現(xiàn)),本文直接給出效果圖和兩種語(yǔ)言的實(shí)現(xiàn)代碼及布局代碼,需要的朋友可以參考下2015-06-06
Android自動(dòng)獲取輸入短信驗(yàn)證碼庫(kù)AutoVerifyCode詳解
這篇文章主要為大家詳細(xì)介紹了Android自動(dòng)獲取輸入短信驗(yàn)證碼庫(kù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
Android Studio實(shí)現(xiàn)華為手機(jī)的充電動(dòng)畫效果
本篇文章介紹了我參照華為手機(jī)的充電動(dòng)畫來(lái)仿照實(shí)現(xiàn)的樣例,這個(gè)動(dòng)畫并不難實(shí)現(xiàn),不過(guò)案例精簡(jiǎn)具有參考意義,需要的朋友快往下看吧2021-10-10
Android系統(tǒng)進(jìn)程間通信Binder機(jī)制在應(yīng)用程序框架層的Java接口源代碼分析
本文主要介紹 Android系統(tǒng)進(jìn)程間通信Binder機(jī)制Java 接口源碼分析,這里詳細(xì)介紹了如何實(shí)現(xiàn)Binder 機(jī)制和Java接口直接的通信,有興趣的小伙伴可以參考下2016-08-08
Android TimeLine 時(shí)間節(jié)點(diǎn)軸的實(shí)現(xiàn)實(shí)例代碼
本篇文章主要介紹了Android TimeLine 時(shí)間節(jié)點(diǎn)軸的實(shí)現(xiàn)實(shí)例代碼,這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2017-03-03
android 獲取本機(jī)的IP地址和mac物理地址的實(shí)現(xiàn)方法
本文主要介紹android 獲取本機(jī)的IP地址和mac物理地址的實(shí)現(xiàn)方法,這里提供示例代碼,實(shí)現(xiàn)功能,有需要的小伙伴可以參考下2016-09-09
android tv列表焦點(diǎn)記憶實(shí)現(xiàn)的方法
本篇文章主要介紹了android tv列表焦點(diǎn)記憶實(shí)現(xiàn)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
Android開(kāi)發(fā)常用經(jīng)典代碼段集錦
這篇文章主要介紹了Android開(kāi)發(fā)常用經(jīng)典代碼段,涉及Android開(kāi)發(fā)過(guò)程中針對(duì)手機(jī)、聯(lián)系人、圖片、存儲(chǔ)卡等的相關(guān)操作技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2016-02-02

