Dosyalar için depolama hizmetleri sağlayan bir uygulama (ör. buluta kaydetme hizmetinizle birlikte) dosyalarınızı Özel bir doküman sağlayıcı yazarak Depolama Erişim Çerçevesi (SAF) Bu sayfada, özel doküman sağlayıcısının nasıl oluşturulacağı açıklanmaktadır.
Depolama Erişim Çerçevesi'nin işleyiş şekli hakkında daha fazla bilgi için Depolama Erişim Çerçevesi'ne genel bakış
Manifest
Özel bir doküman sağlayıcı uygulamak için aşağıdakileri ekleyin: manifesto:
- API düzeyi 19 veya üstünü hedefleyen bir hedef.
- Özel depolama alanınızı tanımlayan bir
<provider>
öğesi sağlar. -
android:name
özelliğiDocumentsProvider
alt sınıf, sınıfının adıdır. Bu paket adı, paket adını da içerir:com.example.android.storageprovider.MyCloudProvider
. -
android:authority
özelliği, yani paketinizin adıdır (bu örnekte,com.example.android.storageprovider
) ve içerik sağlayıcının türünü (documents
). android:exported
özelliği"true"
olarak ayarlandı. Diğer uygulamaların görebilmesi için sağlayıcınızı dışa aktarmanız gerekir.android:grantUriPermissions
özelliği"true"
. Bu ayar, sistemin diğer uygulamalara erişim izni vermesine olanak tanır daha fazla bilgi edineceksiniz. Bu diğer uygulamaların daha fazla bilgi edinmek için Kalıcı izinleriyle ilgili daha fazla bilgi edinin.MANAGE_DOCUMENTS
izni. Varsayılan olarak bir sağlayıcı mevcuttur herkese açık hale getiriyoruz. Bu iznin eklenmesi, sağlayıcınızın sistemle kısıtlanmasını sağlar. Bu kısıtlama, güvenlik açısından önemlidir.- Şunu içeren bir intent filtresi:
android.content.action.DOCUMENTS_PROVIDER
işlemi uygulayarak sağlayıcınızın Sistem, sağlayıcı aradığında seçicide görünür.
Aşağıda, bir sağlayıcı içeren örnek bir manifest'ten alıntılar verilmiştir:
<manifest... > ... <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> .... <provider android:name="com.example.android.storageprovider.MyCloudProvider" android:authorities="com.example.android.storageprovider.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> </application> </manifest>
Android 4.3 ve önceki sürümleri çalıştıran cihazları destekleyen cihazlar
İlgili içeriği oluşturmak için kullanılan
ACTION_OPEN_DOCUMENT
intent yalnızca kullanılabilir
Android 4.4 ve sonraki sürümleri çalıştıran cihazlarda.
Uygulamanızın ACTION_GET_CONTENT
özelliğini desteklemesini istiyorsanız
Android 4.3 ve önceki sürümleri çalıştıran cihazları kullanılabilmesi için
şurada ACTION_GET_CONTENT
intent filtresini devre dışı bırakın:
manifest dosyanız. CEVAP
belge sağlayıcı ve ACTION_GET_CONTENT
göz önünde bulundurulmalıdır
birlikte kullanılamaz. Her ikisini de aynı anda destekliyorsanız uygulamanız
sistem seçici kullanıcı arayüzünde iki kez görünür ve iki farklı erişim yolu sunar.
saklı tutar. Bu durum kullanıcıların kafasını karıştırabilir.
Cihazlar için ACTION_GET_CONTENT
intent filtresi
Android 4.4 veya daha yeni bir sürüm kullanıyorsanız:
res/values/
altındakibool.xml
kaynakları dosyanıza bu satırda:<bool name="atMostJellyBeanMR2">true</bool>
res/values-v19/
altındakibool.xml
kaynakları dosyanıza bu satırda:<bool name="atMostJellyBeanMR2">false</bool>
- Bir
etkinlik
takma adı ile
ACTION_GET_CONTENT
amacını devre dışı bırakın 4.4 (API düzeyi 19) ve sonraki sürümler için filtre uygulayın. Örnek:<!-- This activity alias is added so that GET_CONTENT intent-filter can be disabled for builds on API level 19 and higher. --> <activity-alias android:name="com.android.example.app.MyPicker" android:targetActivity="com.android.example.app.MyActivity" ... android:enabled="@bool/atMostJellyBeanMR2"> <intent-filter> <action android:name="android.intent.action.GET_CONTENT" /> <category android:name="android.intent.category.OPENABLE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> <data android:mimeType="video/*" /> </intent-filter> </activity-alias>
.
Sözleşmeler
Özel bir içerik sağlayıcı yazdığınızda genellikle görevlerden biri
(bkz.
İçerik Sağlayıcılar geliştirici kılavuzu. Sözleşmeli sınıf, public final
sınıftır
sütun adları, MIME türleri ve
meta verileri de ekleyebilirsiniz. SAF
bu sözleşme derslerini size sunar. Böylece,
sahibi:
Örneğin, Belge sağlayıcınızın dokümanlar veya kök dizin için sorgulanması:
Kotlin
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_DISPLAY_NAME, DocumentsContract.Document.COLUMN_LAST_MODIFIED, DocumentsContract.Document.COLUMN_FLAGS, DocumentsContract.Document.COLUMN_SIZE )
Java
private static final String[] DEFAULT_ROOT_PROJECTION = new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES,}; private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};
Kök dizininizin bazı gerekli sütunları içermesi gerekir. Bu sütunlar şunlardır:
Dokümanlar imlecinin aşağıdaki gerekli sütunları içermesi gerekir:
COLUMN_DOCUMENT_ID
COLUMN_DISPLAY_NAME
COLUMN_MIME_TYPE
COLUMN_FLAGS
COLUMN_SIZE
COLUMN_LAST_MODIFIED
DocumentsProvider alt sınıfı oluşturma
Özel bir doküman sağlayıcısı yazmanın bir sonraki adımı,
soyut sınıf DocumentsProvider
. En azından
aşağıdaki yöntemleri uygulayın:
Mutlaka uygulamanız gereken yöntemler bunlardır ancak
arama yapabilirsiniz. Bkz. DocumentsProvider
inceleyebilirsiniz.
Kök tanımlayın
queryRoots()
uygulamanızın, tümCursor
içinde tanımlanan sütunları kullanarak doküman sağlayıcınızın kök dizinlerini
DocumentsContract.Root
.
Aşağıdaki snippet'te projection
parametresi
arayanın geri dönmek istediği belirli alanlar vardır. Snippet yeni bir imleç oluşturuyor
ve ona bir satır ekler: Bir kök, bir üst düzey dizin,
İndirilenler veya Resimler. Çoğu sağlayıcının yalnızca bir kökü vardır. Birden fazla
birden fazla kullanıcı hesabı olduğunda. Bu durumda,
imleçten ikinci satırı seçin.
Kotlin
override fun queryRoots(projection: Array<out String>?): Cursor { // Use a MatrixCursor to build a cursor // with either the requested fields, or the default // projection if "projection" is null. val result = MatrixCursor(resolveRootProjection(projection)) // If user is not logged in, return an empty root cursor. This removes our // provider from the list entirely. if (!isUserLoggedIn()) { return result } // It's possible to have multiple roots (e.g. for multiple accounts in the // same app) -- just add multiple cursor rows. result.newRow().apply { add(DocumentsContract.Root.COLUMN_ROOT_ID, ROOT) // You can provide an optional summary, which helps distinguish roots // with the same title. You can also use this field for displaying an // user account name. add(DocumentsContract.Root.COLUMN_SUMMARY, context.getString(R.string.root_summary)) // FLAG_SUPPORTS_CREATE means at least one directory under the root supports // creating documents. FLAG_SUPPORTS_RECENTS means your application's most // recently used documents will show up in the "Recents" category. // FLAG_SUPPORTS_SEARCH allows users to search all documents the application // shares. add( DocumentsContract.Root.COLUMN_FLAGS, DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_RECENTS or DocumentsContract.Root.FLAG_SUPPORTS_SEARCH ) // COLUMN_TITLE is the root title (e.g. Gallery, Drive). add(DocumentsContract.Root.COLUMN_TITLE, context.getString(R.string.title)) // This document id cannot change after it's shared. add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocIdForFile(baseDir)) // The child MIME types are used to filter the roots and only present to the // user those roots that contain the desired type somewhere in their file hierarchy. add(DocumentsContract.Root.COLUMN_MIME_TYPES, getChildMimeTypes(baseDir)) add(DocumentsContract.Root.COLUMN_AVAILABLE_BYTES, baseDir.freeSpace) add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_launcher) } return result }
Java
@Override public Cursor queryRoots(String[] projection) throws FileNotFoundException { // Use a MatrixCursor to build a cursor // with either the requested fields, or the default // projection if "projection" is null. final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection)); // If user is not logged in, return an empty root cursor. This removes our // provider from the list entirely. if (!isUserLoggedIn()) { return result; } // It's possible to have multiple roots (e.g. for multiple accounts in the // same app) -- just add multiple cursor rows. final MatrixCursor.RowBuilder row = result.newRow(); row.add(Root.COLUMN_ROOT_ID, ROOT); // You can provide an optional summary, which helps distinguish roots // with the same title. You can also use this field for displaying an // user account name. row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary)); // FLAG_SUPPORTS_CREATE means at least one directory under the root supports // creating documents. FLAG_SUPPORTS_RECENTS means your application's most // recently used documents will show up in the "Recents" category. // FLAG_SUPPORTS_SEARCH allows users to search all documents the application // shares. row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_RECENTS | Root.FLAG_SUPPORTS_SEARCH); // COLUMN_TITLE is the root title (e.g. Gallery, Drive). row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title)); // This document id cannot change after it's shared. row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(baseDir)); // The child MIME types are used to filter the roots and only present to the // user those roots that contain the desired type somewhere in their file hierarchy. row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(baseDir)); row.add(Root.COLUMN_AVAILABLE_BYTES, baseDir.getFreeSpace()); row.add(Root.COLUMN_ICON, R.drawable.ic_launcher); return result; }
Belge sağlayıcınız dinamik bir kök kümesine (ör. bir USB kablosuna) bağlanıyorsa
bağlantısı kesilmiş olabilecek bir cihaz veya kullanıcının oturumunu kapatabileceği bir hesap
dokümanın kullanıcı arayüzünü,
ContentResolver.notifyChange()
yöntemini çağırın.
Kotlin
val rootsUri: Uri = DocumentsContract.buildRootsUri(BuildConfig.DOCUMENTS_AUTHORITY) context.contentResolver.notifyChange(rootsUri, null)
Java
Uri rootsUri = DocumentsContract.buildRootsUri(BuildConfig.DOCUMENTS_AUTHORITY); context.getContentResolver().notifyChange(rootsUri, null);
Sağlayıcıdaki dokümanları listeleme
queryChildDocuments()
.
içindeki tüm dosyalara işaret eden bir Cursor
döndürmelidir
şunda tanımlanan sütunları kullanarak belirtilen dizin:
DocumentsContract.Document
.
Bu yöntem, kullanıcı seçici arayüzünde kökünüzü seçtiğinde çağrılır.
Yöntem,
COLUMN_DOCUMENT_ID
Kullanıcı bir öğeyi seçtiğinde sistem bu yöntemi
alt dizine ekleyin.
Bu snippet, istenen sütunlarla yeni bir imleç oluşturur ve bilgileri içerir. Alt öğe bir resim, başka bir dizin ve herhangi bir dosya olabilir:
Kotlin
override fun queryChildDocuments( parentDocumentId: String?, projection: Array<out String>?, sortOrder: String? ): Cursor { return MatrixCursor(resolveDocumentProjection(projection)).apply { val parent: File = getFileForDocId(parentDocumentId) parent.listFiles() .forEach { file -> includeFile(this, null, file) } } }
Java
@Override public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException { final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); final File parent = getFileForDocId(parentDocumentId); for (File file : parent.listFiles()) { // Adds the file's display name, MIME type, size, and so on. includeFile(result, null, file); } return result; }
Belge bilgilerini al
queryDocument()
.
belirtilen dosyaya işaret eden bir Cursor
döndürmesi gerekir,
(DocumentsContract.Document
adresinde tanımlanan sütunlar kullanılıyor).
queryDocument()
yönteminde,
queryChildDocuments()
,
belirli bir dosya için:
Kotlin
override fun queryDocument(documentId: String?, projection: Array<out String>?): Cursor { // Create a cursor with the requested projection, or the default projection. return MatrixCursor(resolveDocumentProjection(projection)).apply { includeFile(this, documentId, null) } }
Java
@Override public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException { // Create a cursor with the requested projection, or the default projection. final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); includeFile(result, documentId, null); return result; }
Belge sağlayıcınız ayrıca,
geçersiz kılma
DocumentsProvider.openDocumentThumbnail()
yöntemini kullanarak
FLAG_SUPPORTS_THUMBNAIL
flag'i desteklenen dosyalara ekleyin.
Aşağıdaki kod snippet'i,
DocumentsProvider.openDocumentThumbnail()
Kotlin
override fun openDocumentThumbnail( documentId: String?, sizeHint: Point?, signal: CancellationSignal? ): AssetFileDescriptor { val file = getThumbnailFileForDocId(documentId) val pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) return AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH) }
Java
@Override public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { final File file = getThumbnailFileForDocId(documentId); final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); }
Dikkat:
Bir doküman sağlayıcı, iki kattan daha fazla küçük resim döndürmemelidir
sizeHint
parametresiyle belirtilen boyut.
Doküman açma
Şunu temsil eden bir ParcelFileDescriptor
döndürmek için openDocument()
uygulamanız gerekir:
belirtilen dosya. Diğer uygulamalar döndürülen ParcelFileDescriptor
kullanabilir
veri akışı gerçekleştirebilirsiniz. Kullanıcı bir dosya seçtikten sonra sistem bu yöntemi çağırır.
Ardından istemci uygulaması,
openFileDescriptor()
Örnek:
Kotlin
override fun openDocument( documentId: String, mode: String, signal: CancellationSignal ): ParcelFileDescriptor { Log.v(TAG, "openDocument, mode: $mode") // It's OK to do network operations in this method to download the document, // as long as you periodically check the CancellationSignal. If you have an // extremely large file to transfer from the network, a better solution may // be pipes or sockets (see ParcelFileDescriptor for helper methods). val file: File = getFileForDocId(documentId) val accessMode: Int = ParcelFileDescriptor.parseMode(mode) val isWrite: Boolean = mode.contains("w") return if (isWrite) { val handler = Handler(context.mainLooper) // Attach a close listener if the document is opened in write mode. try { ParcelFileDescriptor.open(file, accessMode, handler) { // Update the file with the cloud server. The client is done writing. Log.i(TAG, "A file with id $documentId has been closed! Time to update the server.") } } catch (e: IOException) { throw FileNotFoundException( "Failed to open document with id $documentId and mode $mode" ) } } else { ParcelFileDescriptor.open(file, accessMode) } }
Java
@Override public ParcelFileDescriptor openDocument(final String documentId, final String mode, CancellationSignal signal) throws FileNotFoundException { Log.v(TAG, "openDocument, mode: " + mode); // It's OK to do network operations in this method to download the document, // as long as you periodically check the CancellationSignal. If you have an // extremely large file to transfer from the network, a better solution may // be pipes or sockets (see ParcelFileDescriptor for helper methods). final File file = getFileForDocId(documentId); final int accessMode = ParcelFileDescriptor.parseMode(mode); final boolean isWrite = (mode.indexOf('w') != -1); if(isWrite) { // Attach a close listener if the document is opened in write mode. try { Handler handler = new Handler(getContext().getMainLooper()); return ParcelFileDescriptor.open(file, accessMode, handler, new ParcelFileDescriptor.OnCloseListener() { @Override public void onClose(IOException e) { // Update the file with the cloud server. The client is done // writing. Log.i(TAG, "A file with id " + documentId + " has been closed! Time to " + "update the server."); } }); } catch (IOException e) { throw new FileNotFoundException("Failed to open document with id" + documentId + " and mode " + mode); } } else { return ParcelFileDescriptor.open(file, accessMode); } }
Belge sağlayıcınız dosya akışı gerçekleştiriyorsa veya karmaşık dosya işlemleri yapıyorsa
veri yapılarını kullanıyorsanız
createReliablePipe()
.
veya
createReliableSocketPair()
yöntem.
Bu yöntemler bir çift
ParcelFileDescriptor
nesne (birini döndürebilirsiniz)
ve diğerini
ParcelFileDescriptor.AutoCloseOutputStream
veya
ParcelFileDescriptor.AutoCloseInputStream
.
Son dokümanları ve aramayı destekleyin
Web sitenizin kök dizininde, yakın zamanda değiştirilen dokümanların listesini
geçersiz kılarak doküman sağlayıcıyı
queryRecentDocuments()
yöntemi ve geri dönüyor
FLAG_SUPPORTS_RECENTS
,
Aşağıdaki kod snippet'i,
queryRecentDocuments()
yöntem.
Kotlin
override fun queryRecentDocuments(rootId: String?, projection: Array<out String>?): Cursor { // This example implementation walks a // local file structure to find the most recently // modified files. Other implementations might // include making a network call to query a // server. // Create a cursor with the requested projection, or the default projection. val result = MatrixCursor(resolveDocumentProjection(projection)) val parent: File = getFileForDocId(rootId) // Create a queue to store the most recent documents, // which orders by last modified. val lastModifiedFiles = PriorityQueue( 5, Comparator<File> { i, j -> Long.compare(i.lastModified(), j.lastModified()) } ) // Iterate through all files and directories // in the file structure under the root. If // the file is more recent than the least // recently modified, add it to the queue, // limiting the number of results. val pending : MutableList<File> = mutableListOf() // Start by adding the parent to the list of files to be processed pending.add(parent) // Do while we still have unexamined files while (pending.isNotEmpty()) { // Take a file from the list of unprocessed files val file: File = pending.removeAt(0) if (file.isDirectory) { // If it's a directory, add all its children to the unprocessed list pending += file.listFiles() } else { // If it's a file, add it to the ordered queue. lastModifiedFiles.add(file) } } // Add the most recent files to the cursor, // not exceeding the max number of results. for (i in 0 until Math.min(MAX_LAST_MODIFIED + 1, lastModifiedFiles.size)) { val file: File = lastModifiedFiles.remove() includeFile(result, null, file) } return result }
Java
@Override public Cursor queryRecentDocuments(String rootId, String[] projection) throws FileNotFoundException { // This example implementation walks a // local file structure to find the most recently // modified files. Other implementations might // include making a network call to query a // server. // Create a cursor with the requested projection, or the default projection. final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); final File parent = getFileForDocId(rootId); // Create a queue to store the most recent documents, // which orders by last modified. PriorityQueue lastModifiedFiles = new PriorityQueue(5, new Comparator() { public int compare(File i, File j) { return Long.compare(i.lastModified(), j.lastModified()); } }); // Iterate through all files and directories // in the file structure under the root. If // the file is more recent than the least // recently modified, add it to the queue, // limiting the number of results. final LinkedList pending = new LinkedList(); // Start by adding the parent to the list of files to be processed pending.add(parent); // Do while we still have unexamined files while (!pending.isEmpty()) { // Take a file from the list of unprocessed files final File file = pending.removeFirst(); if (file.isDirectory()) { // If it's a directory, add all its children to the unprocessed list Collections.addAll(pending, file.listFiles()); } else { // If it's a file, add it to the ordered queue. lastModifiedFiles.add(file); } } // Add the most recent files to the cursor, // not exceeding the max number of results. for (int i = 0; i < Math.min(MAX_LAST_MODIFIED + 1, lastModifiedFiles.size()); i++) { final File file = lastModifiedFiles.remove(); includeFile(result, null, file); } return result; }
Yukarıdaki snippet'e ilişkin kodun tamamını StorageProvider kod örneğidir.
Doküman oluşturmayı destekleyin
İstemci uygulamalarının doküman sağlayıcınızda dosya oluşturmasına izin verebilirsiniz.
Bir istemci uygulaması ACTION_CREATE_DOCUMENT
gönderirse
böyle bir isteğiniz varsa, doküman sağlayıcınız, o istemci uygulamasının
yeni dokümanlar ekleyebilirsiniz.
Belge oluşturma işleminin desteklenmesi için kök biriminizin
FLAG_SUPPORTS_CREATE
işareti.
Kendi içinde yeni dosyalar oluşturulmasına izin veren dizinlerin,
FLAG_DIR_SUPPORTS_CREATE
.
tıklayın.
Belge sağlayıcınızın ayrıca
createDocument()
yöntemini çağırın. Bir kullanıcı
yeni bir dosya kaydetmesi için doküman sağlayıcısına bir çağrı alır.
createDocument()
createDocument()
yöntemi, yeni bir yöntem döndürüyorsunuz
Şu tarih için COLUMN_DOCUMENT_ID
:
dosyası olarak kaydedebilirsiniz. Ardından istemci uygulaması, dosya için bir herkese açık kullanıcı adı almak üzere bu kimliği kullanabilir.
ve nihayetinde de
Yeni dosyaya yazmak için openDocument()
tuşlarına basın.
Aşağıdaki kod snippet'i, Doküman sağlayıcı olabilir.
Kotlin
override fun createDocument(documentId: String?, mimeType: String?, displayName: String?): String { val parent: File = getFileForDocId(documentId) val file: File = try { File(parent.path, displayName).apply { createNewFile() setWritable(true) setReadable(true) } } catch (e: IOException) { throw FileNotFoundException( "Failed to create document with name $displayName and documentId $documentId" ) } return getDocIdForFile(file) }
Java
@Override public String createDocument(String documentId, String mimeType, String displayName) throws FileNotFoundException { File parent = getFileForDocId(documentId); File file = new File(parent.getPath(), displayName); try { file.createNewFile(); file.setWritable(true); file.setReadable(true); } catch (IOException e) { throw new FileNotFoundException("Failed to create document with name " + displayName +" and documentId " + documentId); } return getDocIdForFile(file); }
Yukarıdaki snippet'e ilişkin kodun tamamını StorageProvider kod örneğidir.
Doküman yönetimi özelliklerini destekleme
Doküman sağlayıcınız, dosya açma, oluşturma ve görüntülemenin yanı sıra
istemci uygulamalarının yeniden adlandırma, kopyalama, taşıma ve silme işlemlerini yapmasına da izin verebilir
dosyası olarak da kaydedebilir. Google Drive'a doküman yönetimi işlevi eklemek için
bir bayrak ekleyin.
COLUMN_FLAGS
sütun
belirtin. Ayrıca ekip üyelerinden
ilgili DocumentsProvider
yöntemi
sınıfını kullanır.
Aşağıdaki tabloda
COLUMN_FLAGS
işareti
ve DocumentsProvider
yöntemi de dahil olmak üzere
sağlayıcının belirli özellikleri sunması gerekir.
Özellik | İşaretle | Yöntem |
---|---|---|
Dosya silme |
FLAG_SUPPORTS_DELETE
|
deleteDocument()
|
Dosyayı yeniden adlandırma |
FLAG_SUPPORTS_RENAME
|
renameDocument()
|
Dosyayı, doküman sağlayıcı içindeki yeni bir üst dizine kopyalama |
FLAG_SUPPORTS_COPY
|
copyDocument()
|
Bir dosyayı doküman sağlayıcı içinde bir dizinden diğerine taşıma |
FLAG_SUPPORTS_MOVE
|
moveDocument()
|
Bir dosyayı üst dizininden kaldırma |
FLAG_SUPPORTS_REMOVE
|
removeDocument()
|
Sanal dosyaları ve alternatif dosya biçimlerini destekleme
Sanal dosyalar Android 7.0'da (API seviyesi 24) kullanıma sunulan bir özellik, kullanılmayan dosyalara görüntüleme erişimi sağlamak için bayt kodu temsili kullanılır. Diğer uygulamaların sanal dosyaları görüntüleyebilmesini sağlamak için: Belge sağlayıcınızın, açılabilir alternatif bir dosya oluşturması gerekiyor. temsil eder.
Örneğin, bir doküman sağlayıcısının
diğer uygulamaların doğrudan açamayacağı biçim, temelde sanal bir dosya.
İstemci uygulaması bir ACTION_VIEW
niyeti gönderdiğinde
CATEGORY_OPENABLE
kategorisi hariç
Kullanıcılar bu sanal dosyaları doküman sağlayıcı içinden seçebilir
görüntüleyin. Ardından, belge sağlayıcı sanal dosyayı döndürür.
bir resim dosyası olarak yükleyebilirsiniz.
Böylece istemci uygulaması, kullanıcının görüntülemesi için sanal dosyayı açabilir.
Sağlayıcıdaki bir dokümanın sanal olduğunu beyan etmek için
FLAG_VIRTUAL_DOCUMENT
.
queryDocument()
yöntemidir. Bu işaret, istemci uygulamalarını, dosyanın doğrudan bir bağlantısı olmadığı konusunda uyarır
bayt kodu temsilidir ve doğrudan açılamaz.
Belge sağlayıcınızdaki bir dosyanın sanal olduğunu belirtirseniz
sunumunuzu başka bir yerde sunmanız önemle tavsiye edilir
Resim veya PDF gibi bir MIME türü. Belge sağlayıcı
tanımladığı alternatif MIME türlerini bildirir
geçersiz kılarak sanal bir dosyanın görüntülenmesini destekler.
getDocumentStreamTypes()
.
yöntemidir. İstemci uygulamaları
getStreamTypes(android.net.Uri, java.lang.String)
.
yöntemini çağırırken sistem
getDocumentStreamTypes()
tercih edebilirsiniz. İlgili içeriği oluşturmak için kullanılan
getDocumentStreamTypes()
.
yöntemi kullanılarak çalıştırılan
doküman sağlayıcının dosya için desteklediğini gösterir.
Müşteri, belirlediği bu
doküman sağlayıcının dokümanı görüntülenebilir bir dosyada oluşturabilmesini
istemci uygulaması,
openTypedAssetFileDescriptor()
.
yöntemini kullanır. Bu yöntem, belge sağlayıcının
openTypedDocument()
yöntemidir. Belge sağlayıcı, dosyayı istemci uygulamasına geri gönderir.
.
Aşağıdaki kod snippet'i,
getDocumentStreamTypes()
.
ve
openTypedDocument()
yöntemlerine göz atın.
Kotlin
var SUPPORTED_MIME_TYPES : Array<String> = arrayOf("image/png", "image/jpg") override fun openTypedDocument( documentId: String?, mimeTypeFilter: String, opts: Bundle?, signal: CancellationSignal? ): AssetFileDescriptor? { return try { // Determine which supported MIME type the client app requested. when(mimeTypeFilter) { "image/jpg" -> openJpgDocument(documentId) "image/png", "image/*", "*/*" -> openPngDocument(documentId) else -> throw IllegalArgumentException("Invalid mimeTypeFilter $mimeTypeFilter") } } catch (ex: Exception) { Log.e(TAG, ex.message) null } } override fun getDocumentStreamTypes(documentId: String, mimeTypeFilter: String): Array<String> { return when (mimeTypeFilter) { "*/*", "image/*" -> { // Return all supported MIME types if the client app // passes in '*/*' or 'image/*'. SUPPORTED_MIME_TYPES } else -> { // Filter the list of supported mime types to find a match. SUPPORTED_MIME_TYPES.filter { it == mimeTypeFilter }.toTypedArray() } } }
Java
public static String[] SUPPORTED_MIME_TYPES = {"image/png", "image/jpg"}; @Override public AssetFileDescriptor openTypedDocument(String documentId, String mimeTypeFilter, Bundle opts, CancellationSignal signal) { try { // Determine which supported MIME type the client app requested. if ("image/png".equals(mimeTypeFilter) || "image/*".equals(mimeTypeFilter) || "*/*".equals(mimeTypeFilter)) { // Return the file in the specified format. return openPngDocument(documentId); } else if ("image/jpg".equals(mimeTypeFilter)) { return openJpgDocument(documentId); } else { throw new IllegalArgumentException("Invalid mimeTypeFilter " + mimeTypeFilter); } } catch (Exception ex) { Log.e(TAG, ex.getMessage()); } finally { return null; } } @Override public String[] getDocumentStreamTypes(String documentId, String mimeTypeFilter) { // Return all supported MIME tyupes if the client app // passes in '*/*' or 'image/*'. if ("*/*".equals(mimeTypeFilter) || "image/*".equals(mimeTypeFilter)) { return SUPPORTED_MIME_TYPES; } ArrayList requestedMimeTypes = new ArrayList<>(); // Iterate over the list of supported mime types to find a match. for (int i=0; i < SUPPORTED_MIME_TYPES.length; i++) { if (SUPPORTED_MIME_TYPES[i].equals(mimeTypeFilter)) { requestedMimeTypes.add(SUPPORTED_MIME_TYPES[i]); } } return (String[])requestedMimeTypes.toArray(); }
Güvenlik
Belge sağlayıcınızın şifre korumalı bir bulut depolama alanı hizmeti olduğunu varsayalım.
ve dosyalarını paylaşmaya başlamadan önce kullanıcıların giriş yaptığından emin olmak istiyorsanız.
Kullanıcı giriş yapmamışsa uygulamanız ne yapmalıdır? Çözüm, sisteme
queryRoots()
uygulamanızda sıfır kök Yani boş bir kök imleç:
Kotlin
override fun queryRoots(projection: Array<out String>): Cursor { ... // If user is not logged in, return an empty root cursor. This removes our // provider from the list entirely. if (!isUserLoggedIn()) { return result }
Java
public Cursor queryRoots(String[] projection) throws FileNotFoundException { ... // If user is not logged in, return an empty root cursor. This removes our // provider from the list entirely. if (!isUserLoggedIn()) { return result; }
Diğer adım getContentResolver().notifyChange()
işlevini çağırmaktır.
DocumentsContract
adlı yeri hatırlıyor musunuz? Bunu kullanarak
bu URI'yı kullanın. Aşağıdaki snippet, sisteme
kullanıcının giriş durumu her değiştiğinde doküman sağlayıcısına izin verilir. Kullanıcı
giriş yapılmışsa queryRoots()
öğesine yapılan bir çağrı,
boş imleç, yukarıda gösterildiği gibi. Bu, sağlayıcının dokümanlarının yalnızca
Kullanıcı, sağlayıcıya giriş yaptığında kullanılabilir.
Kotlin
private fun onLoginButtonClick() { loginOrLogout() getContentResolver().notifyChange( DocumentsContract.buildRootsUri(AUTHORITY), null ) }
Java
private void onLoginButtonClick() { loginOrLogout(); getContentResolver().notifyChange(DocumentsContract .buildRootsUri(AUTHORITY), null); }
Bu sayfa ile ilgili örnek kod için bkz:
Bu sayfayla ilgili videolar için şuraya bakın:
- DevBytes: Android 4.4 Depolama Erişimi Çerçevesi: Sağlayıcı
- Depolama Erişim Çerçevesi: DocumentsProvider Oluşturma
- Depolama Erişim Çerçevesi'nde Sanal Dosyalar
Daha fazla ilgili bilgi için aşağıdaki kaynakları inceleyebilirsiniz:
- DocumentsProvider Oluşturma
- Depolama Erişim Çerçevesi'ni kullanarak dosyaları açma
- İçerik Sağlayıcı ile İlgili Temel Bilgiler