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

实战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

你可能感兴趣的:(#,Android)