Android App 與 U 盤通信詳解

前言

對(duì)于 U 盤的了解,相信大多數(shù)人應(yīng)該只停留在跟 U 盤跟電腦通信的階段,其實(shí)現(xiàn)在通過(guò) OTG 線就可以實(shí)現(xiàn)手機(jī)跟 U 盤之間的數(shù)據(jù)操作,不僅可以將 U 盤中的文件讀取到手機(jī)中來(lái),還能將手機(jī)中的文件導(dǎo)出到 U 盤中,從而實(shí)現(xiàn)手機(jī)與 U 盤之間的通信。本文將從 Android App 入手,通過(guò)相關(guān)的代碼,帶大家一步步了解手機(jī)與 U 盤之間的通信。代碼我已經(jīng)放上 Github 了,有需要的 點(diǎn)擊這里

一、自定義廣播接收器接收 U 盤相關(guān)的信息


在 U 盤插入或插出的時(shí)候,系統(tǒng)都會(huì)發(fā)出一條相關(guān)的廣播,所以我們需要自定義廣播接收器,接收這兩條廣播,然后進(jìn)行相應(yīng)的處理。

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 盤已插入");
                 // 獲取相關(guān)的 Usb 設(shè)備
                UsbDevice attachUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (attachUsbDevice != null) {
                     // 進(jìn)行權(quán)限申請(qǐng)
                    permissionRequest();
                }
                break;
            // 接收到 U 盤拔出廣播
            case UsbManager.ACTION_USB_DEVICE_DETACHED:
                showToast(context, "U 盤已拔出");
                break;
            default:
                break;
        }
    }
}

因?yàn)?Usb 相關(guān)的設(shè)備操作,需要申請(qǐng)相關(guān)的權(quán)限,所以在接收到 U 盤插入的廣播之后,我們需要進(jìn)行權(quán)限申請(qǐng)。

    private void permissionRequest() {
        // 設(shè)備管理器
        UsbManager usbManager = (UsbManager) MainActivity.getContext().get().getSystemService(Context.USB_SERVICE);
        // 獲取 U 盤存儲(chǔ)設(shè)備
        UsbMassStorageDevice[] storageDevices = UsbMassStorageDevice.getMassStorageDevices(OtgApplication.getContext());
        PendingIntent pendingIntent = PendingIntent.getBroadcast(OtgApplication.getContext(), 0, new Intent(ACTION_USB_PERMISSION), 0);
        // 進(jìn)行權(quán)限申請(qǐng)
        usbManager.requestPermission(device.getUsbDevice(), pendingIntent);
    }

可以看到我們?cè)谏暾?qǐng)權(quán)限的時(shí)候,傳入了一個(gè) PendingIntent,PendingIntent 里面?zhèn)魅胛覀冏远x的廣播 ACTION_USB_PERMISSION,等到權(quán)限申請(qǐng)完成,便會(huì)發(fā)出這條廣播,然后我們可以在廣播接收器中接收并處理,從而進(jìn)行后續(xù)的操作。

   case ACTION_USB_PERMISSION:
       UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
       if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
           if (usbDevice != null) {
               // 讀取 U 盤相關(guān)的信息
               readDevice(getUsbMass(usbDevice));
           } else {
               showToast(context, "沒(méi)有插入 U 盤");
           }
       } else {
           showToast(context, "未獲取到 U 盤權(quán)限");
       }
       break;

為了簡(jiǎn)化相關(guān)的代碼,我導(dǎo)入 Github 上開(kāi)源的 libaums,所以需要在 build.gradle 里面加上

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

通過(guò)接收我們自定義的廣播,便可以從 Intent 里面獲取相應(yīng)的包含 U 盤信息的 UsbDevice

    private void readDevice(UsbMassStorageDevice device) {
            device.init(); 
            // 設(shè)備分區(qū)
            Partition partition = device.getPartitions().get(0);
            // 文件系統(tǒng)
            FileSystem currentFs = partition.getFileSystem();
            // 獲取 U 盤的根目錄
            mRootFolder = currentFs.getRootDirectory();
            // 獲取 U 盤的容量
            long capacity = currentFs.getCapacity();
            // 獲取 U 盤的剩余容量
            long freeSpace = currentFs.getFreeSpace();
            // 獲取 U 盤的標(biāo)識(shí)
            String volumeLabel = currentFs.getVolumeLabel();
    }

二、將文件導(dǎo)入到 U 盤中


通常我們將手機(jī)跟 U 盤通過(guò) OTG 線進(jìn)行連接,都是為了將手機(jī)里面的文件導(dǎo)入到 U 盤中,我們就以圖片為例子,看看怎樣將圖片導(dǎo)出到 U 盤中。

將圖片導(dǎo)出到 U 盤中,我們可以通過(guò)流來(lái)實(shí)現(xiàn),先在 U 盤對(duì)應(yīng)的目錄,創(chuàng)建新的 jpg/png 格式的文件,然后通過(guò) BitmapFactory 將圖片轉(zhuǎn)換成 Bitmap,再進(jìn)一步拿到對(duì)應(yīng)圖片的 ByteArrayOutputStream,最后將對(duì)應(yīng)的字節(jié)寫(xiě)入文件中。

    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 盤的根目錄,便可以將圖片寫(xiě)入到 U 盤中,在上一節(jié)中,我們已經(jīng)通過(guò)廣播拿到 U 盤的根目錄,所以直接用就行了。將文件從 U 盤中導(dǎo)入到手機(jī)中,其實(shí)思路也是一樣的。畢竟當(dāng) U 盤插入手機(jī)的那一刻,將 U 盤當(dāng)成手機(jī)的一個(gè)普通的目錄來(lái)處理就行了。

三、該注意的地方


雖然說(shuō),U 盤跟手機(jī)之間的通信相對(duì)來(lái)說(shuō)不是很難,但其實(shí)也有很多需要注意的地方,也是筆者在開(kāi)發(fā)過(guò)程中踩過(guò)的坑,這里都記錄出來(lái),供大家參考。

3.1 獲取圖片的路徑

我們通過(guò)圖片選擇庫(kù)或者照相機(jī)回調(diào)出來(lái)的,很多時(shí)候都是圖片的 Uri,而要得到圖片對(duì)應(yīng)的 Bitmap 需要的是圖片的真實(shí)路徑,我們可以通過(guò)以下方法進(jìn)行轉(zhuǎn)換。

    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 文件相關(guān)的讀寫(xiě)操作

文件相關(guān)的讀寫(xiě)操作都是比較耗時(shí)的,特別是當(dāng)文件比較大的時(shí)候。所以我們不能在主線程中進(jìn)行文件的讀寫(xiě),必須將其放在子線程中,等 I/O 操作完成了,再轉(zhuǎn)換到主線程中進(jìn)行操作。

3.3 廣播的注冊(cè)與移除

因?yàn)槲覀兪亲远x廣播接收器來(lái)接收相應(yīng)的廣播,所以需要在 Activity 中進(jìn)行廣播的動(dòng)態(tài)注冊(cè),將對(duì)應(yīng) Action 進(jìn)行過(guò)濾。最后不要忘記了在 onDestroy() 中移除廣播,防止內(nèi)存泄露。

    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);
        // 注冊(cè)監(jiān)聽(tīng)自定義廣播
        IntentFilter filter = new IntentFilter(OtgReceiver.ACTION_USB_PERMISSION);
        registerReceiver(mOtgReceiver, filter);
    }

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

猜你喜歡

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,819評(píng)論 25 708
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,837評(píng)論 18 139
  • 面試題總結(jié) 通用 安卓學(xué)習(xí)途徑, 尋找資料學(xué)習(xí)的博客網(wǎng)站 AndroidStudio使用, 插件使用 安卓和蘋(píng)果的...
    JingBeibei閱讀 1,713評(píng)論 2 21
  • Ubuntu的發(fā)音 Ubuntu,源于非洲祖魯人和科薩人的語(yǔ)言,發(fā)作 oo-boon-too 的音。了解發(fā)音是有意...
    螢火蟲(chóng)de夢(mèng)閱讀 99,466評(píng)論 9 467
  • 姓名:樓立秧 公司:慈溪光華實(shí)業(yè)有限公司 寧波盛和塾塾生,235期學(xué)員 知~學(xué)習(xí) 《六項(xiàng)精進(jìn)》大綱 誦讀2遍 總9...
    樓立秧閱讀 120評(píng)論 0 0