歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Android media媒體庫分析之:分類別統計媒體文件大小

對系統媒體庫不了解的線看這兒:

  • Android media媒體庫分析之:MediaProvider http://www.linuxidc.com/Linux/2015-03/114755.htm
  • Android media媒體庫分析之:調用系統媒體庫完成指定媒體文件掃描 http://www.linuxidc.com/Linux/2015-03/114756.htm

先看一下要實現的效果:

上圖是系統設置中分類別對文件所占空間的統計,項目中要統計媒體文件所占空間,於是研究了一下系統的做法,收獲如下:

1、從源碼packages/app/下找到settings工程,找到存儲功能的實現,相關類有:

com.android.settings.deviceinfo.StorageMeasurement
com.android.settings.deviceinfo.StorageVolumePreferenceCategory
其他相關源碼:
com.android.defcontainer.DefaultContainerService

StorageMeasurement類中啟動服務,綁定服務,通知界面更新;
StorageVolumePreferenceCategory顯示界面
StorageMeasurement中建立了與DefaultContainerService服務的通信,指定要掃描的目錄:

    /** Media types to measure on external storage. */
    private static final Set<String> sMeasureMediaTypes = Sets.newHashSet(
            Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
            Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_MUSIC,
            Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
            Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS,
            Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);

上面這個set就是定義了我們系統裡面常用的目錄:

public static String DIRECTORY_DOWNLOADS = "Download";
public static String DIRECTORY_DCIM = "DCIM";
public static String DIRECTORY_MUSIC = "Music";
public static String DIRECTORY_PICTURES = "Pictures";
public static String DIRECTORY_MOVIES = "Movies";
...

獲取每個目錄的大小:

                for (String type : sMeasureMediaTypes) {
                    final File path = currentEnv.getExternalStoragePublicDirectory(type);
                    final long size = getDirectorySize(imcs, path);
                    details.mediaSize.put(type, size);
                }

其中getDirectorySize方法通過IMediaContainerService調用了DefaultContainerService服務中的方法,在DefaultContainerService中獲取到所有信息之後回調IMediaContainerService,再更新界面;

也就是說:系統只統計了上述幾個目錄的大小(sMeasureMediaTypes中定義的),如果你的歌曲是拷貝到其他目錄,那系統存儲裡面是不會統計使用量。看來Android也不是很智能嘛!!!難度是考慮效率???

2、如何獲取某個目錄的大小?
先看一下系統是怎麼做的:
下面方法是在DefaultContainerService中定義的,獲取某一目錄大小,獲取某一文件大小;

        @Override
        public long calculateDirectorySize(String path) throws RemoteException {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

            final File directory = new File(path);
            if (directory.exists() && directory.isDirectory()) {
                return MeasurementUtils.measureDirectory(path);
            } else {
                return 0L;
            }
        }

        @Override
        public long[] getFileSystemStats(String path) {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

            try {
                final StructStatFs stat = Libcore.os.statfs(path);
                final long totalSize = stat.f_blocks * stat.f_bsize;
                final long availSize = stat.f_bavail * stat.f_bsize;
                return new long[] { totalSize, availSize };
            } catch (ErrnoException e) {
                throw new IllegalStateException(e);
            }
        }

但是
MeasurementUtils.measureDirectory(path)和Libcore.os.statfs(path)
兩個方法我們都調不到,怎麼辦?
我們把MeasurementUtils類從源碼中拷貝出來到我們的功能,但注意要保留包路徑,否則可以編譯通過,但無法運行,因為他裡面用到了一個本地方法,需要加載一個so文件。如下:

 
這樣就可以解決第一個問題了;

再來看第二方法,其實這個方法我們用不到,我們已經通過第一個方法獲取到指定文件夾的大小了,那關於某一個文件的大小我們之間通過file可以拿到。
第二個方法有一個封裝類:starfs,可以獲取rom、sdcard的總大小和可用空間,如下:

    /**
    * The size, in bytes, of a block on the file system. This corresponds to
    * the Unix {@code statfs.f_bsize} field.
    */
    public int getBlockSize() {
        return (int) mStat.f_bsize;
    }

    /**
    * The total number of blocks on the file system. This corresponds to the
    * Unix {@code statfs.f_blocks} field.
    */
    public int getBlockCount() {
        return (int) mStat.f_blocks;
    }

這個可以直接在我們獲取總體容量時使用。

上面把系統是怎麼做的大概搞清楚,下面我們來實現我們的需求:

一、思路:
1、Android media媒體庫分析之:調用系統媒體庫完成指定媒體文件掃描 http://www.linuxidc.com/Linux/2015-03/114756.htm
這篇文章可以知道,媒體文件(音頻、視頻、圖片)系統完成掃描之後就存入了數據庫,那我們可以查詢數據庫,得到所有媒體文件,把這些文件的大小相加。
2、由於sdcard可以會unmount,所以在相加時要根據路徑判斷此文件是否存在;

3、下面時我實現的工具類,希望對你有用:

package com.linuxidc.jerome;

import java.io.File;
import java.util.ArrayList;

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.StatFs;
import android.provider.MediaStore;

public class MemoryUtil {
 Context mContext;

 public MemoryUtil(Context context) {
  mContext = context;
 }

 /**
  * 獲得SD卡總大小
  *
  * @return
  */
 public long getSDTotalSize() {
  File path = Environment.getExternalStorageDirectory();
  StatFs stat = new StatFs(path.getPath());
  long blockSize = stat.getBlockSize();
  long totalBlocks = stat.getBlockCount();
  return blockSize * totalBlocks;
 }

 /**
  * 獲得sd卡剩余容量,即可用大小
  *
  * @return
  */
 public long getSDAvailableSize() {
  File path = Environment.getExternalStorageDirectory();
  StatFs stat = new StatFs(path.getPath());
  long blockSize = stat.getBlockSize();
  long availableBlocks = stat.getAvailableBlocks();
  return blockSize * availableBlocks;
 }

 /**
  * 獲得機身內存總大小
  *
  * @return
  */
 public long getRomTotalSize() {
  File path = Environment.getDataDirectory();
  StatFs stat = new StatFs(path.getPath());
  long blockSize = stat.getBlockSize();
  long totalBlocks = stat.getBlockCount();
  return blockSize * totalBlocks;
 }

 /**
  * 獲得機身可用內存
  *
  * @return
  */
 public long getRomAvailableSize() {
  File path = Environment.getDataDirectory();
  StatFs stat = new StatFs(path.getPath());
  long blockSize = stat.getBlockSize();
  long availableBlocks = stat.getAvailableBlocks();
  return blockSize * availableBlocks;
 }

 /**
  * 外部存儲中所有音頻文件所占內存
  *
  * @return
  */
 public long getAudioTotalSize() {
  ArrayList<MemoryInfo> resultList = queryAllMediaList(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
  long size = 0L;
  for (MemoryInfo cInfo : resultList) {
   File file = new File(cInfo.getFilePath());
   if(null!=file &&file.exists()){
    size += cInfo.getFileSize();
   }
  }
  return size;
 }

 /**
  * 外部存儲中除音頻、視頻、圖片之前其他文件所占內存
  *
  * @return
  */
 public long getOtherTotalSize() {
  long size = getSDTotalSize() - getSDAvailableSize()
    - getPictureTotalSize() - getVideoTotalSize()
    - getAudioTotalSize();
  if (size < 0L) {
   size = 0L;
  }
  return size;
 }

 /**
  * 外部存儲中所有圖片文件所占內存
  *
  * @return
  */
 public long getPictureTotalSize() {
  ArrayList<MemoryInfo> resultList = queryAllMediaList(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
  long size = 0L;
  for (MemoryInfo cInfo : resultList) {
   File file = new File(cInfo.getFilePath());
   if(null!=file &&file.exists()){
    size += cInfo.getFileSize();
   }
  }
  return size;
 }

 /**
  * 外部存儲中所有視頻文件所占內存
  *
  * @return
  */
 public long getVideoTotalSize() {
  ArrayList<MemoryInfo> resultList = queryAllMediaList(MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
  long size = 0L;
  for (MemoryInfo cInfo : resultList) {
   File file = new File(cInfo.getFilePath());
   if(null!=file &&file.exists()){
    size += cInfo.getFileSize();
   }
  }
  return size;
 }

 public ArrayList<MemoryInfo> queryAllMediaList(Uri uri) {
  //我們只需要兩個字段:大小、文件路徑
  Cursor cursor = mContext.getContentResolver().query(
    uri,new String[] { MediaStore.Audio.Media.SIZE,
      MediaStore.Audio.Media.DATA }, null, null, null);

  ArrayList<MemoryInfo> musicList = new ArrayList<MemoryInfo>();
 
  try{
   if (cursor.moveToFirst()) {
    do {
     MemoryInfo mInfo = new MemoryInfo();
     mInfo.setFileSize(cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE)));
     mInfo.setFilePath(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
    }while(cursor.moveToNext());
   }
  }finally{
   if(cursor != null){
    cursor.close();
   }
  }
 
  return musicList;

 }
 
 class MemoryInfo {
  private long fileSize = 0L;
  private String filePath = "";

  public long getFileSize() {
   return fileSize;
  }

  public void setFileSize(long fileSize) {
   this.fileSize = fileSize;
  }

  public String getFilePath() {
   return filePath;
  }

  public void setFilePath(String filePath) {
   this.filePath = filePath;
  }
 }
}

更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11

Copyright © Linux教程網 All Rights Reserved