我有一台中兴的Android手机,型号是 ZTE U930HD,手机没有插入外置SD卡(也就是Micro SD Card,原名Trans-flash Card(TF卡),2004年正式更名为Micro SD Card),但是机身自带了一个内置存储卡(也就是eMMC存储,大小为2G)。

我把这个手机用数据线插到电脑上,也会看到盘符,通过安装「R.E 管理器」等文件管理应用,也可以管理文件,并且能看到该存储的挂载目录是:/mnt/sdcard2

但是,

我打印 Environment.getExternalStorageState(),却返回 ”removed“;

这是怎么回事?明明手机本身带着内置SD卡,却为何提示这么一个信息?

我又试着去打印了Environment.getExternalStorageDirectory(),返回:“/mnt/sdcard”

看起来可以解释了,在我这个中兴手机上,调用Environment.getExternalStorageDirectory(),返回的存储目录并不是系统内置的SD卡目录。

我又换了一个 Sony L39u,一个 MOTO G,调用Environment.getExternalStorageDirectory()返回的目录就是系统内置的SD卡目录。

不同的设备上,调用getExternalStorageDirectory()返回值却不一样。查询了Android的文档,才找到原因,原来这个方法返回的是当前设备厂商所认为的“外部存储”,有可能返回外置的SD卡目录(Micro SD Card),也可能返回内置的存储目(eMMC)。

总结一下:

一部分手机将eMMC存储挂载到 /mnt/external_sd 、/mnt/sdcard2 等节点,而将外置的SD卡挂载到 Environment.getExternalStorageDirectory()这个结点。
此时,调用Environment.getExternalStorageDirectory(),则返回外置的SD的路径。


而另一部分手机直接将eMMC存储挂载在Environment.getExternalStorageDirectory()这个节点,而将真正的外置SD卡挂载到/mnt/external_sd、/mnt/sdcard2 等节点。
此时,调用Environment.getExternalStorageDirectory(),则返回内置的SD的路径。

至此就能解释为都是无外置SD卡的情况下,在中兴手机上,调用

打印 Environment.getExternalStorageState(),却返回 ”removed“,在索尼、MOTO G上就返回:“mounted”

原因已经知道了,可是如何在无外置SD卡的时候,获取到这个内置eMMC存储的具体路径呢?

比如,我这个中兴手机,既然使用 Environment.getExternalStorageDirectory() 获取到的是外置SD卡路径,但是我又没有插入SD卡,这个时候我想使用内置的eMMC存储来存储一些程序中用到的数据,我怎么去获取这个eMMC存储的路径呢?

答案是:通过扫描系统文件"system/etc/vold.fstab”来实现。

"system/etc/vold.fstab” 只是一个简单的配置文件,它描述了Android的挂载点信息。
我们可以遍历这个文件来获取所有的挂载点:

 /**
      * 遍历 "system/etc/vold.fstab” 文件,获取全部的Android的挂载点信息
     
      * @return
      */
     private  static  ArrayList<String> getDevMountList() {
         String[] toSearch = readFile( "/etc/vold.fstab" ).split( " " );
         ArrayList<String> out =  new  ArrayList<String>();
         for  ( int  i =  0 ; i < toSearch.length; i++) {
             if  (toSearch[i].contains( "dev_mount" )) {
                 if  ( new  File(toSearch[i +  2 ]).exists()) {
                     out.add(toSearch[i +  2 ]);
                 }
             }
         }
         return  out;

    }

/**

     * read file

     * 

     * @param filePath

     * @param charsetName The name of a supported {@link java.nio.charset.Charset </code>charset<code>}

     * @return if file not exist, return null, else return content of file

     * @throws RuntimeException if an error occurs while operator BufferedReader

     */

    public static String readFile(String filePath) {

        String fileContent = "";

        File file = new File(filePath);

        if (file == null || !file.isFile()) {

            return null;

        }

 

        BufferedReader reader = null;

        try {

            InputStreamReader is = new InputStreamReader(new FileInputStream(file));

            reader = new BufferedReader(is);

            String line = null;

            int i = 0;

            while ((line = reader.readLine()) != null) {

                fileContent += line + " ";

            }

            reader.close();

            return fileContent;

        } catch (IOException e) {

            e.printStackTrace();

        } finally {

            if (reader != null) {

                try {

                    reader.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }

        return fileContent;

    }



之后,当 Environment.getExternalStorageState()返回“removed”的时候(即,当没有挂载外置SD卡的时候),通过getDevMountList()方法获取一个list,这个list中可以进行写操作的那个就是系统自带的eMMC存储目录了。

判断逻辑:

/**
      * 获取扩展SD卡存储目录
     
      * 如果有外接的SD卡,并且已挂载,则返回这个外置SD卡目录
      * 否则:返回内置SD卡目录
     
      * @return
      */
     public  static  String getExternalSdCardPath() {
 
         if  (SDCardUtils.isMounted()) {
             File sdCardFile =  new  File(Environment.getExternalStorageDirectory().getAbsolutePath());
             return  sdCardFile.getAbsolutePath();
         }
 
         String path =  null ;
 
         File sdCardFile =  null ;
 
         ArrayList<String> devMountList = getDevMountList();
 
         for  (String devMount : devMountList) {
             File file =  new  File(devMount);
 
             if  (file.isDirectory() && file.canWrite()) {
                 path = file.getAbsolutePath();
 
                 String timeStamp =  new  SimpleDateFormat( "ddMMyyyy_HHmmss" ).format( new  Date());
                 File testWritable =  new  File(path,  "test_"  + timeStamp);
 
                 if  (testWritable.mkdirs()) {
                     testWritable.delete();
                 else  {
                     path =  null ;
                 }
             }
         }
 
         if  (path !=  null ) {
             sdCardFile =  new  File(path);
             return  sdCardFile.getAbsolutePath();
         }
 
         return  null ;
     }


但有的手机设备中无法用getExternalSdCardPath()得到内置存储的路径,在system/etc/vold.fstab文件里并没有列明,如三星手机。这时需要通过使用linux的 mount命令来实现读取路径。

/*

获得外部内置存在路径

通过执行mount命令来获得内置存储的路径

*/

public String getExterPath(){

//得到路径

String sdcard_path="";

try {

Runtime runtime = Runtime.getRuntime();

Process proc = runtime.exec("mount");

InputStream is = proc.getInputStream();

InputStreamReader isr = new InputStreamReader(is);

String line;


BufferedReader br = new BufferedReader(isr);

while ((line = br.readLine()) != null) {

if (line.contains("secure")) continue;

if (line.contains("asec")) continue;


if (line.contains("fat")) {

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

if (columns != null && columns.length > 1) {

sdcard_path = sdcard_path.concat(columns[1]);

}

} else if (line.contains("fuse")) {

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

if (columns != null && columns.length > 1) {

sdcard_path = sdcard_path.concat(columns[1] );

}

}

}

}

catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return sdcard_path;

}