您的位置:首页 > 移动开发 > Android开发

Android内部存储与外置SDCard

2015-06-26 17:08 791 查看
Android手机存储

通常有三个部分

1、Ram

Ram 也就是相当于PC内存条的东东,没什么好说的。

2、手机存储

手机内通常内置一块eMMC存储设备,也就是常说的Rom。个人觉得这个Rom就相当于PC的硬盘。

其实,手机自带存储叫Rom有点过时了,所谓的“ROM是只读内存(Read-Only Memory)的简称。ROM所存数据,一般是装入整机前事先写好的,整机工作过程中只能读出,而不像随机存储器那样能快速地、方便地加以改写。ROM所存数据稳定 ,断电后所存数据也不会改变;其结构较简单,读出较方便,因而常用于存储各种固定程序和数据”。在Android之前的手机,包括智能机(Nokia、WM等)和非智能机(索爱,Moto P2K平台,MTK等)都有一个单独的ROM芯片保存系统文件。

系统区存储

随着这块芯片的存储容量越来越大,如今的手机很多都进行了分区。一部分相当于以前的Rom,相对于PC就好像是windows的C盘一样,但依然是可写的,用于存储系统,以及安装的程序。“data/data/包名”目录下可存储应用的私有数据,其它应用没有权限读写。

虚拟SD卡(通常说的内部存储)

这是eMMC芯片的另一部分,相当于PC的D盘,至于可不可以把eMMC芯片分更多区,这个没有找到相关资料,也没有发现这样的设备,应该暂时不用管吧。

3、外部存储

也就是外置的SDcard这些东东。

介绍了Android手机的存储情况,那么问题来啦。

都知道取SDcard的路径用的是Android提供的接口 Environment.getExternalStorageDirectory(),但是接口只有一个,取到的是虚拟的SDcard,还是真正的外置SDcard就不知道了。实际上两个都有可能,不同的手机产商得到的结果会不一样。如何取到另一个以及如何知道哪个是真正的外置SDcard。

方法一:通过分析mount指令的返回结果(百度来的,网址不记得了)

public static String[] getAllSDCard() {

String s = "";

try {

Process process = new ProcessBuilder().command("mount")

.redirectErrorStream(true).start();

process.waitFor();

InputStream is = process.getInputStream();

byte[] buffer = new byte[1024];

while (is.read(buffer) != -1) {

s = s + new String(buffer);

}

is.close();

} catch (Exception e) {

e.printStackTrace();

}

//用行分隔mount列表

String[] lines = s.split("\n");

for(int i=0; i<lines.length; i++) {

//如果行内有挂载路径且为vfat类型,说明可能是内置或者外置sd的挂载点

if(-1 != lines[i].indexOf("sdcard") && -1 != lines[i].indexOf("vfat")) {

//再用空格分隔

String[] blocks = lines[i].split("\\s");

for(int j=0; j<blocks.length; j++) {

//判断是否是挂载为vfat类型

if(-1 != blocks[j].indexOf("sdcard")) {

//Test if it is the external sd card.

}

}

}

}

return lines;

}

方法二:读系统文件,从文件中提取出来 (百度来的)

public static Map<String, File> getAllStorageLocations() {

Map<String, File> map = new HashMap<String, File>(10);

List<String> mMounts = new ArrayList<String>(10);

List<String> mVold = new ArrayList<String>(10);

mMounts.add("/mnt/sdcard");

mVold.add("/mnt/sdcard");

try {

File mountFile = new File("/proc/mounts");

if(mountFile.exists()){

Scanner scanner = new Scanner(mountFile);

while (scanner.hasNext()) {

String line = scanner.nextLine();

if (line.startsWith("/dev/block/vold/")) {

String[] lineElements = line.split(" ");

String element = lineElements[1];

// don't add the default mount path

// it's already in the list.

if (!element.equals("/mnt/sdcard"))

mMounts.add(element);

}

}

}

} catch (Exception e) {

e.printStackTrace();

}

try {

File voldFile = new File("/system/etc/vold.fstab");

if(voldFile.exists()){

Scanner scanner = new Scanner(voldFile);

while (scanner.hasNext()) {

String line = scanner.nextLine();

if (line.startsWith("dev_mount")) {

String[] lineElements = line.split(" ");

String element = lineElements[2];

if (element.contains(":"))

element = element.substring(0, element.indexOf(":"));

if (!element.equals("/mnt/sdcard"))

mVold.add(element);

}

}

}

} catch (Exception e) {

e.printStackTrace();

}

for (int i = 0; i < mMounts.size(); i++) {

String mount = mMounts.get(i);

if (!mVold.contains(mount))

mMounts.remove(i--);

}

mVold.clear();

List<String> mountHash = new ArrayList<String>(10);

for(String mount : mMounts){

File root = new File(mount);

if (root.exists() && root.isDirectory() && root.canWrite()) {

File[] list = root.listFiles();

String hash = "[";

if(list!=null){

for(File f : list){

hash += f.getName().hashCode()+":"+f.length()+", ";

}

}

hash += "]";

if(!mountHash.contains(hash)){

String key = "SD_CARD" + "_" + map.size();

if (map.size() == 0) {

key = "SD_CARD";

} else if (map.size() == 1) {

key = "EXTERNAL_SD_CARD";

}

mountHash.add(hash);

map.put(key, root);

}

}

}

mMounts.clear();

if(map.isEmpty()){

map.put("SD_CARD", Environment.getExternalStorageDirectory());

}

return map;

}

事实上,方法一与方法二都可以取到当前挂载的SDcard列表,然而并不能区分是不是虚拟的。

可是系统自带的设置是可以区分,没有办法,只能去看设置的源码。在memory.java中,终于有了我想要的答案。

答案就在这里:

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

final Context context = getActivity();

mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);

mStorageManager = StorageManager.from(context);

mStorageManager.registerListener(mStorageListener);

addPreferencesFromResource(R.xml.device_info_memory);

addCategory(StorageVolumePreferenceCategory.buildForInternal(context));

final StorageVolume[] storageVolumes = mStorageManager.getVolumeList();

for (StorageVolume volume : storageVolumes) {

if (!volume.isEmulated()) {

addCategory(StorageVolumePreferenceCategory.buildForPhysical(context, volume));

}

}

setHasOptionsMenu(true);

}

通过mStorageManager.getVolumeList()就可以得到存储器的列表,volume.isEmulated()可判断是不是虚拟的。一部分手机的isEmulated得到的都是false,结合isRemovable可得到更准确的判断。因为这些方法与类被SDK隐藏了,不能直接调用,所以只能通过java的反射机制了。

方法三:

public static void getAllStorages(Context c) {

StorageManager storageManager = (StorageManager) c

.getSystemService(Context.STORAGE_SERVICE);

try {

Class<?>[] paramClasses = {};

Method getVolumePathsMethod = StorageManager.class.getMethod(

"getVolumeList", paramClasses);

getVolumePathsMethod.setAccessible(true);

Object[] params = {};

Object[] invoke = (Object[]) getVolumePathsMethod.invoke(storageManager, params);

Class<?> volume = Class.forName("android.os.storage.StorageVolume");

Field[] fields = volume.getDeclaredFields();

for (Field f:fields)

LOG.d(f.getName());

Field fieldPath = volume.getDeclaredField("mPath");

Field fieldEmulated = volume.getDeclaredField("mEmulated");

Field fieldRemove = volume.getDeclaredField("mRemovable");

fieldPath.setAccessible(true);

fieldEmulated.setAccessible(true);

fieldRemove.setAccessible(true);

for (int i = 0; i < invoke.length; i++) {

File filePath = (File) fieldPath.get(invoke[i]);

boolean emulated = fieldEmulated.getBoolean(invoke[i]);

boolean remove = fieldRemove.getBoolean(invoke[i]);

LOG.i("\npath : " + filePath.getPath() + " isEmulated : " + emulated + " isRemovable : " + remove);

}

} catch (Exception e) {

e.printStackTrace();

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: