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解决方法。

 

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

  

android文件存储位置切换
  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

 

android文件存储位置切换
 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

  

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

 

你可能感兴趣的:(android)