Android4.4之后SD卡存储方案

来源:互联网 发布:微信公众号树洞源码 编辑:程序博客网 时间:2024/05/21 08:39

由于Android4.4之后,Android限制了第三方应用在SD卡中的公用目录的写权限,所以我们无法再公用目录创建文件夹,写入文件,但是读操作不受限制,(系统应用如文件管理器,或者root用户不除外)

第三方应用想要写入SD卡,有以下几种方案:

1,Context.getExternalFilesDir()

获取应用的专有目录如:/storage/sdcard1/Android/data/com.xxx.email
第三方应用有该目录的读写权限,但是该目录下的文件不会被MediaScanner检索到,并且在应用删除后,该目录同样会被删除。

2,Context.getExternalMediaDirs()

Android5.0新增,该目录为:
/storage/sdcard1/Android/media/com.xxx.email
该目录和Context.getExternalFilesDir()获取的目录的唯一区别就是该目录下的文件能够被媒体扫描到,进而在如 相册中浏览到,并且可以通过MediaStore被其他应用获取。 应用删除后该目录会被删除。
(在金立手机上出现过没有写权限的时候,然后拔插sd卡后,又有了写权限,不知道是金立手机不稳定还是本身这个API不稳定)

3,Storage Access Framework (SAF)

中文guide:http://developer.android.com/intl/zh-cn/guide/topics/providers/document-provider.html
这个存储访问框架是Android4.4引入的。其功能类似于ContentProvider和ContentResolver
文件提供者-通过DocumentsProvider提供文件访问服务(例如云存储应用,Google云硬盘)
客户端应用-则通过调用 ACTION_OPEN_DOCUMENT和/或 ACTION_CREATE_DOCUMENT Intent 。接收文件提供者返回的文件。
选取器-系统提供的通用UI,用来访问浏览提供者提供的文件
比如通过:
Intent intent =newIntent(Intent.ACTION_OPEN_DOCUMENT);
获取一个图片时,系统会提供一个下图形式的选择器,让用户从中选择文件。类似于ACTION_PICK 或ACTION_GET_CONTENT。

除了上面所说的,Android5.0之后还提供了ACTION_OPEN_DOCUMENT_TREE这种方式来访问目录,并可以新建,删除文件。

我认为这会是Android存储方向上的趋势,操作方法如下:

1.通过Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);

startActivityForResult(intent, DOC_TREE_CODE);
打开一个文件选择器(系统提供)。如下图:
这里写图片描述

2.用户可以在sd目录下新建目录,然后选择该目录。

在选择后,onActivityResult返回的intent中返回该目录的uri:
content://com.android.externalstorage.documents/tree/00F3-1408%3Awps
然后可以通过
buildDocumentUriUsingTree:可以获取文件或者文件夹本身的信息。
buildChildDocumentsUriUsingTree:可以获取文件夹下的内容信息。
示例代码如下:

ContentResolver contentResolver = getActivity().getContentResolver();        Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,                DocumentsContract.getTreeDocumentId(uri));        Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri,                DocumentsContract.getTreeDocumentId(uri));//通过docUri可以查询该文档或者目录的 名称,类型,flags(是否可写等信息)        Cursor docCursor = contentResolver.query(docUri, new String[]{                Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE}, null, null, null);        try {            while (docCursor.moveToNext()) {                Log.d(TAG, "found doc =" + docCursor.getString(0) + ", mime=" + docCursor                        .getString(1));                mCurrentDirectoryUri = uri;                mCurrentDirectoryTextView.setText(docCursor.getString(0));                mCreateDirectoryButton.setEnabled(true);            }        } finally {            closeQuietly(docCursor);        }//通过childrenUri可以查询目录下子文件或者子目录的信息        Cursor childCursor = contentResolver.query(childrenUri, new String[]{                Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE, Document.COLUMN_DOCUMENT_ID,Document.COLUMN_FLAGS}, null, null, null);        try {            List<DirectoryEntry> directoryEntries = new ArrayList<>();            while (childCursor.moveToNext()) {                Log.d(TAG, "found child=" + childCursor.getString(0) + ", mime=" + childCursor                        .getString(1));                DirectoryEntry entry = new DirectoryEntry();                entry.fileName = childCursor.getString(0);                entry.mimeType = childCursor.getString(1);                entry.docId = childCursor.getString(2);                entry.flag = childCursor.getInt(3);                directoryEntries.add(entry);            }            mAdapter.setDirectoryEntries(directoryEntries);            mAdapter.setTreeUri(uri);            mAdapter.setActivity(getActivity());            mAdapter.notifyDataSetChanged();        } finally {            closeQuietly(childCursor);        }

3.新建文件

通过createDocument

Uri dirPic = DocumentsContract.createDocument(cr, dir, "image/png", "pic2.png");

可以在文件夹下创建目录或者文档。返回的依然是Uri
如果想对新建的文件进行写操作(比如邮件下载)只能通过如下方式:

os = contentResolver.openOutputStream(dirPic);

获取文件的输出流,然后写入内容

4.删除文件

文件的删除则是:

//注意这里的pic这个uri是DocumentUriDocumentsContract.deleteDocument(contentResolver, pic))

综上

方案1和2可以实现将附件保存在SD卡上
优点:不用改变现有的存储结构,只需将sd卡下的附件存储位置限定在
/storage/sdcard1/Android/data/com.xxx.email或者/storage/sdcard1/Android/media/com.xxx.email下折腾。并且/storage/sdcard1/Android/data/com.xxx.email目录可以覆盖android4.4以上的系统
缺点:目录选择受限,并且随着应用卸载,该目录会被删除。

方案3.
优点:可以在5.0以上很好的解决sd卡下文件夹新建,随便哪里都可以建.并且这种操作方式也是一种趋势。
缺点:
1,Android4.4 和4.4W这两个版本是个空挡。
2,由于整套API是新的,不涉及File操作,所以所有应用内涉及到新建文件,删除文件的地方,都要写2套操作,一套是File的操作,一套是Uri的操作。并需要对系统版本进行判断。

参考:
Android源码:
android5.1\development\samples\ApiDemos\src\com\example\android\apis\content\DocumentsSample.java
示例代码:https://github.com/googlesamples/android-DirectorySelection
文档:http://developer.android.com/intl/zh-cn/guide/topics/providers/document-provider.html

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 陶瓷壶盖卡住了怎么办 贝德玛瓶盖摔坏怎么办 塑料盖子错位拧不开怎么办 安全瓶盖坏了怎么办 刚买面霜打不开怎么办 可乐瓶盖鼓起来怎么办 暖壶塞子吸住了怎么办 茶兀瓶盖打不开怎么办 水杯盖太紧了拧不开怎么办 矿泉水瓶盖拧不开了怎么办 弩弦用手拉不上怎么办 茅台酒瓶口漏酒怎么办 化妆品盖子丢了怎么办 化妆品盖子碎了怎么办 自制水泵压力小怎么办 大学数学不会做怎么办 下雪了怎么办教案幼儿园小班 下水道被混凝土堵塞怎么办 日本足贴丢了胶布怎么办 牙齿被可乐腐蚀怎么办 三十岁满嘴无牙怎么办 水乳盖子打不开怎么办 蜂蜜罐子打不开了怎么办 蜂蜜盖子第二次拧不开怎么办 玻璃杯子拧不开盖子怎么办 玻璃杯水杯盖子拧不开怎么办 鞋子蝴蝶结掉了怎么办 蝴蝶翅膀受伤了怎么办 手被割了个口子怎么办 致炫方向盘重怎么办 黑檀7打不透怎么办 乒乓球底板太轻怎么办 狙击精英4卡怎么办 鼠标点一下变两下怎么办 工程干完不给钱怎么办 屋里有大蛾子怎么办 房间很多小飞虫怎么办 雷蛇键盘失灵怎么办 xp驱动 不支持win10怎么办 阿提拉全面战争统治度太低怎么办 微信号变成wxid怎么办