Integrating Serial Port, USB, Printers, and Input Devices in Android Applications
This article provides a comprehensive guide for Android developers on how to connect and use serial ports, USB peripherals, printers, barcode scanners, payment boxes, keyboards, and mice, including required permissions, configuration steps, and sample Kotlin/Java code snippets for each device type.
In Android embedded device development, developers often need to interface with serial ports, USB devices, printers, barcode scanners, payment boxes, keyboards, and mice. This guide outlines the essential steps and code examples for each scenario.
Serial Port Integration
Download the serial port library (containing libprt_serial_port.so ) and use the provided SerialPort class. Example Kotlin code demonstrates opening the device, obtaining input/output streams, and handling I/O:
public class SerialPort {
private static final String TAG = "SerialPort";
private FileDescriptor mFd;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
mFd = open(device.getAbsolutePath(), baudrate, flags);
if (mFd == null) {
Log.e(TAG, "native open returns null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}
public InputStream getInputStream() { return mFileInputStream; }
public OutputStream getOutputStream() { return mFileOutputStream; }
private native static FileDescriptor open(String path, int baudrate, int flags);
public native void close();
static { System.loadLibrary("serial_port"); }
}To use the port, configure the device path (e.g., /dev/ttyS4 ) and baud rate (e.g., 9600), then read/write via the streams:
val serialPort = SerialPort(File("/dev/ttyS4"), 9600, 0)
val inputStream = serialPort.inputStream
val outputStream = serialPort.outputStream
val length = inputStream!!.available()
val bytes = ByteArray(length)
inputStream.read(bytes)USB Integration
Add the necessary permissions and features to AndroidManifest.xml :
<uses-permission android:name="android.permission.USB_PERMISSION" />
<uses-feature android:name="android.hardware.usb.host" />
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />Prevent activity recreation on USB plug/unplug by adding android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation" to the activity declaration.
Example Kotlin code shows how to obtain the UsbManager , request permission, detect attached devices, and connect to a USB printer:
mUsbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
mPermissionIntent = PendingIntent.getBroadcast(context, 0, Intent(ACTION_USB_PERMISSION), 0)
val filter = IntentFilter(USBPrinter.ACTION_USB_PERMISSION)
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
context.registerReceiver(mUsbDeviceReceiver, filter)
setUsbDevices()
private fun setUsbDevices() {
mUsbManager?.deviceList?.let {
for (device in it.values) {
val usbInterface = device.getInterface(0)
if (usbInterface.interfaceClass == 7) { // USB printer class
if (!mUsbManager!!.hasPermission(device)) {
mUsbManager!!.requestPermission(device, mPermissionIntent)
} else {
connectUsbPrinter(device)
}
break
}
}
}
}
private val mUsbDeviceReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (ACTION_USB_PERMISSION == action) {
synchronized(this) {
val usbDevice = intent.getParcelableExtra
(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
mUsbDevice = usbDevice
if (mUsbDevice != null) connectUsbPrinter(mUsbDevice)
} else {
WLog.e(this, "Permission denied for device $usbDevice")
}
}
} else if (UsbManager.ACTION_USB_DEVICE_ATTACHED == action) {
// device attached
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED == action) {
if (mUsbDevice != null) {
WLog.e(this, "Device closed")
mUsbDeviceConnection?.close()
}
}
}
}After obtaining the UsbEndpoint and UsbInterface , data can be sent to the printer using bulkTransfer :
fun write(bytes: ByteArray) {
if (mUsbDeviceConnection != null) {
try {
mUsbDeviceConnection!!.claimInterface(usbInterface, true)
val result = mUsbDeviceConnection!!.bulkTransfer(printerEp, bytes, bytes.size, USBPrinter.TIME_OUT)
mUsbDeviceConnection!!.releaseInterface(usbInterface)
} catch (e: Exception) {
e.printStackTrace()
}
}
}Printer Command Set
Common ESC/POS commands are provided as static Java methods, e.g., initializing the printer, setting alignment, and cutting paper:
public static byte[] init_printer() {
byte[] result = new byte[2];
result[0] = ESC;
result[1] = 0x40;
return result;
}
public static byte[] alignLeft() {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 97;
result[2] = 0;
return result;
}
public static byte[] cutter() {
byte[] box = new byte[6];
box[0] = 0x1B;
box[1] = 0x64;
box[2] = 0x01;
box[3] = 0x1d;
box[4] = 0x56;
box[5] = 0x31;
return box;
}Printing text is done by converting the string to GBK bytes and sending them via the write method.
fun printText(msg: String) {
try {
write(msg.toByteArray(charset("gbk")))
} catch (e: UnsupportedEncodingException) {
e.printStackTrace()
}
}Bitmap printing uses the raster command sequence:
public static byte[] printBitmap(Bitmap bitmap) {
byte[] header = new byte[]{GS, 0x76, 0x30, 0x00};
byte[] data = getBytesFromBitMap(bitmap);
return byteMerger(header, data);
}Barcode Scanner, Payment Box, Keyboard, and Mouse
These devices are also USB HID peripherals. They can be handled either via broadcast receivers (e.g., registering a SCAN_ACTION intent) or by intercepting key events in an activity:
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (KeyUtils.doNotSwitchViewPagerByKey(keyCode)) return true
scanHelpL.get().acceptKey(this, keyCode) { viewModel.scanByBarcode(it) }
return super.onKeyDown(keyCode, event)
}The utility class KeyUtils maps key codes to characters, handling shift states and special keys. When the Enter key is detected, the accumulated barcode string is submitted for further processing.
It is important to avoid focusing editable UI components (e.g., EditText ) when using these input devices, as they would automatically capture the scanned data.
Conclusion
The article demonstrates how Android developers can quickly integrate serial ports, USB peripherals, printers, barcode scanners, payment boxes, keyboards, and mice into their applications, providing code snippets and configuration details. Future articles will cover Bluetooth devices, multi‑screen handling, and more advanced scenarios.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.