获取KitKat 4.4.2中SD卡路径和大小

3
我在Google Play上有一个设备信息应用程序,在应用程序中有存储信息。我知道在Android 4.4中,访问外部SD卡已经发生了一些变化。内部存储似乎没有问题。我的问题是,如何可靠地获取KitKat上SD卡的大小?
我已经列出了所需的权限,因为这在早期版本的Android上运行良好。我在SO上搜索过,但总是得到同样的错误之一。我不需要写入SD卡,只需要读取其可用空间大小。
我有一个来自SO的StorageUtils类,抱歉我记不起链接了。
public class StorageUtils {

private static final String TAG = "StorageUtils";

public static class StorageInfo {

    public final String path;
    public final boolean internal;
    public final boolean readonly;
    public final int display_number;

    StorageInfo(String path, boolean internal, boolean readonly,
            int display_number) {
        this.path = path;
        this.internal = internal;
        this.readonly = readonly;
        this.display_number = display_number;
    }
}

public static ArrayList<StorageInfo> getStorageList() {

    ArrayList<StorageInfo> list = new ArrayList<StorageInfo>();
    String def_path = Environment.getExternalStorageDirectory().getPath();
    boolean def_path_internal = !Environment.isExternalStorageRemovable();
    String def_path_state = Environment.getExternalStorageState();
    boolean def_path_available = def_path_state
            .equals(Environment.MEDIA_MOUNTED)
            || def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
    boolean def_path_readonly = Environment.getExternalStorageState()
            .equals(Environment.MEDIA_MOUNTED_READ_ONLY);
    BufferedReader buf_reader = null;
    try {
        HashSet<String> paths = new HashSet<String>();
        buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
        String line;
        int cur_display_number = 1;
        Log.d(TAG, "/proc/mounts");
        while ((line = buf_reader.readLine()) != null) {
            Log.d(TAG, line);
            if (line.contains("vfat") || line.contains("/mnt")) {
                StringTokenizer tokens = new StringTokenizer(line, " ");
                String unused = tokens.nextToken(); // device
                String mount_point = tokens.nextToken(); // mount point
                if (paths.contains(mount_point)) {
                    continue;
                }
                unused = tokens.nextToken(); // file system
                List<String> flags = Arrays.asList(tokens.nextToken()
                        .split(",")); // flags
                boolean readonly = flags.contains("ro");

                if (mount_point.equals(def_path)) {
                    paths.add(def_path);
                    list.add(new StorageInfo(def_path, def_path_internal,
                            readonly, -1));
                } else if (line.contains("/dev/block/vold")) {
                    if (!line.contains("/mnt/secure")
                            && !line.contains("/mnt/asec")
                            && !line.contains("/mnt/obb")
                            && !line.contains("/dev/mapper")
                            && !line.contains("tmpfs")) {
                        paths.add(mount_point);
                        list.add(new StorageInfo(mount_point, false,
                                readonly, cur_display_number++));
                    }
                }
            }
        }

        if (!paths.contains(def_path) && def_path_available) {
            list.add(new StorageInfo(def_path, def_path_internal,
                    def_path_readonly, -1));
        }

    } catch (FileNotFoundException ex) {
        ex.printStackTrace();
    } catch (IOException ex) {
        ex.printStackTrace();
    } finally {
        if (buf_reader != null) {
            try {
                buf_reader.close();
            } catch (IOException ex) {
            }
        }
    }
    return list;
}

public static String getReadableFileSize(long bytes, boolean si) {
    int unit = si ? 1000 : 1024;
    if (bytes < unit)
        return bytes + " B";
    int exp = (int) (Math.log(bytes) / Math.log(unit));
    String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1)
            + (si ? "" : "i");
    return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}

@SuppressLint("NewApi")
public static long getFreeSpace(String path) {
    StatFs statFs = new StatFs(path);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        long sdAvailSize = statFs.getFreeBlocksLong()
                * statFs.getBlockSizeLong();
        return sdAvailSize;
    } else {
        @SuppressWarnings("deprecation")
        double sdAvailSize = (double) statFs.getFreeBlocks()
                * (double) statFs.getBlockSize();

        return (long) sdAvailSize;
    }
}

public static long getUsedSpace(String path) {
    return getTotalSpace(path) - getFreeSpace(path);
}

@SuppressLint("NewApi")
public static long getTotalSpace(String path) {
    StatFs statFs = new StatFs(path);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        long sdTotalSize = statFs.getBlockCountLong()
                * statFs.getBlockSizeLong();
        return sdTotalSize;
    } else {
        @SuppressWarnings("deprecation")
        double sdTotalSize = (double) statFs.getBlockCount()
                * statFs.getBlockSize();

        return (long) sdTotalSize;
    }
}

/**
 * getSize()[0] is /mnt/sdcard. getSize()[1] is size of sd (example 12.0G),
 * getSize()[2] is used, [3] is free, [4] is blksize
 * 
 * @return
 * @throws IOException
 */
public static String[] getSize() throws IOException {
    String memory = "";
    Process p = Runtime.getRuntime().exec("df /mnt/sdcard");
    InputStream is = p.getInputStream();
    int by = -1;
    while ((by = is.read()) != -1) {
        memory += new String(new byte[] { (byte) by });
    }
    for (String df : memory.split("/n")) {
        if (df.startsWith("/mnt/sdcard")) {
            String[] par = df.split(" ");
            List<String> pp = new ArrayList<String>();
            for (String pa : par) {
                if (!pa.isEmpty()) {
                    pp.add(pa);
                }
            }
            return pp.toArray(new String[pp.size()]);

        }
    }
    return null;
}

}

以下是我的代码片段,我试图显示SD卡路径和大小。

public class CpuMemFragment extends Fragment {
// CPU
String devCpuInfo;
TextView tvCpuInfo;

// RAM
String devRamInfo;
TextView tvRamInfo;

// Storage
String devStorageA, devStorageB;
TextView tvStorageAName, tvStorageA, tvStorageB, tvStorageBName;

AdView adView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.cpu_mem, container, false);

    return rootView;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onActivityCreated(savedInstanceState);

    // *** CPU ***
    //
    devCpuInfo = readCpuInfo();
    //
    // #################################

    // *** RAM ***
    //
    devRamInfo = readTotalRam();
    //
    // #################################

    // *** STORAGE ***
    //

    ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();

    tvStorageAName = (TextView) getView().findViewById(R.id.tvStorageAName);

    tvStorageBName = (TextView) getView().findViewById(R.id.tvStorageBName);

    if (storageInfoList.size() > 0) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
                && !storageInfoList.get(0).internal) {
            kitKatWorkaround(0);
        }
        tvStorageAName.setText(storageInfoList.get(0).path);

        devStorageA = StorageUtils.getReadableFileSize(
                (StorageUtils.getUsedSpace(storageInfoList.get(0).path)),
                true)
                + "/"
                + StorageUtils.getReadableFileSize((StorageUtils
                        .getTotalSpace(storageInfoList.get(0).path)), true);

        if (storageInfoList.size() > 1) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
                    && !storageInfoList.get(0).internal) {
                kitKatWorkaround(1);
            }
            tvStorageBName.setText(storageInfoList.get(1).path);

            devStorageB = StorageUtils.getReadableFileSize(
                    StorageUtils.getUsedSpace(storageInfoList.get(1).path)
                            + (StorageUtils.getUsedSpace("system/")), true)
                    + "/"
                    + StorageUtils.getReadableFileSize((StorageUtils
                            .getTotalSpace(storageInfoList.get(1).path)),
                            true);
        } else {
            devStorageB = "N/A";
        }
    } else {
        devStorageA = "N/A";
        devStorageB = "N/A";
    }
    //
    // #################################

    // CPU
    tvCpuInfo = (TextView) getView().findViewById(R.id.tvCpuInfo);
    tvCpuInfo.setText(devCpuInfo);
    //
    // #################################

    // RAM
    tvRamInfo = (TextView) getView().findViewById(R.id.tvRamInfo);
    tvRamInfo.setText(devRamInfo);
    //
    // #################################

    // STORAGE
    tvStorageA = (TextView) getView().findViewById(R.id.tvStorageA);
    tvStorageA.setText(devStorageA);

    tvStorageB = (TextView) getView().findViewById(R.id.tvStorageB);
    tvStorageB.setText(devStorageB);
    //
    // #################################

    // Look up the AdView as a resource and load a request.
    adView = (AdView) getActivity().findViewById(R.id.adCpuMemBanner);
    AdRequest adRequest = new AdRequest.Builder().build();
    adView.loadAd(adRequest);
}

@Override
public void onPause() {
    if (adView != null) {
        adView.pause();
    }
    super.onPause();
}

@Override
public void onResume() {
    super.onResume();
    if (adView != null) {
        adView.resume();
    }
}

@Override
public void onDestroy() {
    if (adView != null) {
        adView.destroy();
    }
    super.onDestroy();
}

private static synchronized String readCpuInfo() {
    ProcessBuilder cmd;
    String result = "";

    try {
        String[] args = { "/system/bin/cat", "/proc/cpuinfo" };
        cmd = new ProcessBuilder(args);

        Process process = cmd.start();
        InputStream in = process.getInputStream();
        byte[] re = new byte[1024];
        while (in.read(re) != -1) {
            System.out.println(new String(re));
            result = result + new String(re);
        }
        in.close();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    return result;
}

public static synchronized String readTotalRam() {
    String load = "";
    try {
        RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
        load = reader.readLine();
        reader.close();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    return load;
}

public void kitKatWorkaround(int index) {
    String path1 = Environment.getExternalStorageDirectory().getPath();

            //Storage A
    if (index == 0) {

        tvStorageAName.setText(path1);

        devStorageA = StorageUtils.getReadableFileSize(
                (StorageUtils.getUsedSpace(path1)), true)
                + "/"
                + StorageUtils.getReadableFileSize(
                        (StorageUtils.getTotalSpace(path1)), true);
    }

            //Storage B
    if (index == 1) {
        tvStorageBName.setText(path1);

        devStorageB = StorageUtils.getReadableFileSize(
                StorageUtils.getUsedSpace(path1)), true)
                + "/"
                + StorageUtils.getReadableFileSize(
                        (StorageUtils.getTotalSpace(path1)), true);

    }
}
}

这会导致 EACCES 错误 或在 KitKat 上出现 无效路径(访问被拒绝)。请帮助解决,非常感谢您的时间。
1个回答

2

您可以通过调用Context.getExternalFilesDirs()Context.getExternalCacheDirs()来查找所有共享存储设备。然后,您可以调用File.getUsableSpace()或使用android.os.StatFs检查每个位置的可用磁盘空间。

这些API也在支持库中的ContextCompat中提供,以便无缝地在KitKat之前的设备上工作。


到目前为止,这个代码已经可以工作了,但是我该如何获取SD卡根目录的路径呢?tvStorageAName.setText(sdCard1.getAbsolutePath().replace("Android/data/" + getActivity().getPackageName() + "/cache", ""));这样写合理吗?编辑:在我的Note3设备上似乎很好用。谢谢,希望这也能帮助其他人 :) 祝编码愉快! - MattMatt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接