מצב אביזר USB מאפשר למשתמשים להתחבר חומרת מארח USB שתוכננה במיוחד למכשירים מבוססי Android. האביזרים צריכים להיות תואמים לפרוטוקול האביזר של Android שמתואר בתיעוד של Android Accessory Development Kit. הפעולה הזו מאפשרת למכשירים מבוססי Android, שלא יכולים לפעול כמארח USB, להמשיך לקיים אינטראקציה עם USB חומרה. כשמכשיר מבוסס Android נמצא במצב אביזר USB, מחובר Android USB האביזר משמש כמארח, מספק חשמל לאפיק ה-USB וסופר את המכשירים המחוברים. מערכת Android 3.1 (רמת API 12) תומכת במצב אביזר USB וגם התכונה מועברת לאחור אל Android 2.3.4 (רמת API 10) כדי לאפשר תמיכה במגוון רחב יותר של מכשירים.
בחירת ממשקי ה-API המתאימים של אביזרי USB
למרות שממשקי ה-API של אביזרי USB נוספו לפלטפורמה ב-Android 3.1, הם גם זמינות ב-Android 2.3.4 באמצעות ספריית התוספים של Google APIs. כי ממשקי ה-API האלה לאחר העברה לאחור באמצעות ספרייה חיצונית, יש שתי חבילות שניתן לייבא כדי לתמוך ב-USB מצב 'אביזר'. יכול להיות שתצטרכו לתמוך במכשירים מבוססי-Android שבהם רוצים לתמוך משתמשים באחד מהשניים:
com.android.future.usb
: כדי לתמוך במצב אביזר USB ב-Android 2.3.4, תוסף Google APIs הספרייה כוללת את ממשקי ה-API של אביזרי ה-USB עם העברה לאחור והם כלולים מרחב שמות. Android 3.1 תומך גם בייבוא של כיתות במרחב השמות הזה והפעלה שלהן כדי לתמוך באפליקציות שנכתבו באמצעות ספריית התוספים. ספריית התוספים הזו היא wrapper צר סביב ממשקי ה-API של האביזרandroid.hardware.usb
ולא תומך במצב אירוח USB. אם המיקום אם אתם רוצים לתמוך במגוון הרחב ביותר של מכשירים שתומכים במצב אביזר USB, אתם יכולים להשתמש בתוסף ולייבא את החבילה הזו. חשוב לציין שלא כל מכשירי Android 2.3.4 נתמכים שנדרש לתמיכה בתכונה 'אביזר USB'. כל יצרן מכשיר מחליט האם לתמוך ביכולת הזו, ולכן עליך להצהיר על כך במניפסט חדש.android.hardware.usb
: מרחב השמות הזה מכיל את המחלקות שתומכות ב-USB מצב 'אביזר' ב-Android 3.1. החבילה הזו כלולה כחלק מממשקי ה-API של framework, כך Android 3.1 תומך במצב אביזר USB ללא שימוש בספריית תוספים. שימוש בחבילה הזו אם חשובים לך רק מכשירי Android 3.1 ואילך עם תמיכה בחומרה ב-USB למצב אביזר, שניתן להצהיר עליו בקובץ המניפסט.
התקנה של ספריית התוספים של Google APIs
כדי להתקין את התוסף, צריך להתקין את Google APIs Android API 10 חבילה עם מנהל ה-SDK. למידע נוסף, ניתן לעיין בקטע התקנת ממשקי ה-API של Google. למידע נוסף על התקנת ספריית התוספים,
סקירה כללית על ממשקי API
מכיוון שספריית התוספים היא wrapper של ממשקי ה-API של framework, המחלקות שתומכות ב-framework
מאפייני אביזר ה-USB דומים. אפשר להיעזר במסמכי העזר של android.hardware.usb
גם אם משתמשים בספריית התוספים.
הערה: עם זאת, יש שימוש קטן ההבדל בין ממשקי ה-API של ספריית התוספים ושל framework, שחשוב להיות מודעים להם.
הטבלה הבאה מתארת את המחלקות שתומכות בממשקי API של אביזרי USB:
דרגה | תיאור |
---|---|
UsbManager |
מאפשר לבצע ספירה ולתקשר עם אביזרי USB מחוברים. |
UsbAccessory |
מייצג אביזר USB ומכיל שיטות לגישה מידע. |
הבדלים בשימוש בין ממשקי ה-API של ספריית התוספים ושל הפלטפורמה
יש שני הבדלים בשימוש בין השימוש בספריית התוספים של Google APIs לבין השימוש בפלטפורמה ממשקי API.
אם משתמשים בספריית התוספים, צריך לקבל את האובייקט UsbManager
באופן הבא:
Kotlin
val manager = UsbManager.getInstance(this)
Java
UsbManager manager = UsbManager.getInstance(this);
אם אתם לא משתמשים בספריית התוספים, עליכם לקבל את האובייקט UsbManager
באופן הבא:
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
כשמסננים לפי אביזר מחובר עם מסנן Intent, האובייקט UsbAccessory
נמצא בתוך ה-Intent שמועבר אל
תרגום מכונה. אם משתמשים בספריית התוספים, צריך לקבל את האובייקט UsbAccessory
באופן הבא:
Kotlin
val accessory = UsbManager.getAccessory(intent)
Java
UsbAccessory accessory = UsbManager.getAccessory(intent);
אם אתם לא משתמשים בספריית התוספים, עליכם לקבל את האובייקט UsbAccessory
באופן הבא:
Kotlin
val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
Java
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
דרישות לגבי מניפסטים ב-Android
ברשימה הבאה מפורטים מה צריך להוסיף לקובץ המניפסט של האפליקציה לפני עבודה עם ממשקי API של אביזרי USB. המניפסט וקובץ המשאבים דוגמאות: איך להצהיר (declare) על הפריטים האלה:
- מכיוון שלא כל המכשירים מבוססי Android יתמכו בממשקי API של אביזרים ל-USB,
כוללים רכיב
<uses-feature>
שמצהיר שהאפליקציה משתמשת את התכונהandroid.hardware.usb.accessory
. - אם משתמשים
בספריית התוספים,
מוסיפים את הרכיב
<uses-library>
שמצייןcom.android.future.usb.accessory
לספרייה. - צריך להגדיר את ה-SDK המינימלי של האפליקציה לרמת API 10 אם משתמשים בספריית התוספים
או 12 אם אתה משתמש בחבילת
android.hardware.usb
. -
אם רוצים שהאפליקציה תקבל הודעה לגבי אביזר USB מצורף, צריך לציין זוג אלמנטים של
<intent-filter>
ו-<meta-data>
בשביל כוונת רכישה אחת (android.hardware.usb.action.USB_ACCESSORY_ATTACHED
) בפעילות העיקרית שלך. הרכיב<meta-data>
מפנה לקובץ משאב XML חיצוני ש כוללת הצהרה על פרטים מזהים של האביזר שרוצים לזהות.בקובץ המשאבים ב-XML, מצהירים על רכיבי
<usb-accessory>
של האביזרים שרוצים לסנן. כל<usb-accessory>
יכול לקבל המאפיינים הבאים:manufacturer
model
version
לא מומלץ לסנן לפי
version
. אביזר או שהמכשיר לא תמיד יציין מחרוזת גרסה (בכוונה או בטעות). כשהאפליקציה מצהירה על מאפיין גרסה שלפיו צריך לסנן, והאביזר או המכשיר לא מציין מחרוזת גרסה, זה גורם ל-NullPointerException
גרסאות קודמות של Android. הבעיה הזו נפתרה ב-Android 12.שומרים את קובץ המשאב בספרייה
res/xml/
. השם של קובץ המשאב (ללא הסיומת .xml) צריכה להיות זהה לזו שציינתם רכיב<meta-data>
. הפורמט של קובץ משאב XML מוצג גם בדוגמה שלמטה.
דוגמאות לקובצי מניפסט ולקובצי משאבים
בדוגמה הבאה תוכלו לראות מניפסט לדוגמה ואת קובץ המשאבים התואם שלו:
<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>
במקרה כזה, צריך לשמור את קובץ המשאבים הבא ב-
res/xml/accessory_filter.xml
ומציין שכל אביזר שכולל את
יש לסנן את הדגם, היצרן והגרסה התואמים. האביזר שולח
המאפיין את המכשיר המבוסס על Android:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/> </resources>
עבודה עם אביזרים
כשמשתמשים מחברים אביזרי USB למכשיר מבוסס Android, מערכת Android יכולה לקבוע אם האפליקציה מעוניינת באביזר המחובר. אם כן, אפשר להגדיר תקשורת עם האביזר אם יש צורך. כדי לעשות זאת, האפליקציה צריכה:
- מגלים אביזרים מחוברים באמצעות מסנן Intent שמסננים לפי אביזר אירועים מצורפים או על ידי ספירת אביזרים מחוברים ומציאת האביזרים המתאימים.
- צריך לבקש מהמשתמש הרשאה לתקשר עם האביזר, אם עדיין לא שקיבלתם.
- כדי לתקשר עם האביזר באמצעות קריאה וכתיבה של נתונים בממשק המתאים נקודות קצה (endpoints).
חיפוש אביזר
האפליקציה שלך יכולה לגלות אביזרים באמצעות מסנן Intent כדי לקבל התראה כאשר שהמשתמש מחבר אביזר או על ידי ספירת האביזרים שכבר מחוברים. באמצעות מסנן Intent הוא שימושי אם אתם רוצים לאפשר לאפליקציה לזהות באופן אוטומטי האביזר הרצוי. ספירת האביזרים המחוברים היא שימושית אם רוצים לקבל רשימה של כל האביזרים אביזרים מחוברים או אם האפליקציה לא סוננה לפי Intent.
שימוש במסנן Intent
כדי שהאפליקציה תמצא אביזר USB ספציפי, אפשר לציין מסנן Intent
כדי לסנן לפי ה-Intent 'android.hardware.usb.action.USB_ACCESSORY_ATTACHED
'. יחד
באמצעות מסנן ה-Intent הזה, עליכם לציין קובץ משאבים שמציין את המאפיינים של ה-USB
אביזרים, כמו יצרן, דגם וגרסה.
הדוגמה הבאה מראה איך להצהיר על מסנן Intent:
<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>
הדוגמה הבאה מראה איך להצהיר על קובץ המשאב התואם שמציין את אביזרי ה-USB שמעניינים אתכם:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" /> </resources>
בפעילות שלכם, תוכלו לקבל את הUsbAccessory
שמייצגת
האביזר המצורף מתוך Intent כמו בדוגמה הבאה (עם ספריית התוסף):
Kotlin
val accessory = UsbManager.getAccessory(intent)
Java
UsbAccessory accessory = UsbManager.getAccessory(intent);
או כך (באמצעות ממשקי ה-API של הפלטפורמה):
Kotlin
val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
Java
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
ציון אביזרים
ניתן לבקש מהאפליקציה שלך לספור צירים שזיהו את עצמם בזמן שבהן האפליקציה פועלת.
שימוש בשיטה getAccessoryList()
כדי לקבל מערך את כל אביזרי ה-USB שמחוברים:
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();
הערה: רק אביזר מחובר אחד נתמך בו זמנית.
קבלת הרשאה לתקשורת עם אביזר
לפני התקשורת עם אביזר ה-USB, האפליקציה צריכה לקבל הרשאה משתמשים.
הערה: אם האפליקציה משתמשת מסנן Intent כדי לגלות אביזרים בזמן שהם מחוברים, הוא מקבל באופן אוטומטי הרשאה אם המשתמש מאפשר לאפליקציה לטפל בכוונה. אם לא, צריך לשלוח בקשה הרשאה מפורשת באפליקציה לפני החיבור לאביזר.
ייתכן שיהיה צורך לבקש הרשאה מפורשת במצבים מסוימים, למשל האפליקציה סופרת אביזרים שכבר מחוברים ואחר כך רוצים לתקשר איתם אחת. חובה לבדוק אם יש הרשאת גישה לאביזר לפני שמנסים לתקשר איתו. אם לא, תתקבל שגיאת זמן ריצה אם המשתמש דחה את ההרשאה לגשת אחר.
כדי לקבל הרשאה באופן מפורש, קודם צריך ליצור מקלט שידורים. המקלט הזה מאזין
של הכוונה שמשודרת כשמתקשרים אל requestPermission()
. הקריאה אל requestPermission()
מציגה תיבת דו-שיח
משתמש שמבקש הרשאה להתחבר לאביזר. הקוד לדוגמה הבא מראה איך
יצירת מקלט השידור:
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); } } } } };
כדי לרשום את מקלט השידורים, יש להוסיף את הקוד הזה ל-method 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), 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);
כדי להציג את תיבת הדו-שיח שבה המשתמשים מתבקשים לאשר הרשאה להתחבר לאביזר, קוראים אל
אמצעי תשלום אחד (requestPermission()
):
Kotlin
lateinit var accessory: UsbAccessory ... usbManager.requestPermission(accessory, permissionIntent)
Java
UsbAccessory accessory; ... usbManager.requestPermission(accessory, permissionIntent);
כשמשתמשים משיבים לתיבת הדו-שיח, מקלט השידור מקבל את הכוונה שמכילה את
EXTRA_PERMISSION_GRANTED
נוסף, שהוא ערך בוליאני
שמייצג את התשובה. צריך לבדוק את הערך הנוסף הזה כ-TRUE לפני החיבור אל
אחר.
תקשורת עם אביזר
אפשר לתקשר עם האביזר באמצעות UsbManager
למטרות הבאות:
לקבל מתאר קובץ שאפשר להגדיר בו שידורי קלט ופלט, כדי לקרוא ולכתוב נתונים.
עם תיאור. השידורים מייצגים את נקודות הקצה בכמות גדולה לקלט ולפלט של האביזר. עליך להגדיר
את התקשורת בין המכשיר והאביזר בשרשור אחר, כך שלא תנעלו את
ה-thread הראשי של ממשק המשתמש. הדוגמה הבאה מראה איך לפתוח אביזר כדי לתקשר איתו:
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(); } }
בשיטה run()
של השרשור, אפשר לקרוא ולכתוב לאביזר באמצעות
את האובייקט FileInputStream
או FileOutputStream
. בזמן הקריאה
מאביזר עם אובייקט FileInputStream
, יש לוודא שהמאגר הזמני
שבו אתם משתמשים גדול מספיק כדי לאחסן את נתוני חבילת ה-USB. פרוטוקול האביזר של Android תומך
מאגר נתונים זמני של עד 16,384 בייטים, כך שתוכלו לבחור תמיד להצהיר שהמאגר הזמני הוא כזה
כדי לשמור על פשטות.
הערה: ברמה נמוכה יותר, המנות הן 64 בייטים ל-USB אביזרים במהירות מלאה ו-512 בייטים לאביזרים עם חיבור USB במהירות גבוהה. האביזר של Android פרוטוקול מקבץ את המנות לשתי המהירויות במנה לוגית אחת כדי לפשט את הפשטות.
למידע נוסף על השימוש בשרשורים ב-Android, אפשר לעיין במאמר תהליכים שרשורים.
סיום התקשורת עם אביזר
בסיום התקשורת עם אביזר או אם האביזר נותק, סוגרים את
מתאר קובץ שפתחת על ידי שליחת קריאה ל-close()
.
כדי להאזין לאירועים שמנותקים, יוצרים מקלט שידורים כמו בדוגמה הבאה:
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 } } } };
יצירת מקלט השידור בתוך האפליקציה, ולא במניפסט, מאפשרת אפליקציה שמטפלת באירועים שמנותקים רק בזמן שהיא פועלת. כך, אירועים מנותקים נשלחת רק לאפליקציה שפועלת כרגע ולא משודרת לכל האפליקציות.