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);
 }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • Android应用程序四大组件之使用AIDL如何实现跨进程调用Service
  • 使用Android studio创建的AIDL编译时找不到自定义类的解决办法
  • Android 使用【AIDL】调用外部服务的解决方法
  • 基于Android AIDL进程间通信接口使用介绍
  • Android程序设计之AIDL实例详解
  • 实例讲解Android中的AIDL内部进程通信接口使用
  • Android使用AIDL实现两个App间通信
(0)

相关推荐

  • Android使用AIDL实现两个App间通信

    今天开发了一个功能,通过Android的AIDL机制完成两个app间的通信.功能需求很简单,一个客户端app,叫做client,一个服务端app叫orderManager:客户端负责展示订单列表,并且可以向服务端添加订单:服务端负责管理订单,可以对外提供订单信息. 闲言少叙,下面直接上代码. 1.编写bean类. 首先在client中定义OrderBean类,字段很简单,注意需要实现Parcelable接口: package com.example.wang.client.bean; impor

  • 基于Android AIDL进程间通信接口使用介绍

    AIDL:Android Interface Definition Language,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口. ICP:Interprocess Communication ,内部进程通信. 使用: 1.先创建一个aidl文件,aidl文件的定义和java代码类似,但是!它可以引用其它aidl文件中定义的接口和类,但是不能引用自定义的java类文件中定义的接口和类,要引用自定义的接口或类,需要为此类也定义一个对应的aidl文件,并且此

  • Android应用程序四大组件之使用AIDL如何实现跨进程调用Service

    一.问题描述 Android应用程序的四大组件中Activity.BroadcastReceiver.ContentProvider.Service都可以进行跨进程.在上一篇我们通过ContentProvider实现了不同应用之间的跨进程调用,但ContentProvider主要是提供数据的共享(如sqlite数据库),那么我们希望跨进程调用服务(Service)呢?Android系统采用了远程过程调用(RPC)方式来实现.与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言

  • 实例讲解Android中的AIDL内部进程通信接口使用

    首先描述下我们想要实现的内容,我们希望在一个应用中通过点击按钮,去操作另一个进程中应用的音乐播放功能. 如图,我们点击"播放"时,系统就会去远程调用我们提供的一个service(与当前service不是同一个应用哦),然后操作service中的音乐播放,点击"停止"则会终止播放.想要重新播放的话,必须先点"销毁service",再点播放按钮哦.(至于这里为什么要先点销毁按钮才能播放,完全是为了给大家展示下,远程调用service时,怎么去解绑se

  • Android 使用【AIDL】调用外部服务的解决方法

    在Android 中有一种服务说是服务其实倒不如说是一个接口,这个接口名为:Android Interface Definition Language ,这个接口可提供跨进程访问服务,英文缩写为:AIDL. 此种服务的好处在于,多个应用程序之间建立共同的服务机制,通过AIDL在不同应用程序之间达到数据的共享和数据相互操作,下面将通过一个DEMO 演示AIDL 是如何为应用程序之间提供服务的.本文大纲为:•1.创建AIDL 服务端.•2.创建AIDL 客户端.•3.客户端调用服务端提供的服务接口.

  • Android程序设计之AIDL实例详解

    通常来说,AIDL这项技术在我们的应用开发过程中并不是很常用,虽然新浪微博提供了SSO登录,但是其原理就是使用AIDL.本文就以完整的实例形式讲述了AIDL的原理及实现方法. AIDL(AndRoid接口描述语言)是一种借口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成 AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数,

  • 使用Android studio创建的AIDL编译时找不到自定义类的解决办法

    使用AS创建ADIL文件时AS会在main文件夹下给我们生成一个aidl文件夹和一个相同包名的包,通常我们会把所有和ADIL相关的类或文件放在这个包下,但是如果存在自定义的类时,程序编译时无法通过,提示找不到自定义的包.解决办法如下,在启动Module的build.gradle中加入如下代码: sourceSets { main { manifest.srcFile 'src/main/AndroidManifest.xml' java.srcDirs = ['src/main/java', '

  • Android App 与 U 盘通信示例详解

    前言 对于 U 盘的了解,相信大多数人应该只停留在跟 U 盘跟电脑通信的阶段,其实现在通过OTG 线就可以实现手机跟 U 盘之间的数据操作,不仅可以将 U 盘中的文件读取到手机中来,还能将手机中的文件导出到 U 盘中,从而实现手机与 U 盘之间的通信.本文将从 Android App 入手,通过相关的代码,带大家一步步了解手机与 U 盘之间的通信.代码我已经放上 Github 了,有需要的点击这里 . 一.自定义广播接收器接收 U 盘相关的信息 在 U 盘插入或插出的时候,系统都会发出一条相关的

  • Android画图实现MPAndroidchart折线图示例详解

    目录 效果图 依赖 activity.xml MainActivity MyMarkerView 自定义class maekertextview .xml 常用属性 效果图 用的是3.1.0的依赖 依赖 allprojects { repositories { jcenter() maven { url "https://jitpack.io" } } } //依赖 dependencies{ implementation 'com.github.PhilJay:MPAndroidCh

  • Android车载多媒体开发MediaSession框架示例详解

    目录 一.多媒体应用架构 1.1 音视频传统应用架构 1.2 MediaSession 框架 媒体会话 媒体控制器 二.MediaSession 2.1 概述 2.2 MediaBrowser 2.2.1 MediaBrowser.ConnectionCallback 2.2.2 MediaBrowser.ItemCallback 2.2.3 MediaBrowser.MediaItem 2.2.4 MediaBrowser.SubscriptionCallback 2.3 MediaContr

  • Android 应用程序的启动流程示例详解

    目录 应用进程的启动流程 1.ActivityStackSupervisor.startSpecificActivity 2.ATMS.startProcessAsync 3.LocalService.startProcess 4.startProcessLocked函数 5.ProcessList.startProcessLocked 6.ProcessList.startProcessLocked重载 7.ProcessList.startProcess 8.ZygoteState.star

  • Android Flutter实现3D动画效果示例详解

    目录 前言 AnimatedWidget 简介 3D 旋转动画的实现 总结 前言 上一篇我们介绍了 Animation 和 AnimationController 的使用,这是最基本的动画构建类.但是,如果我们想构建一个可复用的动画组件,通过外部参数来控制其动画效果的时候,上一篇的方法就不太合适了.在 Flutter 中提供了 AnimatedWidget 组件用于构建可复用的动画组件.本篇我们用 AnimatedWidget 来实现组件的3D 旋转效果,如下图所示. AnimatedWidge

  • Android开发Kotlin实现圆弧计步器示例详解

    目录 效果图 定义控件的样式 自定义StepView 绘制文本坐标 Android获取中线到基线距离 效果图 定义控件的样式 看完效果后,我们先定义控件的样式 <!-- 自定义View的名字 StepView --> <!-- name 属性名称 format 格式 string 文字 color 颜色 dimension 字体大小 integer 数字 reference 资源或者颜色 --> <declare-styleable name="StepView&q

  • Android 动态加载 so实现示例详解

    目录 背景 so动态加载介绍 从一个例子出发 so库检索与删除 动态加载so 结束了吗? ELF文件 扩展 总结 背景 对于一个普通的android应用来说,so库的占比通常都是巨高不下的,因为我们无可避免的在开发中遇到各种各样需要用到native的需求,所以so库的动态化可以减少极大的包体积,自从2020腾讯的bugly团队发部关于动态化so的相关文章后,已经过去两年了,相关文章,经过两年的考验,实际上so动态加载也是非常成熟的一项技术了. 但是很遗憾,许多公司都还没有这方面的涉略又或者说不知

  • android教程之service使用方法示例详解

    Service的生命周期 (适用于2.1及以上) 1. 被startService的无论是否有任何活动绑定到该Service,都在后台运行.onCreate(若需要) -> onStart(int id, Bundle args).  多次startService,则onStart调用多次,但不会创建多个Service实例,只需要一次stop.该Service一直后台运行,直到stopService或者自己的stopSelf()或者资源不足由平台结束. 2. 被bindService的调用bin

  • 如何利用Android Studio将moudle变成jar示例详解

    前言 本文主要给大家介绍的是关于利用Android Studio将moudle变成jar的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 方法如下: 1.在moudle中的build.gradle文件中添加如下代码:(和android.dependencies标签同级) task makeJar(type: Copy) { delete 'build/libs/test.jar' from('build/intermediates/bundles/release/')

  • Android布局加载之LayoutInflater示例详解

    前言 Activity 在界面创建时需要将 XML 布局文件中的内容加载进来,正如我们在 ListView 或者 RecyclerView 中需要将 Item 的布局加载进来一样,都是使用 LayoutInflater 来进行操作的. LayoutInflater 实例的获取有多种方式,但最终是通过(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)来得到的,也就是说加载布局的 LayoutInflat

随机推荐