最近朋友在做一个手机连接单反相机实现相册直播边拍边传功能。经查资料得知,
Android 应用获取外部设备文件一共有这样四种方式
内容提供器 (Content-Provider) —已测试,独占模式, 而且需要手动点击导入到手机系统相册中才能使用
USB 传输协议 ----------------已测试,不支持单反相机,仅支持单反相机取出来内存卡数据读取
MTP传输协议 (Media Transfer Protocol)-----已测试,独占模式,可导出真实图片和缩略图
PTP 传输协议 (Picture Transfer Protocol)Digital Camera 数码相机----有同行测试成功
更多讨论方案尝试详情请移步https://github.com/geekxingyun/AndroidOtgUSBMtpSample
首先,我们要读取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" />
为了简化USB 设备的读取,我们添加下这个类库
implementation 'com.github.mjdev:libaums:0.5.5'
我们需要让我们的程序知道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);
}
}
/***
* 请求读写权限
* ****/
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]);
}
}
}
//发送USB广播
private void sendUSBBroadcast() {
//发送广播
Intent intent=new Intent(READ_USB_DEVICE_PERMISSION);
//发送标准广播
sendBroadcast(intent);
}
一共有三个广播意图
@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 设备的相关权限申请
申请成功后自动转到读取设备图片意图中
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();
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文件异常");
}
}
所有读取到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