1. 程式人生 > >Android 讀取SD卡中檔案以及記憶體使用情況

Android 讀取SD卡中檔案以及記憶體使用情況

android.os下的StatFs類主要用來獲取檔案系統的狀態,能夠獲取sd卡的大小和剩餘空間,例如通過例項化一個StatFs物件 new StatFs(path.getPath())把SD卡根目錄路徑傳進去可以獲取SD卡根目錄下的記憶體儲存狀態。呼叫系統API:Environment.getExternalStorageDirectory().getPath();得到的是SDcard路徑為內建的SDcard路徑。當然,現在很多手機廠商處理SDcard的路徑也都不相同。  

SD卡的狀態通常有一下這幾種狀態,顯然,只有Environment.MEDIA_MOUNTED才是我們想要的。

Environment
.MEDIA_MOUNTED // sd卡在手機上正常使用狀態 Environment.MEDIA_UNMOUNTED // 使用者手工到手機設定中解除安裝sd卡之後的狀態 Environment.MEDIA_REMOVED // 使用者手動解除安裝,然後將sd卡從手機取出之後的狀態 Environment.MEDIA_BAD_REMOVAL // 使用者未到手機設定中手動解除安裝sd卡,直接撥出之後的狀態 Environment.MEDIA_SHARED // 手機直接連線到電腦作為u盤使用之後的狀態 Environment.MEDIA_CHECKINGS // 手機正在掃描sd卡過程中的狀態


不廢話了,建立個專案,在寫程式碼前先給專案配置許可權:

第三個許可權加不加根據自己的需求,但是前兩個許可權我們必須配置

<!-- 支援SD卡的寫入許可權 -->
<uses-permission  android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 支援SD卡的讀取許可權 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 支援SD卡的建立,刪除檔案或者資料夾許可權 -->
<uses-permission 
android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

1、首先,我們先封裝一個SDCard操作類SDCardUtils.java,在這個工具類中,我們封裝一些操作方法,方便我們後面使用。

第一步就是先判斷你的SD卡是否可用,當return返回的結果為true,代表SD卡正常可用。

後面就是我們封裝的一些方法了,有獲取SD卡的總記憶體大小、獲取SD卡的可用記憶體大小、獲取SD卡根目錄的路徑、讀取指定檔案中資料、向指定路徑中寫入資料、複製檔案等方法,程式碼中註釋寫的很詳細,就不再一一贅述。SD卡可操作的方法有很多,不止這些。你可以根據自己的需求,去新增一些操作方法。

public class SDCardUtils {// 判斷sd卡是否可用
public boolean isSdSafe() {
      /**
       * equals結果為true時,代表sd卡可正常使用
*/
return Environment.getExternalStorageState().equals(
            Environment.MEDIA_MOUNTED);
}
   // 獲取sd卡總大小
public String getTotalSize(Context context) {
      // 獲取SD卡根目錄
File path = Environment.getExternalStorageDirectory();
// 獲取指定目錄下的記憶體儲存狀態
StatFs stat = new StatFs(path.getPath());
// 獲取單個扇區的大小
long blockSize = stat.getBlockSize();
// 獲取扇區的數量
long totalBlocks = stat.getBlockCount();// 總空間 = 扇區的總數 * 扇區的大小
long totalSize = blockSize * totalBlocks;
// 格式化檔案大小的格式
Log.i("lyb", "總空間 = " + Formatter.formatFileSize(context, totalSize));      return Formatter.formatFileSize(context, totalSize);
}
   // 獲取sd卡的可用大小
public String getAvailableSize(Context context) {
      // 獲取SD卡根目錄
File path = Environment.getExternalStorageDirectory();
// 獲取指定目錄下的記憶體儲存狀態
StatFs stat = new StatFs(path.getPath());
// 獲取單個扇區的大小
long blockSize = stat.getBlockSize();
// 獲取可以使用的扇區數量
long availableBlocks = stat.getAvailableBlocks();
// 可用空間 = 扇區的大小 + 可用的扇區
long availableSize = blockSize * availableBlocks;
// 格式化檔案大小的格式
Log.i("lyb", "可用空間 = " + Formatter.formatFileSize(context, availableSize));      return Formatter.formatFileSize(context, availableSize);
}
   // 獲取sd卡根目錄字串的路徑
public String getSdPath() {
      return Environment.getExternalStorageDirectory().getAbsolutePath();
}

   public File getSdFile() {
      return Environment.getExternalStorageDirectory();
}
   /**
    *  讀取指定檔案中的資料,將資料讀取為byte[]型別
*  引數:要讀取資料的檔案路徑
*/
public byte[] getDataFromFile(String path){
      FileInputStream fis = null;
ByteArrayOutputStream bos = null;
      try {
         fis = new FileInputStream(path);
         byte[] b = new byte[1024];
         int num =-1;
bos = new ByteArrayOutputStream();
         while ((num = fis.read(b)) != -1) {
            bos.write(b,0,num);
}
         return bos.toByteArray();
} catch (Exception e) {e.printStackTrace();
} finally {
         if (fis != null) {
            try {
               fis.close();
} catch (IOException e) {e.printStackTrace();
}
         }
         if (bos != null) {
            try {
               bos.close();
} catch (IOException e) {e.printStackTrace();
}
         }
      }
      return null;
}
   /**
    * 向指定路徑中寫入指定資料
* 1. 設定寫人資料要儲存到的資料夾的路徑
* 2. 要寫入的資料
* 3. 檔名稱
*/
public void saveFile (String path,byte[] b,String fileName) {
      File file = new File(path);
      if (!file.exists()) {
         file.mkdirs();
}
      try {
         FileOutputStream fos = new FileOutputStream(path+File.separator+fileName);
fos.write(b);
fos.close();
} catch (Exception e) {e.printStackTrace();
}
   }
   /*
    * 複製指定檔案    * 1. 要複製的原檔案    * 2. 複製檔案的儲存路徑    * 3. 複製檔案的檔名稱    * */
public void copyFile(File source,String path,String fileName){
      try {
         FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(path+File.separator+fileName);
         byte[] b = new byte[1024];
         int num = -1;
         while((num = fis.read(b)) != -1) {
            fos.write(b,0,num);
}
         fos.close();
fis.close();
} catch (Exception e) {e.printStackTrace();
}
   }
}

2、通過封裝好的工具類提供的操作方法可以獲取到我們想要的資料,然後利用ListView控制元件自定義一個介面卡去展示這些資料。

獲取的資料可能是子資料夾或者檔案,所以我們用兩個圖片標識,這樣列表中的資料就能一目瞭然了。

另外,當用戶點選某個子資料夾進入該子資料夾的目錄下又會得到新的資料。給ListView註冊監聽事件,currentFile是定義的File變數,用來標記當前使用者選中的檔案或者子資料夾,如果currentFile是子資料夾就會再去呼叫initData()方法獲取子資料夾目錄下的檔案,記得在獲取資料前先呼叫clear()方法把當前列表中的資料清空,最後呼叫介面卡的notifyDataSetChanged()方法重新整理列表。

lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
public void onItemClick(AdapterView<?> parent, View view,
                            int position, long id) {
        currentFile = files[position];
        if (currentFile.isDirectory()) {
            initData(currentFile);
adapter.notifyDataSetChanged();
}
    }
});
private void initData(File file) {
    //獲取
files = file.listFiles();
list.clear();
    for (File f: files) {
        if (f.isDirectory()) {  // 判斷其是否為一個資料夾
list.add(f);
} else {
            list.add(f);
}
    }
}
這裡還要注意一點,當用戶點選Back返回鍵時,要判斷當前列表中的資料是否是SD卡根目錄下的資料,如果是,那麼返回鍵就是退出應用程式,直接finish()即可。如果介面上展示的資料是某個子資料夾下的資料,那麼點選返回鍵返回的應該是該子資料夾所在的資料夾目錄下,那麼介面上就會重新展示該子資料夾所在目錄下的資料。
@Override
public void onBackPressed() {
    if (!currentFile.getAbsolutePath().equals(utils.getSdPath())) {
        //獲取上一層目錄
currentFile = currentFile.getParentFile();
initData(currentFile);
adapter.notifyDataSetChanged();
} else {
        finish();
}
}

另外,APP裡用到了一個ProgressBar控制元件,用來展示SD卡記憶體使用的情況。


前面我們獲取的SD卡記憶體總大小(Formatter.formatFileSize(context, totalSize))和可用記憶體空間資料(Formatter.formatFileSize(context, availableSize))是格式化的,3.80GB、4.84GB。所以必須轉成數字。這裡用了字串一些操作方法得到的返回值是可用記憶體所佔總記憶體的百分數,然後給progress.setMax(100)就OK了。當然你也可以不這樣做。

long availableSize = blockSize * availableBlocks; 

long totalSize = blockSize * totalBlocks;

直接拿到long型別的資料。

private int getProgress(String str1, String str2){
    String [] totalArr = str1.split("G");
String [] avaArr = str2.split("G");
    double total = Double.parseDouble(totalArr[0]);
    double ava = Double.parseDouble(avaArr[0]);
    return (int) (ava/total*100);
}

這裡附上完整的程式碼,有興趣的點選下載程式碼:專案原始碼

補充:

Context.getFilesDir(); //得到的是當前應用程式預設在內部的資料儲存目錄
Context.getCacheDir(); //得到的是當前應用程式預設在內部的快取檔案的儲存目錄
Context.getExternalCacheDir(); //得到的是當前應用程式預設在外部的快取檔案的儲存目錄
Context.getgetExternalFilesDir(null); //得到的是當前應用程式預設在外部的資料檔案的儲存目錄
Environment.getExternalStorageDirectory(); //得到的是系統外部資料儲存目錄
Environment.getDownloadCacheDirectory(); //得到的是系統內部快取儲存目錄
Environment.getDataDirectory(); //得到的是系統內部資料儲存目錄

番外篇:

谷歌推出了Android Studio 2.2正式版,你更新了嗎。

本次更新的內容,包括speed, smarts, and Android platform support,也就是速度、智慧和Android平臺三個方面。

約束佈局(ConstraintLayout它是今年 Google 推出的一種全新的佈局,變得更強大,而且屬性也特別多。Constraint Layout 可以實現以前我們開發中各種需要巢狀才能實現的效果,考慮到實際操作,Google 提供了一種完全視覺化的操作介面。


下面給大家推薦兩篇部落格,一篇stormzhangV的,一篇徐宜生的。

醫生的新部落格詳細的介紹了ConstraintLayout基本介面,有興趣的點選可閱: