Android media媒体库分析之:分类别统计媒体文件大小

对系统媒体库不了解的线看这儿:
Android media媒体库分析之:MediaProvider http://gqdy365.iteye.com/blog/2150883
Android media媒体库分析之:调用系统媒体库完成指定媒体文件扫描
http://gqdy365.iteye.com/blog/2164836

先看一下要实现的效果:

[img]http://dl2.iteye.com/upload/attachment/0104/4779/ce4808cb-fb82-3f86-ba90-3b831effc643.png[/img]

上图是系统设置中分类别对文件所占空间的统计,项目中要统计媒体文件所占空间,于是研究了一下系统的做法,收获如下:
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,再更新界面;

[b]也就是说:系统只统计了上述几个目录的大小(sMeasureMediaTypes中定义的),如果你的歌曲是拷贝到其他目录,那系统存储里面是不会统计使用量。看来Android也不是很智能嘛!!!难度是考虑效率???[/b]

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文件。如下:

[img]http://dl2.iteye.com/upload/attachment/0104/4818/326c7105-5cdb-33aa-ba07-5e152245120c.png[/img]
这样就可以解决第一个问题了;

再来看第二方法,其实这个方法我们用不到,我们已经通过第一个方法获取到指定文件夹的大小了,那关于某一个文件的大小我们之间通过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://gqdy365.iteye.com/blog/2164836这篇文章可以知道,媒体文件(音频、视频、图片)系统完成扫描之后就存入了数据库,那我们可以查询数据库,得到所有媒体文件,把这些文件的大小相加。
2、由于sdcard可以会unmount,所以在相加时要根据路径判断此文件是否存在;

3、下面时我实现的工具类,希望对你有用:


package com.test.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;
}
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值