美文网首页Android开发经验谈Android开发程序员
Android SD卡及U盘插拔状态监听及内容读取

Android SD卡及U盘插拔状态监听及内容读取

作者: EvanZch | 来源:发表于2018-09-20 11:49 被阅读5次

    本篇是通过系统方法来对sd卡及U盘插拔监听及数据获取,Android盒子端开发,有系统权限,当然,这个比较简单,知道具体方法,可以通过反射来实现。

    先贴上效果图:

    获取外置存储设备并监听插拔状态

    获取文件内容

    前言

    先说需求,App在引导过程中,通过外置存储设备(U盘或者sd卡)上传指定的配置文件,开始我没打算用系统方法,网上看到 libaums 这个库文件,尝试使用了一下,但是最后发现它并不能友好的支持NTFS格式U盘,能监听到,但是好像没有办法获取到路径,最后看官方也说了不支持NTFS格式,最后索性直接使用系统方法,反正有权限,真的可以为所欲为

    正文


    可以看到系统设置里面,是能监听到ntfs格式u盘(Evan_zch)的,并且能获取U盘里面的文件,这样就好办了,挽起袖子直接开干。

    1、页面定位

    要查看具体某个功能的源码,可以通过界面定位,这样能更快的找到我们想要的代码。
    通过执行下面代码,可以直接定位当前展示界面的包名和类名。

    linux:
    
    adb shell dumpsys activity | grep "mFocusedActivity"
    
    windows:
    
    adb shell dumpsys activity | findstr "mFocusedActivity"
    

    执行结果:


    此时可以定位到系统设置存储界面是StorageSettingsActivity,这个时候可以去 Android OS 这个网站搜索并查看相应的源码。

    Snipaste_2018-09-20_11-43-55.png

    2、源码分析

    直接查看 StorageSettings 这个界面源码,这个比较简单,大致还是能看的清楚,因为项目时间比较紧,没有仔细去研究,只贴一些关键代码,具体能实现我的需求,等完成了这个项目,再好好来琢磨。

    private StorageManager mStorageManager;
    // 创建 StorageManager
    mStorageManager = context.getSystemService(StorageManager.class);
    // 注册监听
    mStorageManager.registerListener(mStorageListener);
    
    // 监听回调
    private final StorageEventListener mStorageListener = new StorageEventListener() {
            @Override
            public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
                if (isInteresting(vol)) {
                    refresh();
                }
            }
    
            @Override
            public void onDiskDestroyed(DiskInfo disk) {
                refresh();
            }
        };
        
        
     
    private static boolean isInteresting(VolumeInfo vol) {
            switch(vol.getType()) {
                // 内置存储设备
                case VolumeInfo.TYPE_PRIVATE:
                // 外置存储设备
                case VolumeInfo.TYPE_PUBLIC:
                    return true;
                default:
                    return false;
            }
        }
    

    在源码中,直接在 onCreate 方法中创建 StorageManager 然后通过 registerListener 注册存储设备的监听,在监听回调里可以直接判断存储设备的状态,其中说一下 onVolumeStateChanged 回调中的 oldStatenewState 参数。通过查看源码,能发现在 VolumeInfo 类中设置了存储设备的一些基本状态。

        public static final int STATE_UNMOUNTED = 0;
        public static final int STATE_CHECKING = 1;
        public static final int STATE_MOUNTED = 2;
        public static final int STATE_MOUNTED_READ_ONLY = 3;
        public static final int STATE_FORMATTING = 4;
        public static final int STATE_EJECTING = 5;
        public static final int STATE_UNMOUNTABLE = 6;
        public static final int STATE_REMOVED = 7;
        public static final int STATE_BAD_REMOVAL = 8;
    

    在回调中添加打印日志:


    通过后台日志,可以发现在U盘插入过程中,其状态变化为:
    STATE_UNMOUNTED ——> STATE_CHECKING ——> STATE_MOUNTED
    U盘插入日志
    U盘拨出,状态变化:
    STATE_EJECTING ——> STATE_UNMOUNTED ——> STATE_BAD_REMOVAL
    最后调用监听回调中的 onDiskDestroyed 方法。
    U盘拔出日志

    为了避免 onVolumeStateChanged 的多次回调,我自己写了个判断方法 isMounted,我们可以在 onVolumeStateChanged 加入 isMounted判断方法,只有当是外置存储设备且挂载成功后才进行ui更新。

     
     public boolean isMounted(VolumeInfo vol, int oldState, int newState) {
            return (isInteresting(vol) && oldState != newState && newState == VolumeInfo.STATE_MOUNTED);
        }
    
    
        private static boolean isInteresting(VolumeInfo vol) {
            switch (vol.getType()) {
                // 这里我们只关心外置存储设备,所以直接注释掉了 TYPE_PRIVATE
                // case VolumeInfo.TYPE_PRIVATE:
                case VolumeInfo.TYPE_PUBLIC:
                    return true;
                default:
                    return false;
            }
        }
    

    在监听回调后,可以通过StorageManager类的 getVolumes方法,获取所有的存储设备。

    public List<VolumeInfo> getStorageDeviceList() {
            if (mStorageManager == null) {
                throw new RuntimeException("StorageManagerUtils not init");
            }
            List<VolumeInfo> volumes = mStorageManager.getVolumes();
            List<VolumeInfo> publicVolumes = new ArrayList<>();
            publicVolumes.clear();
            for (VolumeInfo info : volumes) {
                int type = info.getType();
                // 获取当前存储设备的路径
                File path = volumeInfo.getPath();
                // 同样的,只关心外置存储设备。
                if (info.getType() == VolumeInfo.TYPE_PUBLIC) {
                    publicVolumes.add(info);
                }else if(info.getType() == VolumeInfo.TYPE_PRIVATE){
                    // 获取内置存储设备
                }
            }
            return publicVolumes;
        }
    

    当拿到设备后,通过 VolumeInfo 类的 getPath 方法就可以获取到U盘具体路径


    后面就可以通过这个路径直接获取U盘的文件了,基本就大功告成,时间有点急,写得有点粗糙,后面有时间再整理一下。
    最后贴一下工具类的完整代码:
    /**
     * @author Evan_zch
     * @date 2018/9/17 19:13
     * <p>
     * 存储设备管理类
     */
    public class StorageManagerUtils {
    
        private static final String TAG = "StorageManagerUtils";
        private final StorageManager mStorageManager;
        private static long totalBytes = 0;
        private static long usedBytes = 0;
    
        private static final class StorageManagerHolder {
            private static final StorageManagerUtils INSTANCE = new StorageManagerUtils();
        }
    
        public static StorageManagerUtils getInstance() {
            return StorageManagerHolder.INSTANCE;
        }
    
        private StorageManagerUtils() {
            mStorageManager = DigiTvApplication.getAppContext().getSystemService(StorageManager.class);
        }
    
        public List<VolumeInfo> getStorageDeviceList() {
            if (mStorageManager == null) {
                throw new RuntimeException("StorageManagerUtils not init");
            }
            List<VolumeInfo> volumes = mStorageManager.getVolumes();
            List<VolumeInfo> publicVolumes = new ArrayList<>();
            publicVolumes.clear();
            for (VolumeInfo info : volumes) {
                int type = info.getType();
                if (info.getType() == VolumeInfo.TYPE_PUBLIC) {
                    Logutils.d(TAG + "--refresh  type is public");
                    String bestVolumeDescription = mStorageManager.getBestVolumeDescription(info);
                    File path = info.getPath();
                    Logutils.d(TAG + "--refresh  type=" + type + ",bestVolumeDescription=" + bestVolumeDescription + ",path=" + path);
                    publicVolumes.add(info);
                }
            }
            return publicVolumes;
        }
    
        public boolean isMounted(VolumeInfo vol, int oldState, int newState) {
            return (isInteresting(vol) && oldState != newState && newState == VolumeInfo.STATE_MOUNTED);
        }
    
        private static boolean isInteresting(VolumeInfo vol) {
            switch (vol.getType()) {
                //case VolumeInfo.TYPE_PRIVATE:
                case VolumeInfo.TYPE_PUBLIC:
                    return true;
                default:
                    return false;
            }
        }
    
        public String getTotalSize(VolumeInfo vol) {
            if (vol.isMountedReadable()) {
                final File path = vol.getPath();
                if (totalBytes <= 0) {
                    totalBytes = path.getTotalSpace();
                }
            }
            return Formatter.formatFileSize(DigiTvApplication.getAppContext(), totalBytes);
        }
    
        public String getUsedSize(VolumeInfo vol) {
            if (vol.isMountedReadable()) {
                final File path = vol.getPath();
                final long freeBytes = path.getFreeSpace();
                usedBytes = totalBytes - freeBytes;
            }
            return Formatter.formatFileSize(DigiTvApplication.getAppContext(), usedBytes);
        }
    }
    

    相关文章

      网友评论

        本文标题:Android SD卡及U盘插拔状态监听及内容读取

        本文链接:https://www.haomeiwen.com/subject/qzignftx.html