เมื่ออุปกรณ์ที่ใช้ระบบ Android อยู่ในโหมดโฮสต์ USB อุปกรณ์จะทำงานเป็นโฮสต์ USB จ่ายไฟให้รถบัส และแจกแจงอุปกรณ์ USB ที่เชื่อมต่ออยู่ ระบบรองรับโหมดโฮสต์ USB ใน Android 3.1 ขึ้นไป
ภาพรวมของ API
ก่อนที่จะเริ่มต้น คุณควรเข้าใจชั้นเรียนที่คุณต้องการทำงานด้วย
ตารางต่อไปนี้อธิบาย API ของโฮสต์ USB ในแพ็กเกจ android.hardware.usb
ชั้น | คำอธิบาย |
---|---|
UsbManager |
ช่วยให้คุณสามารถแจกแจงและสื่อสารกับอุปกรณ์ USB ที่เชื่อมต่ออยู่ |
UsbDevice |
แสดงอุปกรณ์ USB ที่เชื่อมต่ออยู่และมีวิธีเข้าถึงการระบุอุปกรณ์ อินเทอร์เฟซ และอุปกรณ์ปลายทางต่างๆ |
UsbInterface |
หมายถึงอินเทอร์เฟซของอุปกรณ์ USB ซึ่งกำหนดชุดฟังก์ชันการทำงานสำหรับ อุปกรณ์ อุปกรณ์สามารถมีอินเทอร์เฟซตั้งแต่ 1 รายการขึ้นไปสำหรับสื่อสาร |
UsbEndpoint |
หมายถึงปลายทางของอินเทอร์เฟซ ซึ่งเป็นช่องทางการสื่อสารสำหรับอินเทอร์เฟซนี้ CANNOT TRANSLATE ของอินเทอร์เฟซมีปลายทางได้อย่างน้อย 1 รายการ และมักจะมีปลายทางอินพุตและเอาต์พุตสำหรับ เป็นการสื่อสารแบบ 2 ทางกับอุปกรณ์ |
UsbDeviceConnection |
หมายถึงการเชื่อมต่อกับอุปกรณ์ ซึ่งจะโอนข้อมูลบนปลายทาง ชั้นเรียนนี้ ช่วยให้คุณสามารถส่งข้อมูลกลับไปกลับมาแบบซิงโครนัสหรือแบบไม่พร้อมกัน |
UsbRequest |
แสดงคำขอแบบไม่พร้อมกันเพื่อสื่อสารกับอุปกรณ์ผ่าน UsbDeviceConnection |
UsbConstants |
กำหนดค่าคงที่ USB ที่ตรงกับคำจำกัดความใน linux/usb/ch9.h ของ Linux เคอร์เนล |
โดยทั่วไป คุณจะต้องใช้ชั้นเรียนเหล่านี้ทั้งหมด (ต้องใช้ UsbRequest
เฉพาะกรณีที่คุณสื่อสารแบบไม่พร้อมกันเท่านั้น)
เมื่อสื่อสารกับอุปกรณ์ USB โดยทั่วไปแล้ว คุณจะได้รับ UsbManager
เพื่อเรียกข้อมูล UsbDevice
ที่ต้องการ
เมื่อมีอุปกรณ์แล้ว คุณจะต้องค้นหา UsbInterface
ที่เหมาะสมและ UsbEndpoint
ของอุปกรณ์นั้น
สำหรับการสื่อสาร เมื่อคุณได้ปลายทางที่ถูกต้องแล้ว ให้เปิด UsbDeviceConnection
เพื่อสื่อสารกับอุปกรณ์ USB
ข้อกำหนดของไฟล์ Manifest สำหรับ Android
รายการต่อไปนี้อธิบายสิ่งที่คุณต้องเพิ่มลงในไฟล์ Manifest ของแอปพลิเคชันก่อน การทำงานกับ API ของโฮสต์ USB
- เนื่องจากอุปกรณ์ที่ใช้ Android บางอุปกรณ์ไม่สามารถรับประกัน API ของโฮสต์ USB ได้
รวมเอลิเมนต์
<uses-feature>
ที่ประกาศว่าแอปพลิเคชันของคุณใช้ ฟีเจอร์android.hardware.usb.host
- ตั้งค่า SDK ขั้นต่ำของแอปพลิเคชันเป็น API ระดับ 12 ขึ้นไป API ของโฮสต์ USB ไม่ใช่ ซึ่งมีอยู่ใน API ระดับก่อนหน้า
- หากต้องการให้แอปพลิเคชันของคุณรับการแจ้งเตือนว่ามีอุปกรณ์ USB ที่เชื่อมต่ออยู่ ให้ระบุ
คู่เอลิเมนต์
<intent-filter>
และ<meta-data>
สำหรับ Intentandroid.hardware.usb.action.USB_DEVICE_ATTACHED
ในกิจกรรมหลัก เอลิเมนต์<meta-data>
ชี้ไปที่ไฟล์ทรัพยากร XML ภายนอกที่ประกาศ ข้อมูลที่ระบุตัวตนได้ เกี่ยวกับอุปกรณ์ที่คุณต้องการตรวจหาในไฟล์ทรัพยากร XML ให้ประกาศองค์ประกอบ
<usb-device>
สำหรับ USB อุปกรณ์ที่คุณต้องการกรอง รายการต่อไปนี้จะอธิบายแอตทริบิวต์ของ<usb-device>
โดยทั่วไป ให้ใช้ผู้ให้บริการและรหัสผลิตภัณฑ์หากต้องการกรอง สำหรับอุปกรณ์ที่เฉพาะเจาะจง และใช้คลาส คลาสย่อย และโปรโตคอลหากคุณต้องการกรองหากลุ่ม ของอุปกรณ์ USB เช่น อุปกรณ์จัดเก็บข้อมูลจำนวนมาก หรือกล้องดิจิทัล คุณสามารถระบุไม่มีหรือ แอตทริบิวต์ทั้งหมดนี้ การระบุไม่ให้แอตทริบิวต์ที่ตรงกับอุปกรณ์ USB ทุกเครื่อง ดังนั้นโปรดทําเฉพาะ หากแอปพลิเคชันของคุณต้องใช้งานvendor-id
product-id
class
subclass
protocol
(อุปกรณ์หรืออินเทอร์เฟซ)
บันทึกไฟล์ทรัพยากรในไดเรกทอรี
res/xml/
ชื่อไฟล์ทรัพยากร (ไม่มีนามสกุล .xml) ต้องเป็นชื่อเดียวกับที่คุณระบุไว้ใน องค์ประกอบ<meta-data>
รูปแบบสำหรับไฟล์ทรัพยากร XML อยู่ในรูปแบบ ตัวอย่างด้านล่าง
ตัวอย่างไฟล์ Manifest และไฟล์ทรัพยากร
ตัวอย่างต่อไปนี้แสดงตัวอย่างไฟล์ Manifest และไฟล์ทรัพยากรที่เกี่ยวข้อง
<manifest ...> <uses-feature android:name="android.hardware.usb.host" /> <uses-sdk android:minSdkVersion="12" /> ... <application> <activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity> </application> </manifest>
ในกรณีนี้ ไฟล์ทรัพยากรต่อไปนี้ควรได้รับการบันทึกไว้ใน
res/xml/device_filter.xml
และระบุว่าอุปกรณ์ USB ที่มี
ควรกรองแอตทริบิวต์ต่อไปนี้
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" /> </resources>
ทำงานกับอุปกรณ์
เมื่อผู้ใช้เชื่อมต่ออุปกรณ์ USB กับอุปกรณ์ Android ระบบ Android จะสามารถระบุ แอปพลิเคชันของคุณสนใจอุปกรณ์ที่เชื่อมต่อหรือไม่ หากใช่ คุณสามารถตั้งค่า สื่อสารกับอุปกรณ์ได้ หากต้องการ ในการดำเนินการดังกล่าว แอปพลิเคชันของคุณจะต้อง:
- สำรวจอุปกรณ์ USB ที่เชื่อมต่อโดยใช้ตัวกรอง Intent เพื่อรับการแจ้งเตือนเมื่อผู้ใช้ เชื่อมต่ออุปกรณ์ USB หรือแจกแจงอุปกรณ์ USB ที่เชื่อมต่ออยู่แล้ว
- ขออนุญาตจากผู้ใช้ในการเชื่อมต่อกับอุปกรณ์ USB หากยังไม่ได้ขอรับ
- สื่อสารกับอุปกรณ์ USB โดยการอ่านและเขียนข้อมูลบนอินเทอร์เฟซที่เหมาะสม ปลายทาง
สำรวจอุปกรณ์
แอปพลิเคชันของคุณสามารถค้นพบอุปกรณ์ USB โดยใช้ตัวกรอง Intent เพื่อรับการแจ้งเตือนเมื่อ ผู้ใช้เชื่อมต่ออุปกรณ์หรือแจกแจงอุปกรณ์ USB ที่เชื่อมต่ออยู่แล้ว การใช้ ตัวกรอง Intent จะมีประโยชน์ในกรณีที่คุณต้องการให้แอปพลิเคชันตรวจหา อุปกรณ์ที่ต้องการได้อีกด้วย การระบุอุปกรณ์ USB ที่เชื่อมต่อไว้มีประโยชน์หากต้องการดูรายการทั้งหมด อุปกรณ์ที่เชื่อมต่อ หรือหากแอปพลิเคชันของคุณไม่ได้กรองความตั้งใจออก
ใช้ตัวกรอง Intent
หากต้องการให้แอปพลิเคชันพบอุปกรณ์ USB ที่ต้องการ คุณสามารถระบุตัวกรอง Intent เพื่อ
กรองหา Intent android.hardware.usb.action.USB_DEVICE_ATTACHED
พร้อมกับ
ตัวกรอง Intent นี้ คุณจะต้องระบุไฟล์ทรัพยากรที่ระบุคุณสมบัติของ USB
เช่น รหัสผลิตภัณฑ์และผู้ให้บริการ เมื่อผู้ใช้เชื่อมต่ออุปกรณ์ที่ตรงกับอุปกรณ์ของคุณ
ระบบจะแสดงกล่องโต้ตอบที่ถามผู้ใช้ว่าต้องการเริ่มแอปพลิเคชันหรือไม่
หากผู้ใช้ยอมรับ แอปพลิเคชันของคุณจะได้รับอนุญาตให้เข้าถึงอุปกรณ์โดยอัตโนมัติจนกว่า
อุปกรณ์ไม่ได้เชื่อมต่ออยู่
ตัวอย่างต่อไปนี้แสดงวิธีประกาศตัวกรอง Intent
<activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity>
ตัวอย่างต่อไปนี้แสดงวิธีประกาศไฟล์ทรัพยากรที่เกี่ยวข้องที่ระบุ อุปกรณ์ USB ที่คุณสนใจ:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" /> </resources>
ในกิจกรรม คุณสามารถรับ UsbDevice
ที่แสดงถึง
อุปกรณ์ที่เชื่อมต่อจาก Intent ดังนี้
Kotlin
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
Java
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
แจกแจงอุปกรณ์
หากแอปพลิเคชันของคุณสนใจที่จะตรวจสอบอุปกรณ์ USB ทั้งหมดที่เชื่อมต่ออยู่ในปัจจุบัน
ขณะที่แอปพลิเคชันทำงานอยู่ จะสามารถแจกแจงอุปกรณ์บนรถประจำทางได้ ใช้เมธอด getDeviceList()
เพื่อรับแมปแฮชของทั้งหมด
อุปกรณ์ USB ที่เชื่อมต่ออยู่ ระบบจะคีย์แมปแฮชโดยใช้ชื่ออุปกรณ์ USB หากคุณต้องการ
รับอุปกรณ์จากแผนที่
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager ... val deviceList = manager.getDeviceList() val device = deviceList.get("deviceName")
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); ... HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); UsbDevice device = deviceList.get("deviceName");
นอกจากนี้ คุณยังใช้ตัววนซ้ำจากแฮชแมปและประมวลผลแต่ละอุปกรณ์ได้หากต้องการ หนึ่ง:
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager .. val deviceList: HashMap<String, UsbDevice> = manager.deviceList deviceList.values.forEach { device -> // your code }
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); ... HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); Iterator<UsbDevice> deviceIterator = deviceList.values().iterator(); while(deviceIterator.hasNext()){ UsbDevice device = deviceIterator.next(); // your code }
ขออนุญาตสื่อสารกับอุปกรณ์
ก่อนที่จะสื่อสารกับอุปกรณ์ USB แอปพลิเคชันของคุณต้องได้รับอนุญาตจาก ผู้ใช้
หมายเหตุ: หากแอปพลิเคชันของคุณใช้ Intent ของตัวกรองเพื่อค้นหาอุปกรณ์ USB เมื่อเชื่อมต่ออยู่ อุปกรณ์จะตรวจจับ หากผู้ใช้อนุญาตให้แอปพลิเคชันของคุณจัดการ Intent หากไม่ใช่ คุณต้องส่งคำขอ ได้รับสิทธิ์ในแอปพลิเคชันของคุณอย่างชัดเจนก่อนที่จะเชื่อมต่อกับอุปกรณ์
การขอสิทธิ์อย่างชัดแจ้งในบางสถานการณ์อาจจำเป็น เช่นเมื่อคุณ แอปพลิเคชันแจกแจงอุปกรณ์ USB ที่เชื่อมต่อแล้ว จากนั้นต้องการสื่อสารด้วย ข้อแรก คุณต้องตรวจสอบสิทธิ์ในการเข้าถึงอุปกรณ์ก่อนที่จะพยายามสื่อสารกับอุปกรณ์นั้น ถ้า ไม่ใช่ คุณจะได้รับข้อผิดพลาดเกี่ยวกับรันไทม์หากผู้ใช้ปฏิเสธสิทธิ์การเข้าถึงอุปกรณ์
หากต้องการได้รับอนุญาตอย่างชัดแจ้ง ให้สร้าง Broadcast Receiver ก่อน รีซีฟเวอร์นี้จะคอยฟัง
Intent ที่จะประกาศเมื่อคุณโทรหา requestPermission()
การโทรไปยัง requestPermission()
แสดงกล่องโต้ตอบสำหรับ
ผู้ใช้ที่ขออนุญาตเชื่อมต่อกับอุปกรณ์ โค้ดตัวอย่างต่อไปนี้จะแสดงวิธีการ
สร้าง Broadcast Receiver:
Kotlin
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION" private val usbReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (ACTION_USB_PERMISSION == intent.action) { synchronized(this) { val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { device?.apply { // call method to set up device communication } } else { Log.d(TAG, "permission denied for device $device") } } } } }
Java
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if(device != null){ // call method to set up device communication } } else { Log.d(TAG, "permission denied for device " + device); } } } } };
หากต้องการลงทะเบียน Broadcast Receiver ให้เพิ่มค่านี้ในเมธอด onCreate()
ใน
กิจกรรม:
Kotlin
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION" ... val manager = getSystemService(Context.USB_SERVICE) as UsbManager ... permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE) val filter = IntentFilter(ACTION_USB_PERMISSION) registerReceiver(usbReceiver, filter)
Java
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; ... permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(usbReceiver, filter);
หากต้องการแสดงกล่องโต้ตอบที่ขอสิทธิ์เชื่อมต่อกับอุปกรณ์ ให้เรียกเมธอด requestPermission()
ดังนี้
Kotlin
lateinit var device: UsbDevice ... usbManager.requestPermission(device, permissionIntent)
Java
UsbDevice device; ... usbManager.requestPermission(device, permissionIntent);
เมื่อผู้ใช้ตอบกลับกล่องโต้ตอบ เครื่องรับการออกอากาศจะได้รับ Intent ที่มี
เกินมา EXTRA_PERMISSION_GRANTED
ซึ่งเป็นบูลีน
ที่แสดงถึงคำตอบ ตรวจสอบส่วนเพิ่มเติมนี้เพื่อดูค่า "จริง" ก่อนเชื่อมต่อกับ
อุปกรณ์
สื่อสารกับอุปกรณ์
การสื่อสารกับอุปกรณ์ USB อาจเป็นแบบซิงโครนัสหรืออะซิงโครนัสก็ได้ ไม่ว่าจะเป็นกรณีใด คุณ
ควรสร้างชุดข้อความใหม่ สำหรับรับส่งข้อมูลทั้งหมด เพื่อไม่ให้บล็อก
ชุดข้อความ UI ในการตั้งค่าการสื่อสารกับอุปกรณ์อย่างเหมาะสม คุณต้องขอรับ
UsbInterface
และ UsbEndpoint
ของ
อุปกรณ์ที่คุณต้องการสื่อสารและส่งคำขอในอุปกรณ์ปลายทางนี้ด้วย UsbDeviceConnection
โดยทั่วไป โค้ดของคุณควร:
- ตรวจสอบแอตทริบิวต์ของออบเจ็กต์
UsbDevice
เช่น รหัสผลิตภัณฑ์ รหัสผู้ให้บริการ หรือคลาสของอุปกรณ์เพื่อพิจารณาว่าคุณต้องการสื่อสารกับผู้ให้บริการหรือไม่ อุปกรณ์ - เมื่อแน่ใจแล้วว่าคุณต้องการสื่อสารกับอุปกรณ์ ให้ค้นหาอุปกรณ์ที่เหมาะสม
UsbInterface
ที่คุณต้องการใช้เพื่อสื่อสารควบคู่กับUsbEndpoint
ของอินเทอร์เฟซนั้น อินเทอร์เฟซสามารถมี อุปกรณ์ปลายทางเพิ่มเติม และโดยทั่วไปจะมีอุปกรณ์ปลายทางอินพุตและเอาต์พุตสำหรับการสื่อสารแบบ 2 ทาง การสื่อสาร - เมื่อคุณพบปลายทางที่ถูกต้องแล้ว ให้เปิด
UsbDeviceConnection
ที่ปลายทางนั้น - ระบุข้อมูลที่ต้องการส่งบนปลายทางด้วยเมธอด
bulkTransfer()
หรือcontrolTransfer()
คุณควร ทําตามขั้นตอนนี้ในเทรดอื่นเพื่อป้องกันการบล็อกเทรด UI หลัก สำหรับข้อมูลเพิ่มเติม ข้อมูลเกี่ยวกับการใช้ชุดข้อความใน Android ได้ที่หัวข้อกระบวนการและ Threads
ข้อมูลโค้ดต่อไปนี้เป็นวิธีที่ไม่สำคัญในการโอนข้อมูลแบบซิงโครนัส รหัสของคุณ ควรมีตรรกะมากกว่านี้ในการค้นหาอินเทอร์เฟซและปลายทางที่ถูกต้องเพื่อสื่อสาร และควรทำการโอนข้อมูลในเทรดอื่นที่ไม่ใช่เทรด UI หลักด้วย
Kotlin
private lateinit var bytes: ByteArray private val TIMEOUT = 0 private val forceClaim = true ... device?.getInterface(0)?.also { intf -> intf.getEndpoint(0)?.also { endpoint -> usbManager.openDevice(device)?.apply { claimInterface(intf, forceClaim) bulkTransfer(endpoint, bytes, bytes.size, TIMEOUT) //do in another thread } } }
Java
private Byte[] bytes; private static int TIMEOUT = 0; private boolean forceClaim = true; ... UsbInterface intf = device.getInterface(0); UsbEndpoint endpoint = intf.getEndpoint(0); UsbDeviceConnection connection = usbManager.openDevice(device); connection.claimInterface(intf, forceClaim); connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread
หากต้องการส่งข้อมูลแบบไม่พร้อมกัน ให้ใช้คลาส UsbRequest
เพื่อinitialize
และqueue
คำขอแบบไม่พร้อมกัน แล้วรอผลลัพธ์
ด้วย requestWait()
กำลังยุติการสื่อสารกับอุปกรณ์
เมื่อสื่อสารกับอุปกรณ์เสร็จแล้วหรือหากมีการถอดอุปกรณ์ออก ให้ปิด UsbInterface
และ UsbDeviceConnection
ภายใน
กำลังโทรหา releaseInterface()
และ
close()
หากต้องการฟังเหตุการณ์ที่แยกจากกัน
สร้าง Broadcast Receiver ดังด้านล่าง
Kotlin
var usbReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) { val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) device?.apply { // call your method that cleans up and closes communication with the device } } } }
Java
BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) { // call your method that cleans up and closes communication with the device } } } };
การสร้าง Broadcast Receiver ภายในแอปพลิเคชัน ไม่ใช่ไฟล์ Manifest เพื่อจัดการเหตุการณ์ที่ถูกตัดออกขณะทำงานอยู่เท่านั้น วิธีนี้จะทำให้เหตุการณ์ที่แยกจากกัน ส่งไปยังแอปพลิเคชันที่ทำงานอยู่ในปัจจุบันเท่านั้นและไม่ได้ประกาศไปยังแอปพลิเคชันทั้งหมด