Android App 与 U 盘通信详解

前言

对于 U 盘的了解,相信大多数人应该只停留在跟 U 盘跟电脑通信的阶段,其实现在通过 OTG 线就可以实现手机跟 U 盘之间的数据操作,不仅可以将 U 盘中的文件读取到手机中来,还能将手机中的文件导出到 U 盘中,从而实现手机与 U 盘之间的通信。本文将从 Android App 入手,通过相关的代码,带大家一步步了解手机与 U 盘之间的通信。代码我已经放上 Github 了,有需要的 点击这里

一、自定义广播接收器接收 U 盘相关的信息


在 U 盘插入或插出的时候,系统都会发出一条相关的广播,所以我们需要自定义广播接收器,接收这两条广播,然后进行相应的处理。

public class OtgReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        switch (action) {
            // 接收到 U 盘插入广播
            case UsbManager.ACTION_USB_DEVICE_ATTACHED:
                showToast(context, "U 盘已插入");
                 // 获取相关的 Usb 设备
                UsbDevice attachUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (attachUsbDevice != null) {
                     // 进行权限申请
                    permissionRequest();
                }
                break;
            // 接收到 U 盘拔出广播
            case UsbManager.ACTION_USB_DEVICE_DETACHED:
                showToast(context, "U 盘已拔出");
                break;
            default:
                break;
        }
    }
}

因为 Usb 相关的设备操作,需要申请相关的权限,所以在接收到 U 盘插入的广播之后,我们需要进行权限申请。

    private void permissionRequest() {
        // 设备管理器
        UsbManager usbManager = (UsbManager) MainActivity.getContext().get().getSystemService(Context.USB_SERVICE);
        // 获取 U 盘存储设备
        UsbMassStorageDevice[] storageDevices = UsbMassStorageDevice.getMassStorageDevices(OtgApplication.getContext());
        PendingIntent pendingIntent = PendingIntent.getBroadcast(OtgApplication.getContext(), 0, new Intent(ACTION_USB_PERMISSION), 0);
        // 进行权限申请
        usbManager.requestPermission(device.getUsbDevice(), pendingIntent);
    }

可以看到我们在申请权限的时候,传入了一个 PendingIntent,PendingIntent 里面传入我们自定义的广播 ACTION_USB_PERMISSION,等到权限申请完成,便会发出这条广播,然后我们可以在广播接收器中接收并处理,从而进行后续的操作。

   case ACTION_USB_PERMISSION:
       UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
       if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
           if (usbDevice != null) {
               // 读取 U 盘相关的信息
               readDevice(getUsbMass(usbDevice));
           } else {
               showToast(context, "没有插入 U 盘");
           }
       } else {
           showToast(context, "未获取到 U 盘权限");
       }
       break;

为了简化相关的代码,我导入 Github 上开源的 libaums,所以需要在 build.gradle 里面加上

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

通过接收我们自定义的广播,便可以从 Intent 里面获取相应的包含 U 盘信息的 UsbDevice

    private void readDevice(UsbMassStorageDevice device) {
            device.init(); 
            // 设备分区
            Partition partition = device.getPartitions().get(0);
            // 文件系统
            FileSystem currentFs = partition.getFileSystem();
            // 获取 U 盘的根目录
            mRootFolder = currentFs.getRootDirectory();
            // 获取 U 盘的容量
            long capacity = currentFs.getCapacity();
            // 获取 U 盘的剩余容量
            long freeSpace = currentFs.getFreeSpace();
            // 获取 U 盘的标识
            String volumeLabel = currentFs.getVolumeLabel();
    }

二、将文件导入到 U 盘中


通常我们将手机跟 U 盘通过 OTG 线进行连接,都是为了将手机里面的文件导入到 U 盘中,我们就以图片为例子,看看怎样将图片导出到 U 盘中。

将图片导出到 U 盘中,我们可以通过流来实现,先在 U 盘对应的目录,创建新的 jpg/png 格式的文件,然后通过 BitmapFactory 将图片转换成 Bitmap,再进一步拿到对应图片的 ByteArrayOutputStream,最后将对应的字节写入文件中。

    public static void savePictureToUsb(String picturePath, UsbFile root) {
        UsbFile newDir = root.createDirectory("Haoz" + System.currentTimeMillis());
        UsbFile file = newDir.createFile("Haoz" + System.currentTimeMillis() + ".jpg");
        Bitmap bitmap = BitmapFactory.decodeFile(picturePath);
        OutputStream outputStream = new UsbFileOutputStream(file);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
        outputStream.write(out.toByteArray());
        outputStream.flush();
        outputStream.close();
        file.flush();
        file.close();
    }

可以看到我们传入图片的路径以及 U 盘的根目录,便可以将图片写入到 U 盘中,在上一节中,我们已经通过广播拿到 U 盘的根目录,所以直接用就行了。将文件从 U 盘中导入到手机中,其实思路也是一样的。毕竟当 U 盘插入手机的那一刻,将 U 盘当成手机的一个普通的目录来处理就行了。

三、该注意的地方


虽然说,U 盘跟手机之间的通信相对来说不是很难,但其实也有很多需要注意的地方,也是笔者在开发过程中踩过的坑,这里都记录出来,供大家参考。

3.1 获取图片的路径

我们通过图片选择库或者照相机回调出来的,很多时候都是图片的 Uri,而要得到图片对应的 Bitmap 需要的是图片的真实路径,我们可以通过以下方法进行转换。

    public static String getPath(ContentResolver resolver, Uri uri) {
        if (uri == null) {
            return null;
        }

        if (SCHEME_CONTENT.equals(uri.getScheme())) {
            Cursor cursor = null;
            try {
                cursor = resolver.query(uri, new String[]{MediaStore.Images.ImageColumns.DATA},
                        null, null, null);
                if (cursor == null || !cursor.moveToFirst()) {
                    return null;
                }
                return cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA));
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
        return uri.getPath();
    }

3.2 文件相关的读写操作

文件相关的读写操作都是比较耗时的,特别是当文件比较大的时候。所以我们不能在主线程中进行文件的读写,必须将其放在子线程中,等 I/O 操作完成了,再转换到主线程中进行操作。

3.3 广播的注册与移除

因为我们是自定义广播接收器来接收相应的广播,所以需要在 Activity 中进行广播的动态注册,将对应 Action 进行过滤。最后不要忘记了在 onDestroy() 中移除广播,防止内存泄露。

    private void registerUDiskReceiver() {
        IntentFilter usbDeviceFileter = new IntentFilter();
        usbDeviceFileter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        usbDeviceFileter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        usbDeviceFileter.addAction(Intent.ACTION_MEDIA_MOUNTED);
        registerReceiver(mOtgReceiver, usbDeviceFileter);
        // 注册监听自定义广播
        IntentFilter filter = new IntentFilter(OtgReceiver.ACTION_USB_PERMISSION);
        registerReceiver(mOtgReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mOtgReceiver);
    }

猜你喜欢

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,818评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 面试题总结 通用 安卓学习途径, 寻找资料学习的博客网站 AndroidStudio使用, 插件使用 安卓和苹果的...
    JingBeibei阅读 1,658评论 2 21
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 99,215评论 9 467
  • 姓名:楼立秧 公司:慈溪光华实业有限公司 宁波盛和塾塾生,235期学员 知~学习 《六项精进》大纲 诵读2遍 总9...
    楼立秧阅读 118评论 0 0