实战Android读取USB数据到手机自带存储中

    • 0x01 写在前面的话
    • 0x02 Android 连接USB设备进行文件传输
      • 2.1 添加权限
      • 2.2 添加USB 读取类库
      • 2.3 注册广播
      • 2.4 申请读写权限
      • 2.4 调用读取设备的意图
        • 2.4.1 发送广播意图
        • 2.4.2 拦截广播意图
        • 2.4.3 设备信息初始化
        • 2.4.4 递归遍历USB 设备中的所有图片
        • 2.4.5 找到一个图片文件就把它保存到手机SDCard 中

0x01 写在前面的话

最近朋友在做一个手机连接单反相机实现相册直播边拍边传功能。经查资料得知,

Android 应用获取外部设备文件一共有这样四种方式

  • 内容提供器 (Content-Provider) —已测试,独占模式, 而且需要手动点击导入到手机系统相册中才能使用

  • USB 传输协议 ----------------已测试,不支持单反相机,仅支持单反相机取出来内存卡数据读取

  • MTP传输协议 (Media Transfer Protocol)-----已测试,独占模式,可导出真实图片和缩略图

  • PTP 传输协议 (Picture Transfer Protocol)Digital Camera 数码相机----有同行测试成功

更多讨论方案尝试详情请移步https://github.com/geekxingyun/AndroidOtgUSBMtpSample

0x02 Android 连接USB设备进行文件传输

2.1 添加权限

首先,我们要读取USB 设备所以肯定需要USB 的读写权限,所以在
AndroidManifest.xml 中添加SD Card 的读写权限

        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>       <uses-feature android:name="android.hardware.usb.host" android:required="true" />

2.2 添加USB 读取类库

为了简化USB 设备的读取,我们添加下这个类库

    implementation 'com.github.mjdev:libaums:0.5.5'

2.3 注册广播

我们需要让我们的程序知道USB 设备插入了和拔出了,这两个状态进行监听,我们这里需要用到Android 中的广播。
这里 采用动态广播注册技术

    /*****     * 动态注册USB 设备监听     * */    private void registerUSBReceiver() {        IntentFilter intentFilter = new IntentFilter();        //自定义USB设备读取照片        intentFilter.addAction(READ_USB_DEVICE_PERMISSION);        //USB连接状态发生变化时产生的广播        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);        USBMTPReceiver usbmtpReceiver = new USBMTPReceiver();        registerReceiver(usbmtpReceiver, intentFilter);    }

注册完广播我们还需要在OnDestroy方法里取消注册的广播

  @Override    protected void onDestroy() {        super.onDestroy();        if (usbmtpReceiver != null) {            //取消注册USB设备广播            unregisterReceiver(usbmtpReceiver);        }    }

2.4 申请读写权限

/***     * 请求读写权限     * ****/    private static String[] PERMISSIONS_STORAGE = {            Manifest.permission.READ_EXTERNAL_STORAGE,            Manifest.permission.WRITE_EXTERNAL_STORAGE};    private static int REQUEST_PERMISSION_CODE = 1;    private void requestReadAndWriteAccess(){        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {            if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {                ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_STORAGE, REQUEST_PERMISSION_CODE);            }        }    }        //读写权限回调    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        super.onRequestPermissionsResult(requestCode, permissions, grantResults);        if (requestCode == REQUEST_PERMISSION_CODE) {            for (int i = 0; i < permissions.length; i++) {                SmartToastUtils.showShort(MainActivity.this,"申请的权限为:" + permissions[i] + ",申请结果:" + grantResults[i]);            }        }    }

2.4 调用读取设备的意图

2.4.1 发送广播意图

 //发送USB广播    private void sendUSBBroadcast() {        //发送广播        Intent intent=new Intent(READ_USB_DEVICE_PERMISSION);        //发送标准广播        sendBroadcast(intent);    }

2.4.2 拦截广播意图

一共有三个广播意图

  1. USB 设备插入监听意图
  2. USB 把拔出监听意图
  3. 自己发送读取图片广播意图
  @Override    public void onReceive(Context context, Intent intent) {        SmartToastUtils.showShort(context,"onReceiver start");        mContext=context;        final String action = intent.getAction();        switch (action)        {            case UsbManager.ACTION_USB_DEVICE_ATTACHED://插上USB设备                SmartToastUtils.showShort(context,"USB设备已连接");                UsbDevice find_USB_Device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);                //设备不为空                if (find_USB_Device != null) {                    // 检查权限                     permissionRequest(mContext);                }else{                    SmartToastUtils.showShort(mContext,"findUsb is null");                }                break;            case UsbManager.ACTION_USB_DEVICE_DETACHED://断开USB设备                SmartToastUtils.showShort(context,"USB设备已断开");                try {                    UsbDevice removedDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);                    if(removedDevice!=null){                        UsbMassStorageDevice usbMassStorageDevice=getUsbMass(removedDevice);                        if(usbMassStorageDevice!=null){                            usbMassStorageDevice.close();                        }                    }                } catch (Exception e) {                    SmartToastUtils.showShort(mContext,"USB断开异常"+e.toString());                }                break;            case READ_USB_DEVICE_PERMISSION: //自定义读取USB 设备信息                SmartToastUtils.showShort(context,"USB已获取权限");                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);                // 检查U盘权限                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {                    if (usbDevice != null) {                        //读取USB 设备                        readDevice(getUsbMass(usbDevice));                    } else {                        SmartToastUtils.showShort(mContext, "没有插入U 盘");                    }                } else {                    SmartToastUtils.showShort(mContext, "未获取到 U盘权限");                    permissionRequest(mContext);                }                break;                default:                    break;        }    }

当USB 设备监听到USB设备已插入的时候,进行USB 设备的相关权限申请
申请成功后自动转到读取设备图片意图中

2.4.3 设备信息初始化

    try {            device.init();        } catch (IOException e) {            SmartToastUtils.showShort(mContext,"device.init() error"+e.toString());            return ;        }                // 设备分区        Partition partition = device.getPartitions().get(0);        // 文件系统        FileSystem currentFs = partition.getFileSystem();        // 获取 U 盘的根目录        UsbFile mRootFolder = currentFs.getRootDirectory();        // 获取 U 盘的容量        long capacity = currentFs.getCapacity();        // 获取 U 盘的剩余容量        long freeSpace = currentFs.getFreeSpace();        // 获取 U 盘的标识        String volumeLabel = currentFs.getVolumeLabel();

2.4.4 递归遍历USB 设备中的所有图片

 private void readAllPicFileFromUSBDevice(UsbFile usbFile,FileSystem fileSystem){        try {            UsbFile[] usbFileList=usbFile.listFiles();            for (UsbFile usbFileItem:usbFileList                    ) {                if(!usbFileItem.isDirectory()){                    String FileEnd = usbFileItem.getName().substring(usbFileItem.getName().lastIndexOf(".") + 1,                            usbFileItem.getName().length()).toLowerCase();                    if(FileEnd.equals("jpg") || FileEnd.equals("png") || FileEnd.equals("gif")                            || FileEnd.equals("jpeg")|| FileEnd.equals("bmp")){                        SmartToastUtils.showShort(mContext,"文件名称="+usbFileItem.getName()+"文件大小="+usbFileItem.getLength());                        FileUtils.saveToPhoneDevice(usbFileItem,fileSystem);                    }                }else{                    readAllPicFileFromUSBDevice(usbFileItem,fileSystem);                }            }        } catch (IOException e) {            SmartToastUtils.showShort(mContext,"遍历USB文件异常");        }    }

2.4.5 找到一个图片文件就把它保存到手机SDCard 中

所有读取到USB 设备上的图片会放到SDCard/usb_temp_foler 文件夹下

//SD Card 根目录下创建文件夹    private final static String USBTempFolder=Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"usb_temp_foler";    public final static void saveToPhoneDevice(UsbFile usbFile,FileSystem fileSystem){        if(usbFile.isDirectory()){            SmartLogUtils.showDebug(usbFile.getName()+"是一个文件夹",true);            return ;        }        Boolean sdCardCanUse=Environment.getExternalStorageState().equals(                Environment.MEDIA_MOUNTED);        if(sdCardCanUse){            SmartLogUtils.showDebug("SD Card 可用",true);        }else{            SmartLogUtils.showDebug("SD Card 不可用",true);        }        File file=new File(USBTempFolder);        //文件读写测试       if(file.canRead()){           SmartLogUtils.showError("文件可读",true);       }else{           SmartLogUtils.showError("文件不可读",true);       }        if(file.canWrite()){            SmartLogUtils.showError("文件可写",true);        }else{            SmartLogUtils.showError("文件不可写",true);        }        //文件夹不存在就创建        if(!file.exists()){            file.mkdir();            SmartLogUtils.showDebug("文件夹创建成功",true);        }else{            SmartLogUtils.showDebug("文件夹已存在",true);        }        //写入文件        FileOutputStream os=null;        InputStream is=null;        String newFileName=null;        try {            newFileName=USBTempFolder+File.separator+usbFile.getName();            SmartLogUtils.showError(newFileName,true);            os = new FileOutputStream(newFileName);            is= new UsbFileInputStream(usbFile);            int bytesRead = 0;            byte[] buffer = new byte[fileSystem.getChunkSize()];//作者的推荐写法是currentFs.getChunkSize()为buffer长度            while ((bytesRead = is.read(buffer)) != -1) {                os.write(buffer, 0, bytesRead);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        }catch (IOException e) {            e.printStackTrace();        }finally {            try {                if(os!=null){                    os.flush();                    os.close();                }                if(is!=null){                    is.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }

相关代码视频讲解和本教程中的代码可移步下载
https://github.com/geekxingyun/AndroidOtgUSBMtpSample

更多相关文章

  1. Android刷机Root相关学习总结
  2. 阿里云消息推送服务
  3. Android(安卓)camera---Camera2详解之一 API学习
  4. Android(安卓)- 支持不同的设备 - 支持不同的屏幕
  5. Android(安卓)蓝牙串口调试程序开发
  6. 通过Python 获取Android设备信息的轻量级框架
  7. 在android平板上取位置和天气的实现方式
  8. Unity在Android(安卓)6.0及以上版本弹出权限申请窗口的问题
  9. Android开发之旅(二)服务生命周期和广播接收者生命周期

随机推荐

  1. Android:防止过快点击造成多次事件
  2. Android实现三级联动下拉框 下拉列表spin
  3. Android多媒体相关框架
  4. 可以通过在AndroidManifest.xml中指定Act
  5. Android的线程使用来更新UI----Thread、H
  6. Android(安卓)unspecified' depends on o
  7. Android(安卓)动画
  8. 自定义Seekbar
  9. android 文件上传的类--完整 可以直接被
  10. android webview知识积累