Chế độ phụ kiện USB cho phép người dùng kết nối Phần cứng lưu trữ USB được thiết kế riêng cho các thiết bị chạy Android. Các phụ kiện phải tuân thủ về giao thức phụ kiện Android nêu trong tài liệu về Bộ phát triển phụ kiện Android. Điều này cho phép các thiết bị chạy Android không thể hoạt động như một máy chủ USB vẫn tương tác với USB phần cứng. Khi một thiết bị chạy Android ở chế độ phụ kiện USB, USB Android đi kèm phụ kiện đóng vai trò là máy chủ, cung cấp nguồn cho bus USB và liệt kê các thiết bị được kết nối. Android 3.1 (API cấp 12) hỗ trợ chế độ phụ kiện USB và tính năng này cũng được điều chỉnh cho phiên bản cũ Android 2.3.4 (API cấp 10) cho phép hỗ trợ nhiều thiết bị hơn.
Chọn API phụ kiện USB phù hợp
Mặc dù API phụ kiện USB đã được đưa vào nền tảng trong Android 3.1, nhưng chúng cũng có sẵn trong Android 2.3.4 bằng cách sử dụng thư viện tiện ích bổ sung API của Google. Bởi vì các API này điều chỉnh cho phiên bản cũ bằng cách sử dụng thư viện bên ngoài, bạn có thể nhập hai gói để hỗ trợ USB chế độ phụ kiện. Tuỳ thuộc vào thiết bị chạy Android mà bạn muốn hỗ trợ, bạn có thể phải sử dụng từng cái với nhau:
com.android.future.usb
: Để hỗ trợ chế độ phụ kiện USB trong Android 2.3.4, Tiện ích bổ sung API của Google thư viện bao gồm các API phụ kiện USB được điều chỉnh cho phiên bản cũ và các API này có trong thư viện này không gian tên. Android 3.1 cũng hỗ trợ việc nhập và gọi các lớp trong không gian tên này để hỗ trợ các ứng dụng được viết bằng thư viện tiện ích bổ sung. Thư viện tiện ích bổ sung này là một trình bao bọc mỏng xung quanh API phụ kiệnandroid.hardware.usb
và không hỗ trợ chế độ hỗ trợ USB. Nếu bạn muốn hỗ trợ nhiều thiết bị nhất có hỗ trợ chế độ phụ kiện USB, hãy sử dụng tiện ích bổ sung thư viện và nhập gói này. Điều quan trọng cần lưu ý là không phải tất cả các thiết bị Android 2.3.4 đều cần thiết để hỗ trợ tính năng phụ kiện USB. Mỗi nhà sản xuất thiết bị riêng lẻ sẽ quyết định liệu có hỗ trợ chức năng này hay không. Đó là lý do bạn phải khai báo tính năng này trong tệp kê khai .android.hardware.usb
: Không gian tên này chứa các lớp hỗ trợ USB chế độ phụ kiện trong Android 3.1. Gói này được đưa vào như một phần của API khung, vì vậy Android 3.1 hỗ trợ chế độ phụ kiện USB mà không cần sử dụng thư viện tiện ích bổ sung. Sử dụng gói này nếu bạn chỉ quan tâm đến các thiết bị Android 3.1 hoặc mới hơn có hỗ trợ phần cứng cho USB chế độ phụ kiện mà bạn có thể khai báo trong tệp kê khai.
Cài đặt thư viện tiện ích bổ sung API của Google
Nếu muốn cài đặt tiện ích bổ sung, bạn có thể thực hiện bằng cách cài đặt API Google Android API 10 bằng Trình quản lý SDK. Xem Cài đặt Google API Tiện ích bổ sung để biết thêm thông tin về cách cài đặt thư viện tiện ích bổ sung.
Tổng quan về API
Vì thư viện tiện ích bổ sung là một trình bao bọc cho các API khung, nên các lớp hỗ trợ
Tính năng phụ kiện USB cũng tương tự. Bạn có thể sử dụng tài liệu tham khảo cho android.hardware.usb
ngay cả khi đang sử dụng thư viện tiện ích bổ sung.
Lưu ý: Tuy nhiên, có một trường hợp sử dụng nhỏ điểm khác biệt giữa thư viện tiện ích bổ sung và các API khung mà bạn cần lưu ý.
Bảng sau đây mô tả các lớp hỗ trợ API phụ kiện USB:
Lớp | Mô tả |
---|---|
UsbManager |
Cho phép bạn liệt kê và giao tiếp với các phụ kiện USB đã kết nối. |
UsbAccessory |
Đại diện cho một phụ kiện USB và chứa các phương thức để truy cập vào thông tin nhận dạng phụ kiện của bạn. |
Sự khác biệt về cách sử dụng giữa API thư viện tiện ích bổ sung và API nền tảng
Có hai sự khác biệt về cách sử dụng giữa việc sử dụng thư viện tiện ích bổ sung API của Google và nền tảng API.
Nếu đang sử dụng thư viện tiện ích bổ sung, bạn phải lấy đối tượng UsbManager
theo cách sau:
Kotlin
val manager = UsbManager.getInstance(this)
Java
UsbManager manager = UsbManager.getInstance(this);
Nếu không dùng thư viện tiện ích bổ sung, bạn phải lấy đối tượng UsbManager
theo cách sau:
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
Khi bạn lọc tìm một phụ kiện đã kết nối có bộ lọc ý định, đối tượng UsbAccessory
sẽ nằm trong ý định được truyền đến
. Nếu đang sử dụng thư viện tiện ích bổ sung, bạn phải lấy đối tượng UsbAccessory
theo cách sau:
Kotlin
val accessory = UsbManager.getAccessory(intent)
Java
UsbAccessory accessory = UsbManager.getAccessory(intent);
Nếu không dùng thư viện tiện ích bổ sung, bạn phải lấy đối tượng UsbAccessory
theo cách sau:
Kotlin
val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
Java
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
Yêu cầu về tệp kê khai Android
Danh sách sau đây mô tả những gì bạn cần thêm vào tệp kê khai của ứng dụng trước khi hoạt động với API phụ kiện USB. Tệp kê khai và tệp tài nguyên ví dụ minh hoạ cách khai báo các mục này:
- Vì không phải tất cả thiết bị chạy Android đều được đảm bảo hỗ trợ API phụ kiện USB,
thêm một phần tử
<uses-feature>
khai báo rằng ứng dụng của bạn sử dụng tính năngandroid.hardware.usb.accessory
. - Nếu bạn đang sử dụng
thư viện tiện ích bổ sung,
thêm phần tử
<uses-library>
để chỉ địnhcom.android.future.usb.accessory
cho thư viện. - Đặt SDK tối thiểu của ứng dụng thành API cấp 10 nếu bạn đang dùng thư viện tiện ích bổ sung
hoặc 12 nếu bạn đang dùng gói
android.hardware.usb
. -
Nếu bạn muốn ứng dụng của mình được thông báo về phụ kiện USB đi kèm, hãy chỉ định Cặp phần tử
<intent-filter>
và<meta-data>
cho phần tử Ý địnhandroid.hardware.usb.action.USB_ACCESSORY_ATTACHED
trong hoạt động chính của bạn. Phần tử<meta-data>
trỏ đến một tệp tài nguyên XML bên ngoài khai báo thông tin nhận dạng về phụ kiện mà bạn muốn phát hiện.Trong tệp tài nguyên XML, hãy khai báo các phần tử
<usb-accessory>
cho phần tử phụ kiện mà bạn muốn lọc. Mỗi<usb-accessory>
có thể có các thuộc tính sau:manufacturer
model
version
Bạn không nên lọc trên
version
. Một phụ kiện hoặc thiết bị không phải lúc nào cũng chỉ định chuỗi phiên bản (cố ý hoặc vô tình). Khi ứng dụng của bạn khai báo thuộc tính phiên bản để lọc và phụ kiện hoặc thiết bị không chỉ định chuỗi phiên bản, điều này sẽ dẫn đếnNullPointerException
trên các phiên bản Android cũ hơn. Vấn đề này đã được khắc phục trong Android 12.Lưu tệp tài nguyên trong thư mục
res/xml/
. Tên tệp tài nguyên (không có đuôi .xml) phải giống với đuôi mà bạn đã chỉ định trong Phần tử<meta-data>
. Định dạng của tệp tài nguyên XML cũng được thể hiện trong ví dụ bên dưới.
Ví dụ về tệp kê khai và tệp tài nguyên
Ví dụ sau đây cho thấy một tệp kê khai mẫu và tệp tài nguyên tương ứng của tệp kê khai đó:
<manifest ...> <uses-feature android:name="android.hardware.usb.accessory" /> <uses-sdk android:minSdkVersion="<version>" /> ... <application> <uses-library android:name="com.android.future.usb.accessory" /> <activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/accessory_filter" /> </activity> </application> </manifest>
Trong trường hợp này, tệp tài nguyên sau cần được lưu trong
res/xml/accessory_filter.xml
và quy định rằng mọi phụ kiện có
kiểu máy, nhà sản xuất và phiên bản tương ứng sẽ được lọc. Phụ kiện sẽ gửi những thông tin này
cho biết thiết bị chạy Android:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/> </resources>
Tương thích với các phụ kiện
Khi người dùng kết nối phụ kiện USB với một thiết bị chạy Android, hệ thống Android có thể xác định xem ứng dụng của bạn có quan tâm đến phụ kiện đã kết nối hay không. Nếu có, bạn có thể đặt kết nối với phụ kiện nếu muốn. Để làm được việc này, ứng dụng của bạn phải:
- Khám phá các phụ kiện đã kết nối bằng cách dùng bộ lọc ý định để lọc phụ kiện các sự kiện đính kèm hoặc bằng cách liệt kê các phụ kiện được kết nối và tìm phụ kiện phù hợp.
- Hãy yêu cầu người dùng cấp quyền để giao tiếp với phụ kiện (nếu chưa) thu được.
- Giao tiếp với phụ kiện bằng cách đọc và ghi dữ liệu trên giao diện thích hợp điểm cuối.
Khám phá một phụ kiện
Ứng dụng của bạn có thể phát hiện các phụ kiện bằng cách sử dụng bộ lọc ý định để nhận được thông báo khi người dùng kết nối một phụ kiện hoặc bằng cách liệt kê các phụ kiện đã kết nối. Sử dụng rất hữu ích nếu bạn muốn ứng dụng của bạn tự động phát hiện phụ kiện mong muốn. Việc liệt kê các phụ kiện được kết nối rất hữu ích nếu bạn muốn có danh sách tất cả các phụ kiện các phụ kiện đã kết nối hoặc nếu ứng dụng của bạn không lọc ý định.
Sử dụng bộ lọc ý định
Để ứng dụng của bạn khám phá một phụ kiện USB cụ thể, bạn có thể chỉ định một bộ lọc ý định
để lọc ý định android.hardware.usb.action.USB_ACCESSORY_ATTACHED
. Dọc theo
thì với bộ lọc ý định này, bạn cần chỉ định tệp tài nguyên chỉ định các thuộc tính của USB
phụ kiện, chẳng hạn như nhà sản xuất, kiểu máy và phiên bản.
Ví dụ sau đây cho thấy cách khai báo bộ lọc ý định:
<activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/accessory_filter" /> </activity>
Ví dụ sau đây trình bày cách khai báo tệp tài nguyên tương ứng chỉ định Phụ kiện USB mà bạn quan tâm:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" /> </resources>
Trong hoạt động, bạn có thể lấy UsbAccessory
biểu thị
phụ kiện đi kèm theo ý định như sau (với thư viện tiện ích bổ sung):
Kotlin
val accessory = UsbManager.getAccessory(intent)
Java
UsbAccessory accessory = UsbManager.getAccessory(intent);
hoặc như thế này (với API nền tảng):
Kotlin
val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
Java
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
Liệt kê phụ kiện
Bạn có thể để ứng dụng liệt kê các phụ kiện tự xác định trong khi ứng dụng của bạn đang chạy.
Sử dụng phương thức getAccessoryList()
để lấy một mảng cho tất cả phụ kiện USB được kết nối:
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager val accessoryList: Array<out UsbAccessory> = manager.accessoryList
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); UsbAccessory[] accessoryList = manager.getAccessoryList();
Lưu ý: Chỉ hỗ trợ một phụ kiện đã kết nối tại một lúc.
Xin phép để giao tiếp với một phụ kiện
Trước khi giao tiếp với phụ kiện USB, ứng dụng của bạn phải được người dùng.
Lưu ý: Nếu ứng dụng của bạn sử dụng bộ lọc ý định để khám phá các phụ kiện khi chúng được kết nối, bộ lọc này sẽ tự động nhận được nếu người dùng cho phép ứng dụng của bạn xử lý ý định. Nếu không, bạn phải yêu cầu bạn cấp quyền một cách rõ ràng trong ứng dụng của mình trước khi kết nối với phụ kiện.
Bạn có thể cần phải yêu cầu cấp quyền rõ ràng trong một số trường hợp như khi ứng dụng liệt kê các phụ kiện đã được kết nối và sau đó muốn giao tiếp với một. Bạn phải kiểm tra quyền truy cập vào một phụ kiện rồi mới có thể giao tiếp với phụ kiện đó. Nếu không, bạn sẽ gặp lỗi thời gian chạy nếu người dùng từ chối cấp quyền truy cập vào phụ kiện.
Để có được quyền rõ ràng, trước tiên, hãy tạo một broadcast receiver. Bộ thu này lắng nghe
ý định được truyền tin khi bạn gọi requestPermission()
. Lệnh gọi đến requestPermission()
sẽ hiển thị một hộp thoại cho lệnh gọi đến
người dùng yêu cầu cấp quyền kết nối với phụ kiện. Mã mẫu sau đây cho biết cách
tạo 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 accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { accessory?.apply { // call method to set up accessory communication } } else { Log.d(TAG, "permission denied for accessory $accessory") } } } } }
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) { UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if(accessory != null){ // call method to set up accessory communication } } else { Log.d(TAG, "permission denied for accessory " + accessory); } } } } };
Để đăng ký broadcast receiver, hãy đặt mã này vào phương thức onCreate()
trong
hoạt động:
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), 0) 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), 0); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(usbReceiver, filter);
Để hiển thị hộp thoại yêu cầu người dùng cấp quyền kết nối với phụ kiện, hãy gọi
Phương thức requestPermission()
:
Kotlin
lateinit var accessory: UsbAccessory ... usbManager.requestPermission(accessory, permissionIntent)
Java
UsbAccessory accessory; ... usbManager.requestPermission(accessory, permissionIntent);
Khi người dùng trả lời hộp thoại, broadcast receiver của bạn sẽ nhận được ý định chứa
EXTRA_PERMISSION_GRANTED
bổ sung, là một giá trị boolean
cho câu trả lời. Kiểm tra phần bổ sung này để biết giá trị true trước khi kết nối với
phụ kiện.
Kết nối với một phụ kiện
Bạn có thể giao tiếp với phụ kiện bằng cách dùng UsbManager
để
lấy chỉ số mô tả tệp mà bạn có thể thiết lập luồng đầu vào và đầu ra để đọc và ghi dữ liệu
mã mô tả. Các luồng này biểu thị các điểm cuối hàng loạt đối với đầu vào và đầu ra của phụ kiện. Bạn nên đặt
tăng khả năng giao tiếp giữa thiết bị và phụ kiện trong một chuỗi khác để bạn không khoá
luồng giao diện người dùng chính. Ví dụ sau đây cho biết cách mở một phụ kiện để giao tiếp:
Kotlin
private lateinit var accessory: UsbAccessory private var fileDescriptor: ParcelFileDescriptor? = null private var inputStream: FileInputStream? = null private var outputStream: FileOutputStream? = null ... private fun openAccessory() { Log.d(TAG, "openAccessory: $mAccessory") fileDescriptor = usbManager.openAccessory(accessory) fileDescriptor?.fileDescriptor?.also { fd -> inputStream = FileInputStream(fd) outputStream = FileOutputStream(fd) val thread = Thread(null, this, "AccessoryThread") thread.start() } }
Java
UsbAccessory accessory; ParcelFileDescriptor fileDescriptor; FileInputStream inputStream; FileOutputStream outputStream; ... private void openAccessory() { Log.d(TAG, "openAccessory: " + accessory); fileDescriptor = usbManager.openAccessory(accessory); if (fileDescriptor != null) { FileDescriptor fd = fileDescriptor.getFileDescriptor(); inputStream = new FileInputStream(fd); outputStream = new FileOutputStream(fd); Thread thread = new Thread(null, this, "AccessoryThread"); thread.start(); } }
Trong phương thức run()
của chuỗi, bạn có thể đọc và ghi vào phụ kiện bằng cách sử dụng
các đối tượng FileInputStream
hoặc FileOutputStream
. Khi đọc
dữ liệu từ một phụ kiện có đối tượng FileInputStream
, hãy đảm bảo rằng vùng đệm
mà bạn sử dụng đủ lớn để lưu trữ dữ liệu gói USB. Giao thức phụ kiện Android hỗ trợ
vùng đệm gói lên đến 16384 byte, vì vậy bạn có thể chọn luôn khai báo vùng đệm của mình là vùng đệm này
kích thước lớn hơn để đơn giản hoá.
Lưu ý: Ở cấp độ thấp hơn, các gói có kích thước 64 byte cho USB phụ kiện tốc độ tối đa và 512 byte đối với phụ kiện tốc độ cao USB. Phụ kiện Android giao thức gói các gói lại với nhau cho cả hai tốc độ thành một gói logic để đơn giản hoá.
Để biết thêm thông tin về cách sử dụng luồng trong Android, hãy xem bài viết Quy trình và Chuỗi cuộc trò chuyện.
Chấm dứt giao tiếp với phụ kiện
Khi bạn kết nối xong với một phụ kiện hoặc nếu phụ kiện đó đã bị tháo, hãy đóng
chỉ số mô tả tệp mà bạn đã mở bằng cách gọi close()
.
Để theo dõi các sự kiện đã tách rời, hãy tạo một broadcast receiver như dưới đây:
Kotlin
var usbReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (UsbManager.ACTION_USB_ACCESSORY_DETACHED == intent.action) { val accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) accessory?.apply { // call your method that cleans up and closes communication with the accessory } } } }
Java
BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) { UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); if (accessory != null) { // call your method that cleans up and closes communication with the accessory } } } };
Việc tạo broadcast receiver trong ứng dụng chứ không phải trong tệp kê khai cho phép để chỉ xử lý các sự kiện được tách ra khi đang chạy. Bằng cách này, các sự kiện được tách chỉ gửi tới ứng dụng hiện đang chạy và không truyền phát tới tất cả ứng dụng.