نمای کلی لوازم جانبی USB

حالت لوازم جانبی USB به کاربران اجازه می‌دهد تا سخت‌افزار میزبان USB را که به‌طور خاص برای دستگاه‌های مجهز به Android طراحی شده‌اند، متصل کنند. لوازم جانبی باید به پروتکل لوازم جانبی Android که در مستندات کیت توسعه لوازم جانبی Android آمده است، پایبند باشند. این به دستگاه های مجهز به Android که نمی توانند به عنوان میزبان USB عمل کنند، اجازه می دهد همچنان با سخت افزار USB تعامل داشته باشند. وقتی یک دستگاه مجهز به Android در حالت لوازم جانبی USB است، لوازم جانبی USB Android متصل به عنوان میزبان عمل می‌کند، برق گذرگاه USB را تأمین می‌کند و دستگاه‌های متصل را شمارش می‌کند. Android 3.1 (سطح API 12) از حالت لوازم جانبی USB پشتیبانی می کند و این ویژگی همچنین به Android 2.3.4 (سطح API 10) پشتیبان داده شده است تا پشتیبانی از طیف وسیع تری از دستگاه ها را فعال کند.

API لوازم جانبی USB مناسب را انتخاب کنید

اگرچه APIهای لوازم جانبی USB در Android 3.1 به پلتفرم معرفی شدند، اما با استفاده از کتابخانه الحاقی Google APIs در Android 2.3.4 نیز در دسترس هستند. از آنجایی که این APIها با استفاده از یک کتابخانه خارجی بکپورت شده اند، دو بسته وجود دارد که می توانید آنها را برای پشتیبانی از حالت لوازم جانبی USB وارد کنید. بسته به اینکه از چه دستگاه‌های مجهز به Android می‌خواهید پشتیبانی کنید، ممکن است مجبور باشید از یکی بر دیگری استفاده کنید:

  • com.android.future.usb : برای پشتیبانی از حالت لوازم جانبی USB در Android نسخه 2.3.4، کتابخانه افزونه Google APIs شامل APIهای لوازم جانبی USB پشتیبان‌شده است و آنها در این فضای نام قرار دارند. Android 3.1 همچنین از وارد کردن و فراخوانی کلاس‌ها در این فضای نام برای پشتیبانی از برنامه‌های نوشته شده با کتابخانه افزودنی پشتیبانی می‌کند. این کتابخانه افزودنی یک پوشش نازک در اطراف APIهای جانبی android.hardware.usb است و از حالت میزبان USB پشتیبانی نمی کند. اگر می‌خواهید از وسیع‌ترین طیف دستگاه‌هایی که از حالت لوازم جانبی USB پشتیبانی می‌کنند پشتیبانی کنید، از کتابخانه افزونه استفاده کنید و این بسته را وارد کنید. توجه به این نکته ضروری است که همه دستگاه‌های Android 2.3.4 نیازی به پشتیبانی از ویژگی لوازم جانبی USB ندارند. هر سازنده دستگاه تصمیم می گیرد که از این قابلیت پشتیبانی کند یا نه، به همین دلیل است که باید آن را در فایل مانیفست خود اعلام کنید.
  • android.hardware.usb : این فضای نام شامل کلاس هایی است که از حالت جانبی USB در اندروید 3.1 پشتیبانی می کنند. این بسته به عنوان بخشی از APIهای چارچوب گنجانده شده است، بنابراین Android 3.1 از حالت لوازم جانبی USB بدون استفاده از کتابخانه افزودنی پشتیبانی می کند. اگر فقط به دستگاه‌های Android نسخه 3.1 یا جدیدتر که پشتیبانی سخت‌افزاری از حالت لوازم جانبی USB دارند، که می‌توانید در فایل مانیفست خود اعلام کنید، از این بسته استفاده کنید.

کتابخانه افزونه Google APIs را نصب کنید

اگر می‌خواهید افزونه را نصب کنید، می‌توانید با نصب بسته Google APIs Android API 10 با مدیر SDK این کار را انجام دهید. برای اطلاعات بیشتر در مورد نصب کتابخانه افزونه به نصب افزونه Google APIs مراجعه کنید.

نمای کلی API

از آنجایی که کتابخانه افزونه پوششی برای APIهای چارچوب است، کلاس هایی که از ویژگی لوازم جانبی USB پشتیبانی می کنند مشابه هستند. حتی اگر از کتابخانه افزونه استفاده می کنید، می توانید از اسناد مرجع برای android.hardware.usb استفاده کنید.

توجه: با این حال، تفاوت استفاده جزئی بین کتابخانه افزودنی و APIهای چارچوب وجود دارد که باید از آن آگاه باشید.

جدول زیر کلاس هایی را توضیح می دهد که از API های جانبی USB پشتیبانی می کنند:

کلاس توضیحات
UsbManager به شما امکان می دهد لوازم جانبی USB متصل را برشمارید و با آنها ارتباط برقرار کنید.
UsbAccessory نشان دهنده یک لوازم جانبی USB و حاوی روش هایی برای دسترسی به اطلاعات شناسایی آن است.

تفاوت استفاده بین کتابخانه افزودنی و APIهای پلتفرم

دو تفاوت استفاده بین استفاده از کتابخانه افزودنی Google APIs و APIهای پلتفرم وجود دارد.

اگر از کتابخانه افزونه استفاده می کنید، باید شی UsbManager را به روش زیر دریافت کنید:

کاتلین

val manager = UsbManager.getInstance(this)

جاوا

UsbManager manager = UsbManager.getInstance(this);

اگر از کتابخانه افزونه استفاده نمی کنید، باید شی UsbManager را به روش زیر دریافت کنید:

کاتلین

val manager = getSystemService(Context.USB_SERVICE) as UsbManager

جاوا

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);

هنگامی که یک وسیله جانبی متصل را با فیلتر هدف فیلتر می کنید، شی UsbAccessory در داخل intent قرار می گیرد که به برنامه شما ارسال می شود. اگر از کتابخانه افزونه استفاده می کنید، باید شی UsbAccessory را به روش زیر دریافت کنید:

کاتلین

val accessory = UsbManager.getAccessory(intent)

جاوا

UsbAccessory accessory = UsbManager.getAccessory(intent);

اگر از کتابخانه افزونه استفاده نمی کنید، باید شی UsbAccessory را به روش زیر دریافت کنید:

کاتلین

val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory

جاوا

UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

الزامات مانیفست اندروید

لیست زیر آنچه را که باید قبل از کار با APIهای لوازم جانبی USB به فایل مانیفست برنامه خود اضافه کنید، توضیح می دهد. مثال‌های فایل مانیفست و منبع نشان می‌دهد که چگونه می‌توان این موارد را اعلام کرد:

  • از آنجایی که همه دستگاه‌های مجهز به Android تضمینی برای پشتیبانی از API لوازم جانبی USB ندارند، یک عنصر <uses-feature> را اضافه کنید که اعلام می‌کند برنامه شما از ویژگی android.hardware.usb.accessory استفاده می‌کند.
  • اگر از کتابخانه افزونه استفاده می کنید، عنصر <uses-library> را اضافه کنید که com.android.future.usb.accessory را برای کتابخانه مشخص می کند.
  • اگر از کتابخانه افزونه استفاده می کنید، حداقل SDK برنامه را روی API Level 10 یا اگر از بسته android.hardware.usb استفاده می کنید، 12 تنظیم کنید.
  • اگر می‌خواهید برنامه شما از یک وسیله جانبی 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 می شود. این مشکل در اندروید 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 می تواند تعیین کند که آیا برنامه شما به لوازم جانبی متصل شده علاقه مند است یا خیر. در این صورت، در صورت تمایل می توانید با لوازم جانبی ارتباط برقرار کنید. برای انجام این کار، برنامه شما باید:

  1. لوازم جانبی متصل را با استفاده از فیلتر هدف که رویدادهای جانبی پیوست شده را فیلتر می کند یا با برشمردن لوازم جانبی متصل و یافتن مورد مناسب، کشف کنید.
  2. از کاربر اجازه بخواهید که با لوازم جانبی ارتباط برقرار کند، اگر قبلاً آن را دریافت نکرده اید.
  3. با خواندن و نوشتن داده ها در نقاط انتهایی رابط مناسب با لوازم جانبی ارتباط برقرار کنید.

یک وسیله جانبی را کشف کنید

برنامه شما می‌تواند لوازم جانبی را با استفاده از فیلتر هدف کشف کند تا هنگام اتصال کاربر یک وسیله جانبی مطلع شود یا با برشمردن لوازم جانبی که قبلاً متصل شده‌اند. اگر می خواهید بتوانید به طور خودکار یک وسیله جانبی مورد نظر را شناسایی کند، استفاده از فیلتر هدف مفید است. اگر می‌خواهید فهرستی از همه لوازم جانبی متصل را دریافت کنید یا اگر برنامه شما برای یک هدف فیلتر نشده است، شمارش لوازم جانبی متصل مفید است.

از فیلتر قصد استفاده کنید

برای اینکه برنامه شما یک لوازم جانبی USB خاص را پیدا کند، می توانید یک فیلتر هدف برای فیلتر کردن هدف android.hardware.usb.action.USB_ACCESSORY_ATTACHED مشخص کنید. همراه با این فیلتر قصد، باید فایل منبعی را مشخص کنید که ویژگی های لوازم جانبی USB مانند سازنده، مدل و نسخه را مشخص کند.

مثال زیر نحوه اعلان فیلتر قصد را نشان می دهد:

<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 که نشان‌دهنده لوازم جانبی پیوست شده است، از این هدف (با کتابخانه افزودنی) دریافت کنید:

کاتلین

val accessory = UsbManager.getAccessory(intent)

جاوا

UsbAccessory accessory = UsbManager.getAccessory(intent);

یا مانند این (با APIهای پلتفرم):

کاتلین

val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory

جاوا

UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

لوازم جانبی را برشمارید

می توانید از برنامه خود بخواهید لوازم جانبی را که در حین اجرای برنامه شما شناسایی شده اند، برشمرد.

از متد getAccessoryList() برای بدست آوردن آرایه ای از تمام لوازم جانبی USB که متصل هستند استفاده کنید:

کاتلین

val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val accessoryList: Array<out UsbAccessory> = manager.accessoryList

جاوا

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessoryList = manager.getAccessoryList();

توجه: هر بار فقط یک وسیله جانبی متصل پشتیبانی می شود.

برای برقراری ارتباط با لوازم جانبی اجازه بگیرید

قبل از برقراری ارتباط با لوازم جانبی USB، برنامه شما باید از کاربران شما اجازه داشته باشد.

توجه: اگر برنامه شما از فیلتر هدف برای کشف لوازم جانبی در حین اتصال آنها استفاده می کند ، اگر کاربر به برنامه شما اجازه دهد که هدف را مدیریت کند، به طور خودکار مجوز دریافت می کند. در غیر این صورت، قبل از اتصال به لوازم جانبی باید صراحتاً در برنامه خود مجوز درخواست کنید.

درخواست صراحتاً برای اجازه ممکن است در برخی شرایط ضروری باشد، مانند زمانی که برنامه شما لوازم جانبی را که قبلاً متصل هستند و سپس می‌خواهد با یکی از آنها ارتباط برقرار کند، فهرست می‌کند. قبل از تلاش برای برقراری ارتباط با لوازم جانبی، باید مجوز دسترسی به لوازم جانبی را بررسی کنید. در غیر این صورت، اگر کاربر اجازه دسترسی به لوازم جانبی را رد کند، یک خطای زمان اجرا دریافت خواهید کرد.

برای دریافت صریح مجوز، ابتدا یک گیرنده پخش ایجاد کنید. این گیرنده هنگام فراخوانی requestPermission() به هدفی که پخش می شود گوش می دهد. فراخوانی به requestPermission() یک دیالوگ را به کاربر نمایش می دهد که از کاربر اجازه می خواهد تا به لوازم جانبی متصل شود. کد نمونه زیر نحوه ایجاد گیرنده پخش را نشان می دهد:

کاتلین

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")
                }
            }
        }
    }
}

جاوا

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);
                }
            }
        }
    }
};

برای ثبت گیرنده پخش، این را در متد onCreate() در فعالیت خود قرار دهید:

کاتلین

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)

جاوا

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() را فراخوانی کنید:

کاتلین

lateinit var accessory: UsbAccessory
...
usbManager.requestPermission(accessory, permissionIntent)

جاوا

UsbAccessory accessory;
...
usbManager.requestPermission(accessory, permissionIntent);

وقتی کاربران به گفتگو پاسخ می‌دهند، گیرنده پخش شما هدفی را دریافت می‌کند که حاوی EXTRA_PERMISSION_GRANTED اضافی است، که یک بولی نشان‌دهنده پاسخ است. قبل از اتصال به لوازم جانبی، این مقدار اضافی را برای مقدار true بررسی کنید.

با لوازم جانبی ارتباط برقرار کنید

می توانید با استفاده از UsbManager با ابزار جانبی ارتباط برقرار کنید تا یک توصیفگر فایل به دست آورید که می توانید جریان های ورودی و خروجی را برای خواندن و نوشتن داده ها در توصیفگر تنظیم کنید. استریم ها نشان دهنده نقاط پایانی حجیم ورودی و خروجی لوازم جانبی هستند. باید ارتباط بین دستگاه و لوازم جانبی را در رشته دیگری تنظیم کنید تا رشته رابط کاربری اصلی را قفل نکنید. مثال زیر نحوه باز کردن لوازم جانبی برای برقراری ارتباط را نشان می دهد:

کاتلین

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()
    }
}

جاوا

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() thread، می‌توانید با استفاده از اشیاء FileInputStream یا FileOutputStream لوازم جانبی را بخوانید و بنویسید. هنگام خواندن داده ها از لوازم جانبی با یک شی FileInputStream ، مطمئن شوید که بافری که استفاده می کنید به اندازه کافی بزرگ باشد تا داده های بسته USB را ذخیره کند. پروتکل لوازم جانبی اندروید از بافرهای بسته تا 16384 بایت پشتیبانی می‌کند، بنابراین می‌توانید برای سادگی، همیشه بافر خود را به این اندازه اعلام کنید.

توجه: در سطح پایین تر، بسته ها 64 بایت برای لوازم جانبی USB با سرعت کامل و 512 بایت برای لوازم جانبی USB با سرعت بالا هستند. پروتکل لوازم جانبی اندروید بسته ها را برای هر دو سرعت در یک بسته منطقی برای سادگی جمع می کند.

برای اطلاعات بیشتر در مورد استفاده از رشته ها در Android، به فرآیندها و موضوعات مراجعه کنید.

ارتباط با لوازم جانبی را قطع کنید

هنگامی که برقراری ارتباط با یک لوازم جانبی تمام شد یا اگر لوازم جانبی جدا شد، توصیفگر فایلی را که باز کرده‌اید با فراخوانی close() ببندید. برای گوش دادن به رویدادهای جدا، یک گیرنده پخش مانند زیر ایجاد کنید:

کاتلین

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
            }
        }
    }
}

جاوا

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
            }
        }
    }
};

ایجاد گیرنده پخش در برنامه، و نه مانیفست، به برنامه شما اجازه می‌دهد فقط رویدادهای جدا شده را در حین اجرا مدیریت کند. به این ترتیب، رویدادهای جدا شده تنها به برنامه‌ای ارسال می‌شوند که در حال حاضر در حال اجرا است و برای همه برنامه‌ها پخش نمی‌شود.