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

  

如果還有什么地方沒有考慮到的,歡迎討論。  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM