android文件存储位置切换

  最近有个需求,助手的google卫星地图和OpenCycleMap下载的离线地图数据,要能够在内置存储和外置存储空间之间切换,因为离线瓦片数据非常大,很多户外用户希望将这些文件存储在外置TF卡上,不占用内置存储空间,所以把最近研究的整理了下,分享给大家。

  需要考虑和遇到的问题(主要是不同手机、不同系统的兼容性):

  1.这样获取手机所有挂载的存储器?

     Android是没有提供显式的接口的,首先肯定是要阅读系统设置应用“存储”部分的源码,看存储那里是通过什么方式获取的。最后找到StorageManager和StorageVolume这2个重要的类,然后通过反射获取StorageVolume[]列表。

  2.用什么标示一个存储器的唯一性?

   存储路径?不行(有些手机不插TF卡,内置存储路径是/storage/sdcard0,插上TF卡后,内置存储路径变成/storage/sdcard1,TF卡变成/storage/sdcard0)。

   存储卡名称?不行(可能会切换系统语言,导致名称匹配失败,名称的resId也不行,较低的系统版本StorageVolume没有mDescriptionId这一属性)。

     经过测试,发现使用mStorageId可以标示存储器的唯一性,存储器数量改变,每个存储器的id不会改变。

  3.如何获得存储器的名称?

    经测试,不同的手机主要有3种获取存储器名换的方法:getDescription()、getDescription(Context context)、先获得getDescriptionId()再通过resId获取名称。

  4.任务文件下载一半时,切换文件保存存储器,怎么处理?

    有2种方案:

    4.1 切换时,如果新的存储空间足够所有文件转移,先停止所有下载任务,将所有下载完和下载中的文件拷贝到新的存储空间,然后再更新下载数据库下载任务的存储路径,再恢复下载任务;

    4.2 切换时,先拷贝所有下载完成的文件到新的存储空间,下载任务继续下载,下载完成再移动到新的存储空间。

  5.在4.4系统上,第三方应用无法读取外置存储卡的问题。(参考“External Storage”)

   google为了在程序卸载时,能够完全彻底的将程序所有数据清理干净,应用将不能向2级存储区域写入文件。

   “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. Restricting writes in this way ensures the system can clean up files when applications are uninstalled.”

   要能够在4.4系统上TF卡写入文件,必须先root,具体方法可以google。

   所以4.4系统上,切换会导致文件转移和下载失败,用户如果要切换到TF卡,至少需要提醒用户,并最好给出4.4上root解决方法。

 

  以下是获取存储器的部分代码:

  

  1 public static class MyStorageVolume{
  2         public int mStorageId;
  3         public String mPath;
  4         public String mDescription;
  5         public boolean mPrimary;
  6         public boolean mRemovable;
  7         public boolean mEmulated;
  8         public int mMtpReserveSpace;
  9         public boolean mAllowMassStorage;
 10         public long mMaxFileSize;  //最大文件大小。(0表示无限制)
 11         public String mState;      //返回null
 12 
 13         public MyStorageVolume(Context context, Object reflectItem){
 14             try {
 15                 Method fmStorageId = reflectItem.getClass().getDeclaredMethod("getStorageId");
 16                 fmStorageId.setAccessible(true);
 17                 mStorageId = (Integer) fmStorageId.invoke(reflectItem);
 18             } catch (Exception e) {
 19             }
 20 
 21             try {
 22                 Method fmPath = reflectItem.getClass().getDeclaredMethod("getPath");
 23                 fmPath.setAccessible(true);
 24                 mPath = (String) fmPath.invoke(reflectItem);
 25             } catch (Exception e) {
 26             }
 27 
 28             try {
 29                 Method fmDescriptionId = reflectItem.getClass().getDeclaredMethod("getDescription");
 30                 fmDescriptionId.setAccessible(true);
 31                 mDescription = (String) fmDescriptionId.invoke(reflectItem);
 32             } catch (Exception e) {
 33             }
 34             if(mDescription == null || TextUtils.isEmpty(mDescription)){
 35                 try {
 36                     Method fmDescriptionId = reflectItem.getClass().getDeclaredMethod("getDescription");
 37                     fmDescriptionId.setAccessible(true);
 38                     mDescription = (String) fmDescriptionId.invoke(reflectItem, context);
 39                 } catch (Exception e) {
 40                 }
 41             }
 42             if(mDescription == null || TextUtils.isEmpty(mDescription)){
 43                 try {
 44                     Method fmDescriptionId = reflectItem.getClass().getDeclaredMethod("getDescriptionId");
 45                     fmDescriptionId.setAccessible(true);
 46                     int mDescriptionId = (Integer) fmDescriptionId.invoke(reflectItem);
 47                     if(mDescriptionId != 0){
 48                         mDescription = context.getResources().getString(mDescriptionId);
 49                     }
 50                 } catch (Exception e) {
 51                 }
 52             }
 53 
 54             try {
 55                 Method fmPrimary = reflectItem.getClass().getDeclaredMethod("isPrimary");
 56                 fmPrimary.setAccessible(true);
 57                 mPrimary = (Boolean) fmPrimary.invoke(reflectItem);
 58             } catch (Exception e) {
 59             }
 60 
 61             try {
 62                 Method fisRemovable = reflectItem.getClass().getDeclaredMethod("isRemovable");
 63                 fisRemovable.setAccessible(true);
 64                 mRemovable = (Boolean) fisRemovable.invoke(reflectItem);
 65             } catch (Exception e) {
 66             }
 67 
 68             try {
 69                 Method fisEmulated = reflectItem.getClass().getDeclaredMethod("isEmulated");
 70                 fisEmulated.setAccessible(true);
 71                 mEmulated = (Boolean) fisEmulated.invoke(reflectItem);
 72             } catch (Exception e) {
 73             }
 74 
 75             try {
 76                 Method fmMtpReserveSpace = reflectItem.getClass().getDeclaredMethod("getMtpReserveSpace");
 77                 fmMtpReserveSpace.setAccessible(true);
 78                 mMtpReserveSpace = (Integer) fmMtpReserveSpace.invoke(reflectItem);
 79             } catch (Exception e) {
 80             }
 81 
 82             try {
 83                 Method fAllowMassStorage = reflectItem.getClass().getDeclaredMethod("allowMassStorage");
 84                 fAllowMassStorage.setAccessible(true);
 85                 mAllowMassStorage = (Boolean) fAllowMassStorage.invoke(reflectItem);
 86             } catch (Exception e) {
 87             }
 88 
 89             try {
 90                 Method fMaxFileSize = reflectItem.getClass().getDeclaredMethod("getMaxFileSize");
 91                 fMaxFileSize.setAccessible(true);
 92                 mMaxFileSize = (Long) fMaxFileSize.invoke(reflectItem);
 93             } catch (Exception e) {
 94             }
 95 
 96             try {
 97                 Method fState = reflectItem.getClass().getDeclaredMethod("getState");
 98                 fState.setAccessible(true);
 99                 mState = (String) fState.invoke(reflectItem);
100             } catch (Exception e) {
101             }
102         }
103 
104         /**
105          * 获取Volume挂载状态, 例如Environment.MEDIA_MOUNTED
106          */
107         public String getVolumeState(Context context){
108             return StorageVolumeUtil.getVolumeState(context, mPath);
109         }
110 
111         public boolean isMounted(Context context){
112             return getVolumeState(context).equals(Environment.MEDIA_MOUNTED);
113         }
114 
115         public String getDescription(){
116             return mDescription;
117         }
118 
119         /**
120          * 获取存储设备的唯一标识
121          */
122         public String getUniqueFlag(){
123             return "" + mStorageId;
124         }
125 
126         /*public boolean isUsbStorage(){
127             return mDescriptionId == android.R.string.storage_usb;
128         }*/
129 
130         /**
131          * 获取目录可用空间大小
132          */
133         public long getAvailableSize(){
134             return StorageVolumeUtil.getAvailableSize(mPath);
135         }
136 
137         /**
138          * 获取目录总存储空间
139          */
140         public long getTotalSize(){
141             return StorageVolumeUtil.getTotalSize(mPath);
142         }
143 
144         @Override
145         public String toString() {
146             return "MyStorageVolume{" +
147                     "\nmStorageId=" + mStorageId +
148                     "\n, mPath='" + mPath + '\'' +
149                     "\n, mDescription=" + mDescription +
150                     "\n, mPrimary=" + mPrimary +
151                     "\n, mRemovable=" + mRemovable +
152                     "\n, mEmulated=" + mEmulated +
153                     "\n, mMtpReserveSpace=" + mMtpReserveSpace +
154                     "\n, mAllowMassStorage=" + mAllowMassStorage +
155                     "\n, mMaxFileSize=" + mMaxFileSize +
156                     "\n, mState='" + mState + '\'' +
157                     '}' + "\n";
158         }
159     }
存储信息MyStorageVolume

 

 1 public static List<MyStorageVolume> getVolumeList(Context context){
 2         List<MyStorageVolume> svList = new ArrayList<MyStorageVolume>(3);
 3         StorageManager mStorageManager = (StorageManager)context
 4                 .getSystemService(Activity.STORAGE_SERVICE);
 5         try {
 6             Method mMethodGetPaths = mStorageManager.getClass().getMethod("getVolumeList");
 7             Object[] list = (Object[]) mMethodGetPaths.invoke(mStorageManager);
 8             for(Object item : list){
 9                 svList.add(new MyStorageVolume(context, item));
10             }
11         } catch (Exception e) {
12             e.printStackTrace();
13         }
14         return svList;
15     }
获取所有存储器

 

github上的测试例子:

https://github.com/John-Chen/BlogSamples/tree/master/StorageTest

  

如果还有什么地方没有考虑到的,欢迎讨论。  

 

转载于:https://www.cnblogs.com/John-Chen/p/4216754.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/472540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[c]扫雷

题目描述 扫雷游戏是一款十分经典的单机小游戏。在n行m列的雷区中有一些格子含有地雷&#xff08;称之为地雷格&#xff09;&#xff0c;其他格子不含地雷&#xff08;称之为非地雷格&#xff09;。 玩家翻开一个非地雷格时&#xff0c;该格将会出现一个数字——提示周围格子中…

netcore docker_让.NetCore程序跑在任何有docker的地方

一.分别在Windows/Mac/Centos上安装DockerWindows上下载地址&#xff1a;https://docs.docker.com/docker-for-windows/install/&#xff08;window上安装的常见问题和解决方案请参考下方步骤六&#xff09;Mac上下载地址&#xff1a;https://hub.docker.com/editions/communit…

MapReduce 编程实践

文章目录1. MapReduce 作业流程2. 实践2.1 启动 hadoop2.2 创建 java 项目2.3 MapReduce shell2.4 MapReduce Web UI3. MapReduce 编程实践&#xff1a;统计对象中的某些属性参考书&#xff1a;《Hadoop大数据原理与应用》1. MapReduce 作业流程 2. 实践 2.1 启动 hadoop sta…

MapReduce 编程实践:统计对象中的某些属性

文章目录1. 生成数据2. 编写实体类3. Mapper类4. Reducer类5. Driver类6. 运行参考书&#xff1a;《Hadoop大数据原理与应用》 相关文章&#xff1a;MapReduce 编程实践 1. 生成数据 超市消费者 数据&#xff1a; id&#xff0c; 时间&#xff0c;消费金额&#xff0c;会员/…

超级签名源码_企业签名和超级签名有哪些区别?

我们知道iOS系统对于非App Store中的应用是有安装限制的&#xff0c;而App Store严格的审核机制又将许多APP拒之门外&#xff0c;这令不少开发者们郁闷不已。所以很多开发者们会选择苹果签名的方式&#xff0c;让自己的iOS APP可以不经过App Store就安装在用户的苹果手机上&…

天池 在线编程 到达终点

文章目录1. 题目2. 解题1. 题目 描述 A robot is located in a pair of integer coordinates (x, y). It must be moved to a location with another set of coordinates. Though the bot can move any number of times, it can only make the following two types of moves:…

第十七节(is-a 、is-like-a 、has-a,包和 import )

is - a 类与类之间的继承关系&#xff1b;is - like - a 类与接口之间的关系&#xff1b;has - a 关联关系&#xff1b; public class Animal{public void method01();}// 类与类之间的关系class Dog extends Animal{ // Dog is a Animal} /// public interface I{public void…

quartz获取开始结束时间_Springboot集成quartz

Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。本文描述在springboot 2.x环境下怎么集成quartz。一、添加quartz到项目中在pom.xml中加入 <dependency>特别注意application入口类的注解&#xff0c;这里一定…

linux 添加本地磁盘,XenServer如何添加本地存储

在一次测试中&#xff0c;发现本地有两块磁盘&#xff0c;但是只有一块磁盘在XenServer中显示出来&#xff0c;另外一块没有显示。本地只有一个Local storage。查询KB后&#xff0c;发现XenServer可以添加多块本地存储。详情&#xff0c;请见KB&#xff1a;CTX121313详细添加如…

LeetCode 1805. 字符串中不同整数的数目(哈希set)

文章目录1. 题目2. 解题1. 题目 给你一个字符串 word &#xff0c;该字符串由数字和小写英文字母组成。 请你用空格替换每个不是数字的字符。 例如&#xff0c;“a123bc34d8ef34” 将会变成 " 123 34 8 34" 。 注意&#xff0c;剩下的这些整数间至少要用一个空格隔…

linux 树状结构图,linux下tree指令的用法, 树状图列出目录, 树状图逐级列出目录...

tree命令&#xff0c;主要功能是创建文件列表&#xff0c;将所有文件以树的形式列出来linux下的tree就比较强大了&#xff0c;但一般系统并不自带这个命令&#xff0c;需要手动下载安装,安装sudo apt install tree## or using yum# yum -y install tree语法tree[-aACdDfFgilnNp…

映射表map(平衡二叉树实现)_手动实现Java集合容器之TreeMap(上)

上一篇我们手写了HashMap&#xff0c;还有一个很重要的Map的实现类TreeMap。打开源码第一句话&#xff1a;* A Red-Black tree based {link NavigableMap} implementation.TreeMap是一个基于红黑树的实现。对红黑树没有了解怎么办&#xff0c;那就先搞清楚红黑树的原理。只要理…

wltc循环多少公里_原来所有车都烧机油!但是烧多少才算正常你知道吗?

点击上方“腾讯汽车”关注我们&#xff0c;最新汽车资讯&#xff0c;最方便的用车常识还有最萌的小编都在这里啦&#xff01;最为当今汽车用户最为关心的问题&#xff0c;烧机油现象被很多用户当做引擎是好是坏的重要标准。但根据机油在引擎内冷却及清理引擎内杂质的循环机制来…

LeetCode 1806. 还原排列的最少操作步数(模拟)

文章目录1. 题目2. 解题1. 题目 给你一个偶数 n​​​​​​ &#xff0c;已知存在一个长度为 n 的排列 perm &#xff0c;其中 perm[i] i​&#xff08;下标 从 0 开始 计数&#xff09;。 一步操作中&#xff0c;你将创建一个新数组 arr &#xff0c;对于每个 i &#xff…

python3经典实例_Python3十大经典错误及解决办法

接触了很多Python爱好者&#xff0c;有初学者&#xff0c;亦有转行人。不论大家学习Python的目的是什么&#xff0c;总之&#xff0c;学习Python前期写出来的代码不报错就是极好的。下面&#xff0c;严小样儿为大家罗列出Python3十大经典错误及解决办法&#xff0c;供大家学习。…

LeetCode 1807. 替换字符串中的括号内容(哈希map)

文章目录1. 题目2. 解题1. 题目 给你一个字符串 s &#xff0c;它包含一些括号对&#xff0c;每个括号中包含一个 非空 的键。 比方说&#xff0c;字符串 "(name)is(age)yearsold" 中&#xff0c;有 两个 括号对&#xff0c;分别包含键 “name” 和 “age” 。 你知…

bootice.exe linux 启动盘,下载BOOTICE来把你的U盘做成启动盘

为了方便维护电脑&#xff0c;需要制作一个合适的U盘启动盘。网上制作U盘启动盘的工具也有很多&#xff0c;我下面使用bootice这个U盘启动盘制作工具来制作U盘启动盘。下载BOOTICE1、BOOTICE>分区管理G&#xff0c;对U盘进行格式化&#xff0c;FAT16&#xff0c;卷标设置为G…

excel打开空白_啥?下载的文件显示“文件已损坏,无法打开”?

推荐文章&#xff1a;Windows10系统的优化工具神器对于一个开发人员的我&#xff0c;这两天在网站做一个导出Excel表格功能&#xff0c;遇到了一个坑。在本地测试导出并且可以打开&#xff0c;但是到了测试环境导出打开却显示“文件已损坏&#xff0c;无法打开”。刚开始以为是…

erp故障处理流程图_PLC故障常见原因及处理方法!

欢迎关注“热控圈 ” ID&#xff1a;rekongquan传播热控知识&#xff0c;分享技术精华&#xff01;第一部分、运行中PLC故障常见原因及处理方法(一)、外围电路元器件故障此类故障在PLC工作一定时间后的故障中经常发生。在PLC控制回路中如果出现元器件损坏故障&#xff0c;PLC控…

LeetCode 1808. 好因子的最大数目(整数拆分,乘积最大)

文章目录1. 题目2. 解题1. 题目 给你一个正整数 primeFactors 。你需要构造一个正整数 n &#xff0c;它满足以下条件&#xff1a; n 质因数&#xff08;质因数需要考虑重复的情况&#xff09;的数目 不超过 primeFactors 个。n 好因子的数目 最大化。 如果 n 的一个因子可以…