عندما يكون جهاز Android في وضع مضيف USB، فإنه يعمل كمضيف USB، ويشغّل الناقل، وتعداد أجهزة USB المتصلة. يتوفر وضع مضيف USB في الإصدار 3.1 من نظام التشغيل Android والإصدارات الأحدث.
نظرة عامة على واجهة برمجة التطبيقات
قبل أن تبدأ، من المهم أن تفهم الفصول التي تحتاج إلى العمل معها. تشير رسالة الأشكال البيانية
يصف الجدول التالي واجهات برمجة التطبيقات لمضيف USB في الحزمة android.hardware.usb
.
الفئة | الوصف |
---|---|
UsbManager |
يسمح لك بتعداد أجهزة USB المتصلة والاتصال بها. |
UsbDevice |
يمثل هذا الجهاز جهاز USB متصلًا ويحتوي على طرق للوصول إلى معلومات تعريفه المعلومات والواجهات والنقاط النهاية. |
UsbInterface |
ويمثل ذلك واجهة جهاز USB، تحدد مجموعة من الوظائف الخاص بك. يمكن أن يحتوي الجهاز على واجهة واحدة أو أكثر للتواصل عليها. |
UsbEndpoint |
نقطة نهاية الواجهة، وهي قناة اتصال لهذه الواجهة إنّ يمكن أن تحتوي على نقطة نهاية واحدة أو أكثر، وعادةً ما تحتوي على نقاط نهاية للإدخال والمخرجات اتصال ثنائي الاتجاه مع الجهاز. |
UsbDeviceConnection |
يمثل الاتصال بالجهاز الذي ينقل البيانات على نقاط النهاية. هذا الصف تتيح لك إرسال البيانات ذهابًا وإيابًا بشكل متزامن أو غير متزامن. |
UsbRequest |
ويمثل طلبًا غير متزامن للاتصال بأحد الأجهزة من خلال UsbDeviceConnection . |
UsbConstants |
تحديد ثوابت USB التي تتوافق مع التعريفات في linux/usb/ch9.h لنظام التشغيل Linux النواة (النواة). |
في معظم الحالات، عليك استخدام جميع هذه الصفوف (يجب استخدام UsbRequest
فقط في حال إجراء اتصال غير متزامن)
عند الاتصال بجهاز USB. بشكل عام، تحصل على UsbManager
لاسترداد UsbDevice
المطلوب.
عندما يكون الجهاز بحوزتك، عليك العثور على UsbInterface
وUsbEndpoint
المناسبَين.
واجهة للتواصل معها. بعد الحصول على نقطة النهاية الصحيحة، افتح UsbDeviceConnection
للاتصال بجهاز USB.
متطلبات بيان Android
توضح القائمة التالية ما يجب إضافته إلى ملف بيان تطبيقك قبل تعمل مع واجهات برمجة تطبيقات مضيف USB:
- وبما أنّه لا يمكننا ضمان توافق جميع الأجهزة التي تعمل بنظام التشغيل Android مع واجهات برمجة التطبيقات لمضيف USB،
تضمين عنصر
<uses-feature>
يذكر أنّ تطبيقك يستخدم ميزةandroid.hardware.usb.host
. - اضبط الحد الأدنى لحزمة تطوير البرامج (SDK) للتطبيق على المستوى 12 أو أعلى لواجهة برمجة التطبيقات. واجهات برمجة التطبيقات لمضيف USB ليست في مستويات واجهة برمجة التطبيقات السابقة.
- إذا كنت تريد أن يتم إشعار تطبيقك بجهاز USB متصل، اختَر
زوج العنصرَين
<intent-filter>
و<meta-data>
من أجلandroid.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 ...> <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 المتصلة باستخدام فلتر الأهداف ليتم إرسال إشعار عندما يكون المستخدم لتوصيل جهاز USB أو عن طريق تعداد أجهزة USB المتصلة بالفعل.
- اطلب من المستخدم إذنًا للاتصال بجهاز USB، إذا لم يكن قد تم الحصول عليه من قبل.
- يمكنك التواصل مع جهاز USB من خلال قراءة البيانات وكتابتها على الواجهة المناسبة. والنقاط النهائية.
اكتشاف جهاز
يمكن لتطبيقك اكتشاف أجهزة USB من خلال استخدام فلتر الأهداف ليتم إشعارك عند توصيل المستخدم بجهاز أو عن طريق تعداد أجهزة USB المتصلة بالفعل. استخدام يُعد فلتر الأهداف مفيدًا إذا كنت تريد أن يتمكن تطبيقك من اكتشاف الجهاز المطلوب. يُعد تعداد أجهزة USB المتصلة مفيدًا إذا كنت تريد الحصول على قائمة بجميع الأجهزة المتصلة أو ما إذا كان تطبيقك لا يفلتر غرضًا.
استخدام فلتر أهداف
لتمكين تطبيقك من اكتشاف جهاز USB معين، يمكنك تحديد فلتر أهداف
فلترًا لهدف android.hardware.usb.action.USB_DEVICE_ATTACHED
. جنبًا إلى جنب مع
فلتر الأهداف هذا، يلزمك تحديد ملف موارد يحدد خصائص USB
الجهاز، مثل معرّف المنتج والمورد. عندما يربط المستخدمون جهازًا يتوافق مع جهازك
سيعرض لهم النظام مربع حوار يسألهم عما إذا كانوا يريدون بدء تشغيل التطبيق أم لا.
إذا وافق المستخدمون، سيحصل تطبيقك تلقائيًا على إذن بالوصول إلى الجهاز حتى
جهازك غير متصل.
يوضّح المثال التالي كيفية تعريف فلتر الأهداف:
<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
الذي يمثّل
الجهاز المتصل من الغرض كما يلي:
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، يجب أن يحصل التطبيق على إذن من المستخدمين.
ملاحظة: إذا كان تطبيقك يستخدم فلتر الأهداف لاكتشاف أجهزة USB أثناء ربطها، يتلقّى التطبيق تلقائيًا إذا كان المستخدم يسمح لتطبيقك بمعالجة النية. إذا لم يكن الأمر كذلك، عليك طلب بشكل صريح في تطبيقك قبل الاتصال بالجهاز.
وقد يكون طلب الإذن صراحةً ضروريًا في بعض المواقف، مثل عندما يعدد التطبيق أجهزة USB المتصلة بالفعل ثم يريد الاتصال واحد. يجب التحقّق من إذن الوصول إلى أي جهاز قبل محاولة الاتصال به. في حال حذف لا، ستتلقى خطأ وقت التشغيل إذا رفض المستخدم منح إذن الوصول إلى الجهاز.
للحصول على الإذن صراحةً، يجب أولاً إنشاء جهاز استقبال بث. يستمع هذا الجهاز إلى
النية التي يتم بثها عند الاتصال بـ 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 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); } } } } };
لتسجيل مستقبِل البث، يُرجى إضافة هذا الإعداد في طريقة 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);
عندما يرد المستخدمون على مربّع الحوار، يتلقّى مستقبل البث الغرض الذي يحتوي على
EXTRA_PERMISSION_GRANTED
إضافية، وهي قيمة منطقية
تمثل الإجابة. تحقق من هذه القيمة الإضافية للتأكد من أنها على القيمة true قبل الاتصال
الخاص بك.
الاتصال بجهاز
يمكن أن يكون الاتصال بجهاز USB متزامنًا أو غير متزامن. في كلتا الحالتين،
إنشاء سلسلة محادثات جديدة لتنفيذ جميع عمليات نقل البيانات عليها، وبالتالي لا تحظر
سلسلة واجهة المستخدم لإعداد الاتصال بأحد الأجهزة بشكل صحيح، تحتاج إلى الحصول على
UsbInterface
وUsbEndpoint
من إجمالي
الذي تريد التواصل عليه وإرسال طلبات إلى نقطة النهاية هذه باستخدام UsbDeviceConnection
. بشكل عام، يجب أن تستوفي التعليمة البرمجية الشروط التالية:
- تحقَّق من سمات عنصر
UsbDevice
، مثل معرّف المنتج أو معرّف البائع أو فئة الجهاز لمعرفة ما إذا كنت تريد التواصل مع الخاص بك. - عندما تكون متأكدًا من أنك تريد الاتصال بالجهاز، ابحث عن
UsbInterface
التي تريد استخدامها للتواصل معUsbEndpoint
المناسبة لتلك الواجهة. يمكن أن تحتوي الواجهات على واحدة أو أكثر من نقاط نهاية، وعادةً ما يكون لها نقطة نهاية مدخلات ومخرجات ثنائية الاتجاه التواصل. - عند العثور على نقطة النهاية الصحيحة، افتح
UsbDeviceConnection
. على نقطة النهاية هذه. - أدخِل البيانات التي تريد نقلها على نقطة النهاية باستخدام طريقة
bulkTransfer()
أوcontrolTransfer()
. عليك تنفيذ هذه الخطوة في سلسلة محادثات أخرى لمنع حظر سلسلة واجهة المستخدم الرئيسية. لمزيد من المعلومات، معلومات حول استخدام سلاسل المحادثات في Android، راجِع العمليات سلاسل المحادثات:
مقتطف الرمز التالي هو طريقة بسيطة لنقل البيانات بشكل متزامن. الرمز الخاص بك يجب أن يكون لديه مزيد من المنطق للعثور على الواجهة ونقاط النهاية الصحيحة للاتصال بها بشكل صحيح ويجب أيضًا تنفيذ أي عملية نقل للبيانات في سلسلة محادثات تختلف عن سلسلة واجهة المستخدم الرئيسية:
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()
للاستماع إلى الأحداث المنفصلة،
إنشاء مستقبل بث كما هو موضّح أدناه:
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 } } } };
يتيح إنشاء مستقبِل البث داخل التطبيق، وليس في البيان، معالجة الأحداث المنفصلة فقط أثناء تشغيله. بهذه الطريقة، يتم دمج الأحداث يتم إرساله فقط إلى التطبيق قيد التشغيل حاليًا ولا يتم بثه إلى جميع التطبيقات.