Android-USB-OTG-讀寫U盤文件

參考:
https://developer.android.com/guide/topics/connectivity/usb/host.html
https://blog.csdn.net/qq_29924041/article/details/80141514

本文介紹Android手機(jī)通過(guò)OTG數(shù)據(jù)線讀寫USB存儲(chǔ)設(shè)備(U盤,移動(dòng)硬盤,存儲(chǔ)卡)的兩種方法

方法一: 直接和USB設(shè)備建立連接骇扇,借助第三方庫(kù)libaums識(shí)別U盤的文件系統(tǒng)

由于libaums只支持FAT32文件系統(tǒng),所以U盤的格式化必須采用FAT32!
該庫(kù)的GitHub地址: https://github.com/magnusja/libaums

1.權(quán)限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 
// 手機(jī)必須支持USB主機(jī)特性(OTG)
<uses-feature android:name="android.hardware.usb.host" />

2.監(jiān)聽(tīng)USB插入/拔出

private static final String ACTION_USB_PERMISSION = "com.demo.otgusb.USB_PERMISSION";
private UsbManager mUsbManager;
private PendingIntent mPermissionIntent;    
private BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive: " + intent);
        String action = intent.getAction();
        if (action == null)
            return;
        switch (action) {
            case ACTION_USB_PERMISSION://用戶授權(quán)廣播
                synchronized (this) {
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { //允許權(quán)限申請(qǐng)
                        test();
                    } else {
                        logShow("用戶未授權(quán),訪問(wèn)USB設(shè)備失敗");
                    }
                }
                break;
            case UsbManager.ACTION_USB_DEVICE_ATTACHED://USB設(shè)備插入廣播
                logShow("USB設(shè)備插入");
                break;
            case UsbManager.ACTION_USB_DEVICE_DETACHED://USB設(shè)備拔出廣播
                logShow("USB設(shè)備拔出");
                break;
        }
    }
};

private void init() {   
    //USB管理器
    mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
    mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
    
    //注冊(cè)廣播,監(jiān)聽(tīng)USB插入和拔出
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
    intentFilter.addAction(ACTION_USB_PERMISSION);
    registerReceiver(mUsbReceiver, intentFilter);

    //讀寫權(quán)限
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE}, 111);
    }
}

3.使用libaums庫(kù)讀寫U盤文件

private void test() {
    try {
        UsbMassStorageDevice[] storageDevices = UsbMassStorageDevice.getMassStorageDevices(this);
        for (UsbMassStorageDevice storageDevice : storageDevices) { //一般手機(jī)只有一個(gè)USB設(shè)備
            // 申請(qǐng)USB權(quán)限
            if (!mUsbManager.hasPermission(storageDevice.getUsbDevice())) {
                mUsbManager.requestPermission(storageDevice.getUsbDevice(), mPermissionIntent);
                break;
            }
            // 初始化
            storageDevice.init();
            // 獲取分區(qū)
            List<Partition> partitions = storageDevice.getPartitions();
            if (partitions.size() == 0) {
                logShow("錯(cuò)誤: 讀取分區(qū)失敗");
                return;
            }
            // 僅使用第一分區(qū)
            FileSystem fileSystem = partitions.get(0).getFileSystem();
            logShow("Volume Label: " + fileSystem.getVolumeLabel());
            logShow("Capacity: " + fSize(fileSystem.getCapacity()));
            logShow("Occupied Space: " + fSize(fileSystem.getOccupiedSpace()));
            logShow("Free Space: " + fSize(fileSystem.getFreeSpace()));
            logShow("Chunk size: " + fSize(fileSystem.getChunkSize()));

            UsbFile root = fileSystem.getRootDirectory();
            UsbFile[] files = root.listFiles();
            for (UsbFile file : files)
                logShow("文件: " + file.getName());

            // 新建文件
            UsbFile newFile = root.createFile("hello_" + System.currentTimeMillis() + ".txt");
            logShow("新建文件: " + newFile.getName());

            // 寫文件
            // OutputStream os = new UsbFileOutputStream(newFile);
            OutputStream os = UsbFileStreamFactory.createBufferedOutputStream(newFile, fileSystem);
            os.write(("hi_" + System.currentTimeMillis()).getBytes());
            os.close();
            logShow("寫文件: " + newFile.getName());

            // 讀文件
            // InputStream is = new UsbFileInputStream(newFile);
            InputStream is = UsbFileStreamFactory.createBufferedInputStream(newFile, fileSystem);
            byte[] buffer = new byte[fileSystem.getChunkSize()];
            int len;
            File sdFile = new File("/sdcard/111");
            sdFile.mkdirs();
            FileOutputStream sdOut = new FileOutputStream(sdFile.getAbsolutePath() + "/" + newFile.getName());
            while ((len = is.read(buffer)) != -1) {
                sdOut.write(buffer, 0, len);
            }
            is.close();
            sdOut.close();
            logShow("讀文件: " + newFile.getName() + " ->復(fù)制到/sdcard/111/");

            storageDevice.close();
        }
    } catch (Exception e) {
        logShow("錯(cuò)誤: " + e);
    }
}

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

方法二: 獲取U盤的掛載路徑,直接讀寫U盤(就像掛載sdcard讀寫文件)

對(duì)于U盤的文件系統(tǒng)律胀,只依賴于手機(jī)系統(tǒng)是否支持伯襟,無(wú)需我們做額外工作(所有Android手機(jī)都支持FAT32,有個(gè)別手機(jī)還支持NTFS)
但是有些手機(jī)無(wú)法獲取掛載路徑(如小米等蛔外,就算通過(guò)mount命令找到掛載路徑也沒(méi)有權(quán)限讀寫)蛆楞,所以該方法通用性其實(shí)不如方法一!

1.通過(guò)MEDIA廣播獲取掛載路徑

// 注冊(cè)系統(tǒng)廣播
<receiver android:name=".MediaReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_CHECKING" />
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <action android:name="android.intent.action.MEDIA_EJECT" />
        <action android:name="android.intent.action.MEDIA_UNMOUNTED" />

        <data android:scheme="file" />
    </intent-filter>
</receiver>

// 獲取USB掛載路徑
public class MediaReceiver extends BroadcastReceiver {      
    @Override
    public void onReceive(Context context, Intent intent) {
        switch (intent.getAction()) {
            case Intent.ACTION_MEDIA_CHECKING:
                break;
            case Intent.ACTION_MEDIA_MOUNTED:
                // 獲取掛載路徑, 讀取U盤文件
                Uri uri = intent.getData();
                if (uri != null) {
                    String filePath = uri.getPath();
                    File rootFile = new File(filePath);
                    for (File file : rootFile.listFiles()) {
                        // 文件列表...
                    }
                }
                break;
            case Intent.ACTION_MEDIA_EJECT:
                break;
            case Intent.ACTION_MEDIA_UNMOUNTED:
                break;
        }
    }
}

2.通過(guò)反射系統(tǒng)方法獲取掛載路徑

public static List<String> getUsbPaths(Context cxt) {
    List<String> usbPaths = new ArrayList<>();
    try {
        StorageManager srgMgr = (StorageManager) cxt.getSystemService(Context.STORAGE_SERVICE);
        Class<StorageManager> srgMgrClass = StorageManager.class;
        String[] paths = (String[]) srgMgrClass.getMethod("getVolumePaths").invoke(srgMgr);
        for (String path : paths) {
            Object volumeState = srgMgrClass.getMethod("getVolumeState", String.class).invoke(srgMgr, path);
            if (!path.contains("emulated") && Environment.MEDIA_MOUNTED.equals(volumeState))
                usbPaths.add(path);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return usbPaths;
}

簡(jiǎn)書(shū): http://www.reibang.com/p/a32e376ea70e
CSDN: https://blog.csdn.net/qq_32115439/article/details/80918046
GitHub博客: http://lioil.win/2018/07/04/Android-USB-OTG.html
Coding博客: http://c.lioil.win/2018/07/04/Android-USB-OTG.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末夹厌,一起剝皮案震驚了整個(gè)濱河市豹爹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌矛纹,老刑警劉巖臂聋,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異或南,居然都是意外死亡孩等,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門采够,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)肄方,“玉大人,你說(shuō)我怎么就攤上這事蹬癌∪ㄋ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵逝薪,是天一觀的道長(zhǎng)隅要。 經(jīng)常有香客問(wèn)我,道長(zhǎng)董济,這世上最難降的妖魔是什么拾徙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮感局,結(jié)果婚禮上尼啡,老公的妹妹穿的比我還像新娘。我一直安慰自己询微,他們只是感情好崖瞭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著撑毛,像睡著了一般书聚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上藻雌,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天雌续,我揣著相機(jī)與錄音,去河邊找鬼胯杭。 笑死驯杜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的做个。 我是一名探鬼主播鸽心,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼滚局,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了顽频?” 一聲冷哼從身側(cè)響起藤肢,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎糯景,沒(méi)想到半個(gè)月后嘁圈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蟀淮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年丑孩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灭贷。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖略贮,靈堂內(nèi)的尸體忽然破棺而出甚疟,到底是詐尸還是另有隱情,我是刑警寧澤逃延,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布览妖,位于F島的核電站,受9級(jí)特大地震影響揽祥,放射性物質(zhì)發(fā)生泄漏讽膏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一拄丰、第九天 我趴在偏房一處隱蔽的房頂上張望府树。 院中可真熱鬧,春花似錦料按、人聲如沸奄侠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)垄潮。三九已至,卻和暖如春闷盔,著一層夾襖步出監(jiān)牢的瞬間弯洗,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工逢勾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牡整,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓溺拱,卻偏偏與公主長(zhǎng)得像果正,于是被迫代替她去往敵國(guó)和親炎码。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,506評(píng)論 25 707
  • afinalAfinal是一個(gè)android的ioc秋泳,orm框架 https://github.com/yangf...
    wgl0419閱讀 6,263評(píng)論 1 9
  • afinalAfinal是一個(gè)android的ioc潦闲,orm框架 https://github.com/yangf...
    passiontim閱讀 15,401評(píng)論 2 45
  • 每個(gè)人都有記憶中的味道,但同時(shí)所有記憶中的味道又都是不一樣的迫皱。 我叫倩倩歉闰,出生于陜西省、銅川市的礦醫(yī)院...
    回不去的曾經(jīng)2閱讀 213評(píng)論 0 1
  • 留心處處皆學(xué)問(wèn) 人情練達(dá)即文章
    天賦文案閱讀 164評(píng)論 0 0