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")
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();
}
}
通常有三个部分
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();
}
}