android4.4上sd卡的读写权限

2023-05-16

Google去年11月正式发布了Android 4.4,代号为KitKat(奇巧,雀巢的一款巧克力品牌), 该系统带来了诸多新的特性 。 

但需要注意的是,该系统可能会让你之前一直正常使用的SD卡变为无用的“摆设”,因为 根据新版本的API改进,应用程序将不能再往SD卡中写入文件。  

来看Android开发者网站的 “外部存储技术信息”文档 中的描述: 

引用
WRITE_EXTERNAL_STORAGE只为设备上的主要外部存储授予写权限,应用程序无法将数据写入二级外部存储设备,除非指定了应用程序允许访问的特定的目录。


这目前只影响双存储设备, 如果你的设备有内部存储空间,即通常所说的机身存储(这就是指主要外部存储),那么你的SD卡就是一个二级外部存储设备。 

在Android 4.4中,如果你同时使用了机身存储和SD卡,那么应用程序将无法在SD卡中创建、修改、删除数据。比如,你无法使用文件管理器通过无线网络从电脑往SD卡中复制文件了。但是应用程序仍然可以往主存储的任意目录中写入数据,不受任何限制。 

Google表示, 这样做的目的是,通过这种方式进行限制,系统可以在应用程序被卸载后清除遗留文件。  

目前三星已经通过OTA向部分手机发送了Android 4.4的更新,已经有Note3用户抱怨FX文件管理器现在不能往SD卡中复制内容了。 

解决办法  

获得系统的ROOT权限是一个解决方法。 

很显然,这是针对用户的解决办法,但是并不是所有的用户都愿意进行ROOT,那么需要SD卡写入权限的开发者该如何做呢? 

XDA论坛已经有大神给出了解决方案——在应用中嵌入一段代码 ,这段代码作用是在Android 4.4+设备上,如果其他方式写入失败,则将数据写入二级存储设备。 

详细方案: http://forum.xda-developers.com/showthread.php?p=50008987  

Java代码 
  1. /* 
  2.  * Copyright (C) 2014 NextApp, Inc. 
  3.  *  
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
  5.  * You may obtain a copy of the License at 
  6.  *  
  7.  * http://www.apache.org/licenses/LICENSE-2.0 
  8.  *  
  9.  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" 
  10.  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language 
  11.  * governing permissions and limitations under the License. 
  12.  */  
  13.   
  14. package nextapp.mediafile;  
  15.   
  16. import java.io.File;  
  17. import java.io.IOException;  
  18. import java.io.OutputStream;  
  19.   
  20. import android.content.ContentResolver;  
  21. import android.content.ContentValues;  
  22. import android.net.Uri;  
  23. import android.provider.MediaStore;  
  24.   
  25. /** 
  26.  * Wrapper for manipulating files via the Android Media Content Provider. As of Android 4.4 KitKat, applications can no longer write 
  27.  * to the "secondary storage" of a device. Write operations using the java.io.File API will thus fail. This class restores access to 
  28.  * those write operations by way of the Media Content Provider. 
  29.  *  
  30.  * Note that this class relies on the internal operational characteristics of the media content provider API, and as such is not 
  31.  * guaranteed to be future-proof. Then again, we did all think the java.io.File API was going to be future-proof for media card 
  32.  * access, so all bets are off. 
  33.  *  
  34.  * If you're forced to use this class, it's because Google/AOSP made a very poor API decision in Android 4.4 KitKat. 
  35.  * Read more at https://plus.google.com/+TodLiebeck/posts/gjnmuaDM8sn 
  36.  * 
  37.  * Your application must declare the permission "android.permission.WRITE_EXTERNAL_STORAGE". 
  38.  */  
  39. public class MediaFile {  
  40.   
  41.     private final File file;  
  42.     private final ContentResolver contentResolver;  
  43.     private final Uri filesUri;  
  44.     private final Uri imagesUri;  
  45.   
  46.     public MediaFile(ContentResolver contentResolver, File file) {  
  47.         this.file = file;  
  48.         this.contentResolver = contentResolver;  
  49.         filesUri = MediaStore.Files.getContentUri("external");  
  50.         imagesUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  
  51.     }  
  52.   
  53.     /** 
  54.      * Deletes the file. Returns true if the file has been successfully deleted or otherwise does not exist. This operation is not 
  55.      * recursive. 
  56.      */  
  57.     public boolean delete()  
  58.             throws IOException {  
  59.         if (!file.exists()) {  
  60.             return true;  
  61.         }  
  62.   
  63.         boolean directory = file.isDirectory();  
  64.         if (directory) {  
  65.             // Verify directory does not contain any files/directories within it.  
  66.             String[] files = file.list();  
  67.             if (files != null && files.length > 0) {  
  68.                 return false;  
  69.             }  
  70.         }  
  71.   
  72.         String where = MediaStore.MediaColumns.DATA + "=?";  
  73.         String[] selectionArgs = new String[] { file.getAbsolutePath() };  
  74.   
  75.         // Delete the entry from the media database. This will actually delete media files (images, audio, and video).  
  76.         contentResolver.delete(filesUri, where, selectionArgs);  
  77.   
  78.         if (file.exists()) {  
  79.             // If the file is not a media file, create a new entry suggesting that this location is an image, even  
  80.             // though it is not.  
  81.             ContentValues values = new ContentValues();  
  82.             values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());  
  83.             contentResolver.insert(imagesUri, values);  
  84.   
  85.             // Delete the created entry, such that content provider will delete the file.  
  86.             contentResolver.delete(filesUri, where, selectionArgs);  
  87.         }  
  88.   
  89.         return !file.exists();  
  90.     }  
  91.   
  92.     public File getFile() {  
  93.         return file;  
  94.     }  
  95.   
  96.     /** 
  97.      * Creates a new directory. Returns true if the directory was successfully created or exists. 
  98.      */  
  99.     public boolean mkdir()  
  100.             throws IOException {  
  101.         if (file.exists()) {  
  102.             return file.isDirectory();  
  103.         }  
  104.   
  105.         ContentValues values;  
  106.         Uri uri;  
  107.   
  108.         // Create a media database entry for the directory. This step will not actually cause the directory to be created.  
  109.         values = new ContentValues();  
  110.         values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());  
  111.         contentResolver.insert(filesUri, values);  
  112.   
  113.         // Create an entry for a temporary image file within the created directory.  
  114.         // This step actually causes the creation of the directory.  
  115.         values = new ContentValues();  
  116.         values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath() + "/temp.jpg");  
  117.         uri = contentResolver.insert(imagesUri, values);  
  118.   
  119.         // Delete the temporary entry.  
  120.         contentResolver.delete(uri, nullnull);  
  121.   
  122.         return file.exists();  
  123.     }  
  124.   
  125.     /** 
  126.      * Returns an OutputStream to write to the file. The file will be truncated immediately. 
  127.      */  
  128.     public OutputStream write()  
  129.             throws IOException {  
  130.         if (file.exists() && file.isDirectory()) {  
  131.             throw new IOException("File exists and is a directory.");  
  132.         }  
  133.   
  134.         // Delete any existing entry from the media database.  
  135.         // This may also delete the file (for media types), but that is irrelevant as it will be truncated momentarily in any case.  
  136.         String where = MediaStore.MediaColumns.DATA + "=?";  
  137.         String[] selectionArgs = new String[] { file.getAbsolutePath() };  
  138.         contentResolver.delete(filesUri, where, selectionArgs);  
  139.   
  140.         ContentValues values = new ContentValues();  
  141.         values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());  
  142.         Uri uri = contentResolver.insert(filesUri, values);  
  143.   
  144.         if (uri == null) {  
  145.             // Should not occur.  
  146.             throw new IOException("Internal error.");  
  147.         }  
  148.   
  149.         return contentResolver.openOutputStream(uri);  
  150.     }  
  151. }  



[HOWTO] Write to media card (secondary storage) from an app under Android 4.4 KitKat

In Android 4.4 KitKat, Google/AOSP made the following change to the API specification, much to the detriment of app developers and users:

Quote:
"The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions."
Source:  http://source.android.com/devices/te...age/index.html

You can read my rather unhappy write-up about it here: https://plus.google.com/108338299717...ts/gjnmuaDM8sn

This only applies to dual-storage devices, i.e., devices with a user-writable internal flash storage AND a removable SD card. But if your device has both, as of Android 4.4, apps will no longer be able to write arbitrarily to the "secondary" storage (the SD card). 

There is however still an API exposed that will allow you to write to secondary storage, notably the media content provider. This is far from an ideal solution, and I imagine that someday it will not be possible.

I've written a tiny bit of code to let your applications continue to work with files on the SD card using the media content provider.  This code should only be used to write to the secondary storage on Android 4.4+ devices if all else fails.  I would strongly recommend that you NEVER rely on this code.  This code DOES NOT use root access.

The class:
Code:

/*
 * Copyright (C) 2014 NextApp, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

package nextapp.mediafile;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
import android.provider.MediaStore;

/**
 * Wrapper for manipulating files via the Android Media Content Provider. As of Android 4.4 KitKat, applications can no longer write
 * to the "secondary storage" of a device. Write operations using the java.io.File API will thus fail. This class restores access to
 * those write operations by way of the Media Content Provider.
 * 
 * Note that this class relies on the internal operational characteristics of the media content provider API, and as such is not
 * guaranteed to be future-proof. Then again, we did all think the java.io.File API was going to be future-proof for media card
 * access, so all bets are off.
 * 
 * If you're forced to use this class, it's because Google/AOSP made a very poor API decision in Android 4.4 KitKat.
 * Read more at https://plus.google.com/+TodLiebeck/posts/gjnmuaDM8sn
 *
 * Your application must declare the permission "android.permission.WRITE_EXTERNAL_STORAGE".
 */
public class MediaFile {

    private final File file;
    private final ContentResolver contentResolver;
    private final Uri filesUri;
    private final Uri imagesUri;

    public MediaFile(ContentResolver contentResolver, File file) {
        this.file = file;
        this.contentResolver = contentResolver;
        filesUri = MediaStore.Files.getContentUri("external");
        imagesUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    }

    /**
     * Deletes the file. Returns true if the file has been successfully deleted or otherwise does not exist. This operation is not
     * recursive.
     */
    public boolean delete()
            throws IOException {
        if (!file.exists()) {
            return true;
        }

        boolean directory = file.isDirectory();
        if (directory) {
            // Verify directory does not contain any files/directories within it.
            String[] files = file.list();
            if (files != null && files.length > 0) {
                return false;
            }
        }

        String where = MediaStore.MediaColumns.DATA + "=?";
        String[] selectionArgs = new String[] { file.getAbsolutePath() };

        // Delete the entry from the media database. This will actually delete media files (images, audio, and video).
        contentResolver.delete(filesUri, where, selectionArgs);

        if (file.exists()) {
            // If the file is not a media file, create a new entry suggesting that this location is an image, even
            // though it is not.
            ContentValues values = new ContentValues();
            values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());
            contentResolver.insert(imagesUri, values);

            // Delete the created entry, such that content provider will delete the file.
            contentResolver.delete(filesUri, where, selectionArgs);
        }

        return !file.exists();
    }

    public File getFile() {
        return file;
    }

    /**
     * Creates a new directory. Returns true if the directory was successfully created or exists.
     */
    public boolean mkdir()
            throws IOException {
        if (file.exists()) {
            return file.isDirectory();
        }

        ContentValues values;
        Uri uri;

        // Create a media database entry for the directory. This step will not actually cause the directory to be created.
        values = new ContentValues();
        values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());
        contentResolver.insert(filesUri, values);

        // Create an entry for a temporary image file within the created directory.
        // This step actually causes the creation of the directory.
        values = new ContentValues();
        values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath() + "/temp.jpg");
        uri = contentResolver.insert(imagesUri, values);

        // Delete the temporary entry.
        contentResolver.delete(uri, null, null);

        return file.exists();
    }

    /**
     * Returns an OutputStream to write to the file. The file will be truncated immediately.
     */
    public OutputStream write()
            throws IOException {
        if (file.exists() && file.isDirectory()) {
            throw new IOException("File exists and is a directory.");
        }

        // Delete any existing entry from the media database.
        // This may also delete the file (for media types), but that is irrelevant as it will be truncated momentarily in any case.
        String where = MediaStore.MediaColumns.DATA + "=?";
        String[] selectionArgs = new String[] { file.getAbsolutePath() };
        contentResolver.delete(filesUri, where, selectionArgs);

        ContentValues values = new ContentValues();
        values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());
        Uri uri = contentResolver.insert(filesUri, values);

        if (uri == null) {
            // Should not occur.
            throw new IOException("Internal error.");
        }

        return contentResolver.openOutputStream(uri);
    }
}  
Source download (or cut/paste the above):  http://android.nextapp.com/content/m...MediaFile.java
Eclipse project with test app:  http://android.nextapp.com/content/m...MediaWrite.zip
APK of test app:  http://android.nextapp.com/content/m...MediaWrite.apk

The test project is currently configured to target the path /storage/extSdCard/MediaWriteTest (this is correct for a Note3, at least on 4.3...make sure you don't have anything there). Edit MainActivity.java in the Eclipse project to change it.

And again, let me stress that the above code might not work in the future should Google dislike it. I wouldn't recommend that the average app developer make use of this code, but if you're writing a file manager (or something else that competes with any of my other apps) , it might be useful to you. And actually at the time of writing, this functionality is NOT in FX File Explorer or WebSharing.

Android 4.4 SD卡文件读写变化

1.对多个sd卡支持

从4.4开始android已经支持多了sd卡(之前由厂商自己实现)

可通过以下方法获取

Context.getExternalFilesDirs(), 返回多个sd卡的该应用私有数据区的files目录 

/storage/sdcard0/Android/data/<包名>/files

/storage/sdcard1/Android/data/<包名>/files

 

Context.getExternalCacheDirs(), 返回多个sd卡下该应用私有数据库的缓存目录

/storage/sdcard0/Android/data/<包名>/caches

/storage/sdcard1/Android/data/<包名>/caches

 

Context.getObbDirs(), 返回多个sd卡下obb目录下的私有数据

/storage/sdcard0/Android/obb/<包名>

/storage/sdcard1/Android/obb/<包名>

目前这些api均为hide,需通过反射调用

 

2.对读写权限的修改

    如果应该仅需读取sd卡下该应用私有数据的数据,则不需要申请读写权限(WRITE_EXTERNAL_STORAGE 、READ_EXTERNAL_STORAGE )

如果需要读取sd卡其他目录,则需声明读sd卡权限

如果需要写入sd卡其他目录,则需声明写sd卡权限

对于存在多个sd卡的情况,仅对主卡、及每张卡的私有数据区下的文件有读写权限,对其他卡没有写权限(之前厂商实现中,对每张sd卡均有读写权限)




本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android4.4上sd卡的读写权限 的相关文章

  • 网络调试助手TCP server不能成功连接

    正文 xff1a 在使用网络调试助手时 xff0c 想一个建立TCP server xff0c 一个建立TCP client xff0c 两者之间进行通信 xff0c 打开网络调试助手其默认读取本地IP地址 xff0c 而端口号默认为808
  • 单片机 GSM 模块应用

    如果你只要发送英文而已 xff0c 那你很幸运 xff0c 因为仅仅为了发送个字符而已是很简单的 TEXT模式满足你的要求 xff0c 但是不爽的是 xff0c 我的上司要求我要中文字符一起混发 xff0c 还好有unicode xff0c
  • OSInit()简介

    在uC OS II的学习中 xff0c OSInit 是一个重要的函数 xff0c 它在OS应用中的void main 函数中首先被调用 xff0c 是OS运行的第一个函数 如下程序 xff0c 它完成各初始变量的初始化 xff0c 建立任
  • OSStart()函数简介

    多任务的的启动是通过调用OSStart xff08 xff09 实现的 xff0c 而在启动uc os之前至少需要建立一个应用任务 OSStart xff08 xff09 函数源码 void OSStart void INT8U y INT
  • SMS编码之PDU – 接收短信篇

    上一篇文章简单介绍了下收发短信需要用到的AT指令 xff0c 这篇文章详细分析SMS编码中的PDU编码 首先我用测试手机向短信猫中的sim卡发送了两条短信 xff0c 内容分别为 xff1a Hello World Hello World
  • PDU短信发送与接收格式分析

    一 PDU发送和接收格式分析 PDU是大多数手机短信通讯的核心 xff0c 仅有少数手机只支持Text模式 PDU模式比起Text模式可以提供能为强大的功能 xff0c 但其编码较Text模式困难 无论哪种模式 xff0c 我们都可以通过A
  • ucos OSTimeDly

    来源 xff1a http blog sina com cn s blog 5f9b3de40100e182 html OSTimeDly 在Task中 xff0c 一般执行一段时间之后调用OSTimeDly推迟一段时间再继续运行 xff0
  • WinCE中断结构分析

    以前写的原创博文 xff0c 这里放一份 前一段时间研究了一下WinCE下的中断结构 xff0c 整理了一下 xff0c 希望与大家讨论 最下面有PDF版本下载 xff0c 便于保存 Windows Embedded CE 中断结构分析 关
  • Google chrome 中文版下载

    Google release了自己的浏览器Chrome xff0c 大家可以试试看 xff0c 我觉得还不错 xff0c 对Web2 0的支持还好 xff0c 不知道安全性如何 我发现看tudou和youku等视频有问题 内存占用太大了 x
  • (下载)WinCE注册表编辑器(PC端)

    可以在PC上通过ActiveSync来查看 xff0c 修改device端的注册表 xff0c 方便调试 http download csdn net source 749005 注 xff1a 本人提供的下载均不要资源点
  • (下载)WinCE镜像传输工具ESHELL

    WinCE镜像传输工具ESHELL 使用这个可以不用装PB也可以传输镜像 xff0c 其实就是一个TFTP的传输 xff0c 适用于CE5 xff0c CE6 http download csdn net source 835336 不用资
  • I/O的控制方式——查询,中断,dma

    早期 xff0c I O串行 xff0c 查询方式 发展 xff0c I O并行 xff0c 两种方式其一是中断方式 xff0c 其二是dma方式 xff0c 使得外部设备能直接与主存储器信息交换 xff0c 减轻了cpu的工作量 技术继续
  • 关于WinCE下MC55使用Unimodem进行GPRS拨号,拨上出现断开连接,检查波特率的问题的另一种可能情况

    http www armce com bbs thread 59 1 1 html
  • WinCE上BINFS实现详解

    作者 wwfiney 64 ARMCE 网上不少介绍三星24x0系列的BIN FS启动方式实现 xff0c 有些内容上描述的不是非常全面 下面就WinCE6上的BINFS实现 xff0c 从基本原理 到修改BSP xff0c 再到如何烧录启
  • i.MX27支持8GBSDHC卡驱动修改

    最近需要给mx27加上SDHC驱动 以支持SDHC的SD卡 网上许多关于2440支持SDHC的文章 xff0c 借鉴很多 xff0c 但是由于MX27驱动的特殊结构 xff0c 需要做更多改动 xff0c 详细如下 xff1a 平台 xff
  • /etc/init.d/rcS文件详解

    我使用的简单rcS文件内容如下 最后的IP地址设定非常重要 xff0c 一定要跟服务器的地址再同一个网段 xff0c 不然会出现无法连接错误 nfs server 192 168 0 102 not responding still try
  • CAN通讯的byte序和bit序

    听别人说起CAN通讯协议的时候总说到Intel格式和motorola格式的时候 Intel格式如何 xff0c Motorola格式又如何 xff1f 觉得很有必要搞懂这些知识 xff0c 也看了相关资料 xff0c 可直到今天还没明白 真
  • ROS学习笔记(一):创建工作空间和功能包

    所有的ROS程序 xff0c 包括我们自己开发的程序 xff0c 都被组织成功能包 xff0c 而ROS的功能包被存放在称之为工作空间的目录下 因此 xff0c 在我们写程序之前 xff0c 第一步是创建一个工作空间以容纳我们的功能包 其实
  • PHP活动报名小程序系统源码 带后台管理程序

    活动报名小程序源码 xff0c 基于thinkphp开发的报名小程序源码 xff0c 带有后台管理 xff0c 用户发布活动信息 报名可以后台管理 xff0c 基本都还是可以的 不过需要注意的是 xff0c 用户注册部分是发送手机短信 xf
  • NVIDIA Jetson Xavier NX部署VINS-fusion-GPU

    组内大佬师兄今天抽出时间总结了一篇博客 xff0c 主要内容是 xff1a 把在阿木P450无人机上 xff0c 对自带的NVIDIA Jetson Xavier NX边缘计算机部署VINS fusion GPU教程 xff0c 并进行实验

随机推荐