草庐IT

android免root读写u盘最新方法,支持安卓Q+

MisterZhang666 2023-04-09 原文

android读写u盘支持安卓10以上的最新方法

本人是在写一个app需要读取u盘文件列表,网上找了好多方法,要不就是没有media权限,要不就是收不到广播,全部用不了,就这样搁置了一段时间终于又找到了一个大佬的方法,在这做个笔记

谷歌从Android5.0以上已经不支持开发者随便读写手机的外部存储(包含tf卡、otg外接u盘等),换句话说5.0以下我们还是可以直接读写usb设备的。

当你直接拿到u盘挂载路径,去操作的时候,File类已经读取不到任何东西,因为缺少了以下这个权限

 <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"/>

当然如果你是root用户,也可以直接修改系统配置文件,/system/etc/permissions/platform.xml文件中有两组配置,其中

    <permission name="android.permission.WRITE_MEDIA_STORAGE" >
        <group gid="media_rw" />
    </permission>

是u盘等外接媒体的权限,而

<permission name="android.permission.READ_EXTERNAL_STORAGE" />
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" />

这两组就是手机内置的外部存储,除了系统使用的内部存储,其他一切都是外部存储,所以读写u盘也需要用到他们,那要怎么改呢,从我了解下来,实际上大概样子就是如下

<permission name="android.permission.WRITE_EXTERNAL_STORAGE">
	<group gid="media_rw" />
	<group gid="sdcard_rw" />
</permission>

<permission name="android.permission.WRITE_MEDIA_STORAGE" >
    <group gid="media_rw" />
    <group gid="sdcard_rw" />
</permission>

当然没有root的用户看看就好了,接下来说说非root用户。

针对非root用户,有些比如ES文件浏览器,他们可以直接操作u盘,实际上是通过google的特定方法,SAF框架操作外置sd卡,也就是DocumentFile类,假如不用这个类,直接想操作,那只能向rom厂商申请系统签名,加入白名单,成为系统应用。

SAF框架,你只需要在AndroidManifest.xml加上,想了解可以去官网看看https://developer.android.com/guide/topics/providers/document-provider.html

<provider
    android:name="com.github.mjdev.libaums.storageprovider.UsbDocumentProvider"
    android:authorities="com.github.mjdev.libaums.storageprovider.documents"
    android:exported="true"
    android:grantUriPermissions="true"
    android:permission="android.permission.MANAGE_DOCUMENTS"
    android:enabled="@bool/isAtLeastKitKat">
    <intent-filter>
        <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
    </intent-filter>
</provider>

重点来了,废话不多说,以上的方法仅供了解,下面才是真正的开始,采用第三方框架 libaums

//https://github.com/magnusja/libaums
implementation 'me.jahnen.libaums:core:0.9.1'

具体实现如下:

/**
     * 动态注册监听USB设备的广播。
     * 由于耗电等原因,8.0不能对大部分的广播进行静态注册
     */
    private void registerUsbReceiver() {
        USBBroadCastReceiver usbBroadCastReceiver = new USBBroadCastReceiver(getActivity());

        usbBroadCastReceiver.setOnPermListener(new USBBroadCastReceiver.OnPermListener() {
            @Override
            public void onRemove() {
                updateUdiskView();
            }

            @Override
            public void onConnect() {
                updateUdiskView();
            }
        });

        IntentFilter filter = new IntentFilter(USBBroadCastReceiver.ACTION_USB_PERMISSION);
        filter.addAction("android.hardware.usb.action.USB_STATE");
        filter.addAction("android.hardware.usb.action.USB_DEVICE_ATTACHED");
        filter.addAction("android.hardware.usb.action.USB_DEVICE_DETACHED");
        getActivity().registerReceiver(usbBroadCastReceiver, filter);

        /**
         * 启动软件尝试读取u盘
         */
        usbBroadCastReceiver.redUDiskDeviceList();
    }
package com.zj.ruokeplayer.receive;

import static android.hardware.usb.UsbManager.ACTION_USB_DEVICE_ATTACHED;
import static android.hardware.usb.UsbManager.ACTION_USB_DEVICE_DETACHED;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.util.Log;

import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.ToastUtils;
import com.zj.jplayercore.controller.JPlayer;
import com.zj.ruokeplayer.model.GlobalConstant;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Locale;

import me.jahnen.libaums.core.UsbMassStorageDevice;
import me.jahnen.libaums.core.fs.FileSystem;
import me.jahnen.libaums.core.fs.UsbFile;
import me.jahnen.libaums.core.fs.UsbFileStreamFactory;
import me.jahnen.libaums.core.partition.Partition;

/**
 * usb设备处理
 */
public class USBBroadCastReceiver extends BroadcastReceiver {

    public static final String ACTION_USB_PERMISSION = "com.android.rkplayer.USB_PERMISSION";
    private UsbMassStorageDevice[] storageDevices;
    private final String TAG = "rkplayer_usb";

    private Context context;
    private OnPermListener onPermListener;

    public USBBroadCastReceiver(Context context) {
        this.context = context;
    }

    public void setOnPermListener(OnPermListener onPermListener) {
        this.onPermListener = onPermListener;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String intentAction = intent.getAction();
        UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
        switch (intentAction) {
            case ACTION_USB_PERMISSION:
                //自定义Action
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
//                    LogUtils.i(TAG, "用户同意USB设备访问权限");
                    ToastUtils.showShort("您已同意USB设备访问");
                    readDevice(getUsbMass(device));
                } else {
//                    LogUtils.i(TAG, "用户拒绝USB设备访问权限");
                    ToastUtils.showShort("您已拒绝USB设备访问权限");
                    GlobalConstant.usbInited = false;
                    onPermListener.onRemove();
                }
                break;
            case ACTION_USB_DEVICE_ATTACHED:
//                LogUtils.i(TAG, "U盘设备插入");
                ToastUtils.showShort("U盘设备插入");
                if (device != null) {
                    redUDiskDeviceList();
                }
                break;
            case ACTION_USB_DEVICE_DETACHED:
                ToastUtils.showShort("U盘设备移除");
                GlobalConstant.usbInited = false;
                onPermListener.onRemove();
//                LogUtils.i(TAG, "U盘设备移除");
                break;
            default:
                //。。。
        }
    }

    /**
     * 获取存储设备
     * @param usbDevice 与android设备连接在一起的USB设备
     * @return Usb设备的一个包装类
     */
    private UsbMassStorageDevice getUsbMass(UsbDevice usbDevice) {
        for (UsbMassStorageDevice device : storageDevices) {
            if (usbDevice.equals(device.getUsbDevice())) {
                return device;
            }
        }
        return null;
    }

    /**
     * USB设备读取
     */
    public void redUDiskDeviceList() {
        //设备管理器
        UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        //获取U盘存储设备
        storageDevices = UsbMassStorageDevice.getMassStorageDevices(context);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
        //一般手机只有1个OTG插口
        if (storageDevices.length <= 0) {
            return;
        }
        UsbMassStorageDevice device = storageDevices[0];
        //读取设备是否有权限
        if (usbManager.hasPermission(device.getUsbDevice())) {
            readDevice(device);
        } else {
            //没有权限,进行申请
            usbManager.requestPermission(device.getUsbDevice(), pendingIntent);
        }
    }

    /**
     * 初始化USB设备
     * @param device USB设备
     */
    private void readDevice(UsbMassStorageDevice device) {
        try {
            device.init();//初始化
            //设备分区
            Partition partition = device.getPartitions().get(0);
            //文件系统
            FileSystem currentFs = partition.getFileSystem();
            //可以获取到设备的标识
            currentFs.getVolumeLabel();
            //通过FileSystem可以获取当前U盘的一些存储信息,包括剩余空间大小,容量等等
            LogUtils.e("Volume Label: ", currentFs.getVolumeLabel());
            LogUtils.e("Capacity: ", fSize(currentFs.getCapacity()));
            LogUtils.e("Occupied Space: ", fSize(currentFs.getOccupiedSpace()));
            LogUtils.e("Free Space: ", fSize(currentFs.getFreeSpace()));
            LogUtils.e("Chunk size: ", fSize(currentFs.getChunkSize()));

            GlobalConstant.usbInited = true;
            readAndWrite(currentFs);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 格式化
     * @param sizeInByte
     * @return
     */
    public static String fSize(long sizeInByte) {
        if (sizeInByte < 1024) {
            return String.format("%s", sizeInByte);
        } else if (sizeInByte < 1024 * 1024) {
            return String.format(Locale.CANADA, "%.2fKB", sizeInByte / 1024.);
        } else if (sizeInByte < 1024 * 1024 * 1024) {
            return String.format(Locale.CANADA, "%.2fMB", sizeInByte / 1024. / 1024);
        } else {
            return String.format(Locale.CANADA, "%.2fGB", sizeInByte / 1024. / 1024 / 1024);
        }
    }

    /**
     * 读写文件
     *
     * @param currentFs
     */
    public void readAndWrite(FileSystem currentFs) {
        onPermListener.onConnect();
        try {
            //设置当前文件对象为根目录
            UsbFile usbFile = currentFs.getRootDirectory();
            UsbFile[] files = usbFile.listFiles();
            for (UsbFile file : files) {
                LogUtils.i(TAG,  file.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 回调
     * @param currentFs
     */
    public interface OnPermListener {
        public void onRemove();
        public void onConnect();
    }
}

应用中会唤起读取u盘的权限,基本就到这里结束了

有关android免root读写u盘最新方法,支持安卓Q+的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  5. Ruby 方法() 方法 - 2

    我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

  6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  7. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  8. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  9. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  10. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

随机推荐