Android存储访问框架怎么使用(android,开发技术)

时间:2024-05-09 07:00:47 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

存储访问框架,简称:SAF, 就是系统文件选择器+文件操作API。先选择文件,在用文件操作API处理文件。系统文件选择器,就和Windows的文件选择框一样。

其实绝大多数app,都不会使用这个东西,因为太不方便了。图片,视频,普通文件,需要用户去翻文件夹找,这样的用户体验实在太差了。所以大家都是用第三方的或者自己写一个文件选择器。

之所以讲SAF,一,是因为Android11以后,使用MediaStore无法访问到非多媒体文件了,需要依赖SAF了。二,外卡和SD卡的操作依赖于存储访问框架授权。

打开系统文件选择器与文件过滤

Intentintent=newIntent(Intent.ACTION_OPEN_DOCUMENT);intent.addCategory(Intent.CATEGORY_OPENABLE);intent.setType("application/*");startActivityForResult(intent,REQUEST_CODE)

setType的值是mime type, 可以是"image/*", "*/*", 其中*是通配符。"image/*"代码所有类型的图片。"*/*"代表所有类型的文件。

当只需要打开几种文件类型时,可以用Intent.EXTRA_MIME_TYPES。同时setType设成“*/*”。

Intentintent=newIntent(Intent.ACTION_OPEN_DOCUMENT);intent.addCategory(Intent.CATEGORY_OPENABLE);intent.setType("*/*");intent.putExtra(Intent.EXTRA_MIME_TYPES,newString[]{"application/pdf",//.pdf"application/vnd.oasis.opendocument.text",//.odt"text/plain"//.txt});startActivityForResult(intent,REQUEST_CODE)

Intent.ACTION_PICK和ACTION_GET_CONTENT,也可以打开文件选择框。ACTION_GET_CONTENT更加宽泛,除了文件其他类型的内容还可以取。

Intentintent=newIntent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);intent.setType("image/*");
Intentintent=newIntent(Intent.ACTION_GET_CONTENT);intent.setType("image/*")

下面列举了所有的mime type:

privatestaticfinalString[][]MIME_TYPES=newString[][]{{"3gp","video/3gpp"},{"apk","application/vnd.android.package-archive"},{"asf","video/x-ms-asf"},{"avi","video/x-msvideo"},{"bin","application/octet-stream"},{"bmp","image/bmp"},{"c","text/plain"},{"class","application/octet-stream"},{"conf","text/plain"},{"cpp","text/plain"},{"doc","application/msword"},{"docx","application/vnd.openxmlformats-officedocument.wordprocessingml.document"},{"xls","application/vnd.ms-excel"},{"xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},{"exe","application/octet-stream"},{"gif","image/gif"},{"gtar","application/x-gtar"},{"gz","application/x-gzip"},{"h","text/plain"},{"htm","text/html"},{"html","text/html"},{"jar","application/java-archive"},{"java","text/plain"},{"jpeg","image/jpeg"},{"jpg","image/jpeg"},{"js","application/x-JavaScript"},{"log","text/plain"},{"m3u","audio/x-mpegurl"},{"m4a","audio/mp4a-latm"},{"m4b","audio/mp4a-latm"},{"m4p","audio/mp4a-latm"},{"ape","audio/ape"},{"flac","audio/flac"},{"m4u","video/vnd.mpegurl"},{"m4v","video/x-m4v"},{"mov","video/quicktime"},{"mp2","audio/x-mpeg"},{"mp3","audio/x-mpeg"},{"mp4","video/mp4"},{"mkv","video/x-matroska"},{"flv","video/x-flv"},{"divx","video/x-divx"},{"mpa","video/mpeg"},{"mpc","application/vnd.mpohun.certificate"},{"mpe","video/mpeg"},{"mpeg","video/mpeg"},{"mpg","video/mpeg"},{"mpg4","video/mp4"},{"mpga","audio/mpeg"},{"msg","application/vnd.ms-outlook"},{"ogg","audio/ogg"},{"pdf","application/pdf"},{"png","image/png"},{"pps","application/vnd.ms-powerpoint"},{"ppt","application/vnd.ms-powerpoint"},{"pptx","application/vnd.openxmlformats-officedocument.presentationml.presentation"},{"prop","text/plain"},{"rc","text/plain"},{"rmvb","audio/x-pn-realaudio"},{"rtf","application/rtf"},{"sh","text/plain"},{"tar","application/x-tar"},{"tgz","application/x-compressed"},{"txt","text/plain"},{"wav","audio/x-wav"},{"wma","audio/x-ms-wma"},{"wmv","audio/x-ms-wmv"},{"wps","application/vnd.ms-works"},{"xml","text/plain"},{"z","application/x-compress"},{"zip","application/x-zip-compressed"},{"rar","application/x-rar"},{"","*/*"}};

打开指定文件夹

利用DocumentsContract.EXTRA_INITIAL_URI,在打开文件选择器的时候,跳转到指定文件夹。只有android 8以上才行。

Uriuri=Uri.parse("content://com.android.externalstorage.documents/document/primary:Download");Intentintent=newIntent(Intent.ACTION_OPEN_DOCUMENT);intent.addCategory(Intent.CATEGORY_OPENABLE);intent.setType("*/*");intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI,uri);startActivityForResult(intent,1);

文件夹权限申请

当需要读取非公共文件夹里面的文件时,可以申请授权,授权后保存Uri,之后可以拼接这个Uri操作文件夹里的所有文件。

尤其是SD卡,从Android 5 开始文件的修改删除必须先授权,且必须通过SVF框架接口才能操作。

可以使用EXTRA_INITIAL_URI,打开指定文件夹,让用户授权

Intentintent=newIntent(Intent.ACTION_OPEN_DOCUMENT_TREE);Uriuri=Uri.parse("content://com.android.externalstorage.documents/document/primary:Download");intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI,uri);startActivityForResult(intent)

需要注意的是,Android 11以后,无法授权访问存储根目录,以及Download/,Android/, 这两个文件夹也无法授权。

创建文件夹

创建文件夹有两个情况,一个是在已授权的文件夹下,可以使用SVF框架API。

DocumentsContract.createDocument()

还有一种是在无授权的文件夹下创建,那么可以直接指定类型和名字,通过跳系统选择框创建。

Intentintent=newIntent(Intent.ACTION_CREATE_DOCUMENT);intent.addCategory(Intent.CATEGORY_OPENABLE);intent.setType("application/txt");intent.putExtra(Intent.EXTRA_TITLE,"testfile.txt");startActivityForResult(intent)

存储访问框架API

存储访问框架API,都在DocumentsContract里面,典型的有:

publicstatic@NullableUrirenameDocument(@NonNullContentResolvercontent,@NonNullUridocumentUri,@NonNullStringdisplayName)throwsFileNotFoundException{}/***Deletethegivendocument.**@paramdocumentUridocumentwith{@linkDocument#FLAG_SUPPORTS_DELETE}*@returnifthedocumentwasdeletedsuccessfully.*/publicstaticbooleandeleteDocument(@NonNullContentResolvercontent,@NonNullUridocumentUri)throwsFileNotFoundException{}/***Copiesthegivendocument.**@paramsourceDocumentUridocumentwith{@linkDocument#FLAG_SUPPORTS_COPY}*@paramtargetParentDocumentUridocumentwhichwillbecomeaparentofthesource*document'scopy.*@returnthecopieddocument,or{@codenull}iffailed.*/publicstatic@NullableUricopyDocument(@NonNullContentResolvercontent,@NonNullUrisourceDocumentUri,@NonNullUritargetParentDocumentUri)throwsFileNotFoundException{}/***Movesthegivendocumentunderanewparent.**@paramsourceDocumentUridocumentwith{@linkDocument#FLAG_SUPPORTS_MOVE}*@paramsourceParentDocumentUriparentdocumentofthedocumenttomove.*@paramtargetParentDocumentUridocumentwhichwillbecomeanewparentofthesource*document.*@returnthemoveddocument,or{@codenull}iffailed.*/publicstatic@NullableUrimoveDocument(@NonNullContentResolvercontent,@NonNullUrisourceDocumentUri,@NonNullUrisourceParentDocumentUri,@NonNullUritargetParentDocumentUri)throwsFileNotFoundException{}/***Removesthegivendocumentfromaparentdirectory.**<p>Incontrastto{@link#deleteDocument}itrequiresspecifyingtheparent.*Thismethodisespeciallyusefulifthedocumentcanbeinmultipleparents.**@paramdocumentUridocumentwith{@linkDocument#FLAG_SUPPORTS_REMOVE}*@paramparentDocumentUriparentdocumentofthedocumenttoremove.*@returntrueifthedocumentwasremovedsuccessfully.*/publicstaticbooleanremoveDocument(@NonNullContentResolvercontent,@NonNullUridocumentUri,@NonNullUriparentDocumentUri)throwsFileNotFoundException{}

获取文件夹文件

使用DocumentFile类获取文件夹里文件列表。

privateActivityResultLauncher<Object>openFile(){Intentintent=newIntent(Intent.ACTION_OPEN_DOCUMENT_TREE);Uriuri=Uri.parse("content://com.android.externalstorage.documents/document/primary:AuthSDK");intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI,uri);returnstartActivityForResult(intent,newActivityResultCallback<Intent>(){@OverridepublicvoidonActivityResult(Intentresult){for(DocumentFiledocumentFile:DocumentFile.fromTreeUri(BaseApplication.getInstance().getApplicationContext(),Uri.parse(result.getData().toString())).listFiles()){Log.i("",documentFile.getUri());}}});}

下面的代码演示了,使用SVF读取文件内容,写内容,通过MediaStore查询文件属性。

privateActivityResultLauncher<Object>openFile(){Intentintent=newIntent(Intent.ACTION_OPEN_DOCUMENT_TREE);Uriuri=Uri.parse("content://com.android.externalstorage.documents/document/primary:AuthSDK");intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI,uri);returnstartActivityForResult(intent,newActivityResultCallback<Intent>(){@OverridepublicvoidonActivityResult(Intentresult){for(DocumentFiledocumentFile:DocumentFile.fromTreeUri(BaseApplication.getInstance().getApplicationContext(),Uri.parse(result.getData().toString())).listFiles()){try{InputStreaminputStream=BaseApplication.getInstance().getContentResolver().openInputStream(documentFile.getUri());byte[]readData=newbyte[1024];inputStream.read(readData);OutputStreamoutputStream=BaseApplication.getInstance().getContentResolver().openOutputStream(documentFile.getUri());byte[]writeData="alangong".getBytes(StandardCharsets.UTF_8);outputStream.write(writeData,0,9);outputStream.close();if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.Q){UrimediaUri=MediaStore.getMediaUri(BaseApplication.getInstance().getApplicationContext(),documentFile.getUri());longfileId=ContentUris.parseId(mediaUri);Cursorquery=BaseApplication.getInstance().getContentResolver().query(documentFile.getUri(),null,MediaStore.MediaColumns._ID+"="+fileId,null,null);intcolumnIndex=query.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE);StringmimeType=query.getString(columnIndex);Log.i("","");}}catch(FileNotFoundExceptione){e.printStackTrace();}catch(IOExceptione){e.printStackTrace();}}}});}

使用MediaStore.getMediaUri(documentUri)可以转换,MediaStore Uri 和 Document Uri。通过MediaStore Uri中的数据库id,就可以查询文件的所有属性了。

MediaStore Uri:content://media/external_primary/file/101750

Document Uri: content://com.android.externalstorage.documents/tree/primary%3AAuthSDK

另外,

非公共目录下不能用File API操作的,即使通过SVF授权了, READ_EXTRNAL_PERMISSION的权限也给了。还是会抛出FileNotFoundException, 并且显示permission deny。

Android存储访问框架怎么使用

和MediaStore API的不同

存储访问框架API和MediaStore API的差异,在于存储访问框架API,是基于系统文件选择框的,用户选择了文件,那么相当于授权了, 可以访问所有类型的文件。而MediaStore的特点是可以查询出所有文件,但是开启分区存储后,只能查处多媒体文件,其他类型文件是不可以的。

 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:Android存储访问框架怎么使用的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:怎么用python爬取一组小姐姐图片下一篇:

11 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18