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é)寫入文件中。
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 盤中,在上一節(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)的讀寫操作
文件相關(guān)的讀寫操作都是比較耗時(shí)的,特別是當(dāng)文件比較大的時(shí)候。所以我們不能在主線程中進(jìn)行文件的讀寫,必須將其放在子線程中,等 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); }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android HttpURLConnection下載網(wǎng)絡(luò)圖片設(shè)置系統(tǒng)壁紙
這篇文章主要為大家詳細(xì)介紹了Android HttpURLConnection下載網(wǎng)絡(luò)圖片設(shè)置系統(tǒng)壁紙,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10詳解Android使用CoordinatorLayout+AppBarLayout實(shí)現(xiàn)拉伸頂部圖片功能
這篇文章主要介紹了Android使用CoordinatorLayout+AppBarLayout實(shí)現(xiàn)拉伸頂部圖片功能,本文實(shí)例文字相結(jié)合給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10Kotlin Extension Function擴(kuò)展函數(shù)詳細(xì)介紹
Kotlin支持使用新功能擴(kuò)展類的能力,而無(wú)需通過(guò)類實(shí)現(xiàn)繼承概念或使用設(shè)計(jì)模式,如裝飾器(Decorator)。這是通過(guò)稱為擴(kuò)展功能(Extension Function)的特殊方式來(lái)完成的。因此,此功能可以有效地使代碼變得更清晰和易于閱讀,并且還可以減少代碼2023-02-02Android UTF-8轉(zhuǎn)碼實(shí)例詳解
這篇文章主要介紹了Android UTF-8轉(zhuǎn)碼實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03解決Android V7后自定義Toolbar、ActionBar左側(cè)有空白問(wèn)題
這篇文章主要介紹的Android V7后自定義Toolbar、ActionBar左側(cè)有空白問(wèn)題的解決方法,需要的朋友可以參考下2017-04-04Android實(shí)現(xiàn)手勢(shì)劃定區(qū)域裁剪圖片
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)手勢(shì)劃定區(qū)域裁剪圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05解析android創(chuàng)建快捷方式會(huì)啟動(dòng)兩個(gè)應(yīng)用的問(wèn)題
本篇文章是對(duì)關(guān)于android創(chuàng)建快捷方式會(huì)啟動(dòng)兩個(gè)應(yīng)用的問(wèn)題進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06Android中常用的三個(gè)Dialog彈窗總結(jié)解析
自己雖然一直使用過(guò)dialog,但是一直都是復(fù)制、粘貼;不清楚dialog的具體用途,這次趁著有時(shí)間,總結(jié)一下具體用法,感興趣的朋友跟著小編來(lái)看看吧2021-10-10