android-sunflower中的Compose与USB设备:USB Host模式
你是否在开发Android应用时遇到过需要连接USB设备的场景?是否想知道如何在基于Jetpack Compose的应用中实现USB Host模式功能?本文将以android-sunflower项目为例,详细介绍如何在Compose架构中集成USB设备通信功能,让你的应用轻松与外部硬件交互。读完本文后,你将能够:- 了解Android USB Host模式的基本概念和工作原理- 掌握在Je...
android-sunflower中的Compose与USB设备:USB Host模式
你是否在开发Android应用时遇到过需要连接USB设备的场景?是否想知道如何在基于Jetpack Compose的应用中实现USB Host模式功能?本文将以android-sunflower项目为例,详细介绍如何在Compose架构中集成USB设备通信功能,让你的应用轻松与外部硬件交互。
读完本文后,你将能够:
- 了解Android USB Host模式的基本概念和工作原理
- 掌握在Jetpack Compose中检测和连接USB设备的方法
- 学会在MVVM架构中设计USB设备通信模块
- 实现Compose界面与USB设备的数据交互
USB Host模式简介
USB Host模式允许Android设备充当USB主机,为连接的USB设备提供电力并与之通信。这在需要与外部硬件设备交互的应用中非常有用,比如园艺监测设备、传感器等。
在AndroidManifest.xml中声明USB Host权限是使用该功能的第一步:
<uses-permission android:name="android.hardware.usb.host" />
<uses-feature android:name="android.hardware.usb.host" android:required="true" />
android-sunflower项目的清单文件位于app/src/main/AndroidManifest.xml,你需要在这里添加上述权限声明。
项目架构概览
android-sunflower项目展示了如何将基于View的应用迁移到Jetpack Compose。其架构遵循MVVM模式,主要分为以下几个模块:
- 数据层:处理数据存储和网络请求,如app/src/main/java/com/google/samples/apps/sunflower/data/目录下的数据库和仓库类
- 视图模型层:管理UI数据和业务逻辑,位于app/src/main/java/com/google/samples/apps/sunflower/viewmodels/
- 界面层:使用Jetpack Compose构建的UI组件,如app/src/main/java/com/google/samples/apps/sunflower/compose/目录下的各个屏幕
USB设备检测与权限请求
要在Compose应用中使用USB设备,首先需要检测连接的USB设备并请求用户授权。以下是实现这一功能的关键步骤:
- 创建一个USB设备接收器,监听USB设备的连接和断开事件:
class UsbDeviceReceiver(
private val onDeviceConnected: (UsbDevice) -> Unit,
private val onDeviceDisconnected: (UsbDevice) -> Unit
) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
val device = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE)
device?.let { onDeviceConnected(it) }
}
UsbManager.ACTION_USB_DEVICE_DETACHED -> {
val device = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE)
device?.let { onDeviceDisconnected(it) }
}
}
}
}
- 在ViewModel中注册接收器并管理USB设备列表:
class UsbViewModel(private val usbManager: UsbManager) : ViewModel() {
private val _connectedDevices = MutableStateFlow<List<UsbDevice>>(emptyList())
val connectedDevices: StateFlow<List<UsbDevice>> = _connectedDevices
private lateinit var usbReceiver: UsbDeviceReceiver
fun initialize(context: Context) {
usbReceiver = UsbDeviceReceiver(
onDeviceConnected = { device ->
_connectedDevices.update { it + device }
requestPermission(context, device)
},
onDeviceDisconnected = { device ->
_connectedDevices.update { it - device }
}
)
val filter = IntentFilter().apply {
addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
}
context.registerReceiver(usbReceiver, filter)
}
private fun requestPermission(context: Context, device: UsbDevice) {
val permissionIntent = PendingIntent.getBroadcast(
context, 0, Intent(ACTION_USB_PERMISSION),
PendingIntent.FLAG_IMMUTABLE
)
usbManager.requestPermission(device, permissionIntent)
}
override fun onCleared() {
super.onCleared()
context.unregisterReceiver(usbReceiver)
}
companion object {
const val ACTION_USB_PERMISSION = "com.google.samples.apps.sunflower.USB_PERMISSION"
}
}
Compose界面集成USB功能
在android-sunflower项目中,我们可以在现有的Compose界面中添加USB设备管理功能。例如,在app/src/main/java/com/google/samples/apps/sunflower/compose/garden/GardenScreen.kt中添加一个USB设备状态显示区域:
@Composable
fun GardenScreen(
viewModel: GardenPlantingListViewModel,
navigateToPlantDetail: (String) -> Unit,
usbViewModel: UsbViewModel = viewModel()
) {
val gardenPlantings by viewModel.gardenPlantings.observeAsState(emptyList())
val connectedDevices by usbViewModel.connectedDevices.collectAsState()
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringResource(id = R.string.my_garden)) },
actions = {
UsbDeviceStatusIcon(connectedDevices.isNotEmpty())
}
)
}
) { innerPadding ->
val modifier = Modifier.padding(innerPadding)
if (connectedDevices.isNotEmpty()) {
UsbDeviceList(
devices = connectedDevices,
onDeviceSelected = { device ->
// 处理设备选择
}
)
}
GardenContent(
gardenPlantings = gardenPlantings,
onAddPlantClick = { navigateToPlantDetail("") },
onPlantClick = { plant -> navigateToPlantDetail(plant.plantId) },
modifier = modifier
)
}
}
@Composable
fun UsbDeviceStatusIcon(isConnected: Boolean) {
Icon(
imageVector = if (isConnected) Icons.Filled.Usb else Icons.Outlined.Usb,
contentDescription = stringResource(R.string.usb_status),
tint = if (isConnected) Color.Green else Color.Gray
)
}
@Composable
fun UsbDeviceList(
devices: List<UsbDevice>,
onDeviceSelected: (UsbDevice) -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(Color.LightGray.copy(alpha = 0.5f))
.padding(8.dp)
) {
Text(
text = stringResource(R.string.connected_usb_devices),
style = MaterialTheme.typography.subtitle1,
modifier = Modifier.padding(bottom = 4.dp)
)
devices.forEach { device ->
UsbDeviceItem(device, onDeviceSelected)
}
}
}
@Composable
fun UsbDeviceItem(
device: UsbDevice,
onDeviceSelected: (UsbDevice) -> Unit
) {
TextButton(onClick = { onDeviceSelected(device) }) {
Text(
text = "USB Device: ${device.deviceName}",
modifier = Modifier.fillMaxWidth()
)
}
}
USB数据通信实现
连接USB设备后,下一步是实现数据通信。我们可以创建一个UsbCommunicator类来处理与USB设备的数据交换:
class UsbCommunicator(
private val usbManager: UsbManager,
private val device: UsbDevice
) {
private var connection: UsbDeviceConnection? = null
private var endpoint: UsbEndpoint? = null
fun connect(): Boolean {
val interfaceCount = device.interfaceCount
for (i in 0 until interfaceCount) {
val usbInterface = device.getInterface(i)
for (j in 0 until usbInterface.endpointCount) {
val ep = usbInterface.getEndpoint(j)
if (ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.direction == UsbConstants.USB_DIR_IN) {
endpoint = ep
break
}
}
}
if (endpoint != null) {
connection = usbManager.openDevice(device)
if (connection != null) {
connection?.claimInterface(usbInterface, true)
return true
}
}
}
return false
}
fun readData(): ByteArray? {
endpoint?.let { ep ->
val buffer = ByteArray(ep.maxPacketSize)
val bytesRead = connection?.bulkTransfer(ep, buffer, buffer.size, 1000)
return if (bytesRead > 0) buffer.copyOf(bytesRead) else null
}
return null
}
fun writeData(data: ByteArray): Int {
endpoint?.let { ep ->
val outEndpoint = ep
return connection?.bulkTransfer(outEndpoint, data, data.size, 1000) ?: -1
}
return -1
}
fun disconnect() {
connection?.close()
}
}
完整的USB通信流程
下面是在android-sunflower项目中实现USB Host功能的完整流程:
- 在AndroidManifest.xml中添加USB Host权限和特性声明:
<uses-permission android:name="android.hardware.usb.host" />
<uses-feature android:name="android.hardware.usb.host" />
<activity
android:name=".GardenActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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/usb_device_filter" />
</activity>
- 创建USB设备过滤器xml/usb_device_filter.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="1234" product-id="5678" />
</resources>
- 在Application类中初始化USB相关组件:
class MainApplication : Application() {
lateinit var usbManager: UsbManager
override fun onCreate() {
super.onCreate()
usbManager = getSystemService(Context.USB_SERVICE) as UsbManager
}
}
- 在ViewModel中集成USB通信逻辑:
class PlantDetailViewModel(
plantRepository: PlantRepository,
private val usbManager: UsbManager,
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val plantId: String = savedStateHandle.get<String>(PLANT_ID_ARG)!!
val plant: LiveData<Plant?> = plantRepository.getPlant(plantId)
private var usbCommunicator: UsbCommunicator? = null
private val _usbData = MutableStateFlow<ByteArray?>(null)
val usbData: StateFlow<ByteArray?> = _usbData
fun connectToDevice(device: UsbDevice) {
if (usbCommunicator?.isConnected != true) {
usbCommunicator = UsbCommunicator(usbManager, device).apply {
if (connect()) {
startDataListening()
}
}
}
}
private fun startDataListening() {
viewModelScope.launch(Dispatchers.IO) {
while (isActive) {
val data = usbCommunicator?.readData()
data?.let {
_usbData.value = it
}
delay(100)
}
}
}
fun sendDataToDevice(data: ByteArray) {
viewModelScope.launch(Dispatchers.IO) {
usbCommunicator?.writeData(data)
}
}
override fun onCleared() {
super.onCleared()
usbCommunicator?.disconnect()
}
companion object {
const val PLANT_ID_ARG = "plantId"
}
}
- 在植物详情界面添加USB数据显示和控制功能:
@Composable
fun PlantDetailView(
plant: Plant,
viewModel: PlantDetailViewModel,
modifier: Modifier = Modifier
) {
val usbData by viewModel.usbData.collectAsState()
ScrollableColumn(modifier = modifier) {
PlantHeader(plant)
PlantDetails(plant)
if (usbData != null) {
UsbDataDisplay(data = usbData!!)
}
UsbControlPanel(
onSendCommand = { command ->
viewModel.sendDataToDevice(command.toByteArray())
}
)
}
}
@Composable
fun UsbDataDisplay(data: ByteArray) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
elevation = 4.dp
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = stringResource(R.string.usb_data_received),
style = MaterialTheme.typography.subtitle1,
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = data.toString(Charsets.UTF_8),
style = MaterialTheme.typography.body1,
backgroundColor = Color.Black,
color = Color.Green,
fontFamily = FontFamily.Monospace,
modifier = Modifier.padding(8.dp)
)
}
}
}
@Composable
fun UsbControlPanel(onSendCommand: (String) -> Unit) {
var commandText by remember { mutableStateOf("") }
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
elevation = 4.dp
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = stringResource(R.string.usb_control_panel),
style = MaterialTheme.typography.subtitle1,
modifier = Modifier.padding(bottom = 8.dp)
)
OutlinedTextField(
value = commandText,
onValueChange = { commandText = it },
label = { Text(stringResource(R.string.command)) },
modifier = Modifier.fillMaxWidth()
)
Button(
onClick = { onSendCommand(commandText) },
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp)
) {
Text(stringResource(R.string.send_command))
}
}
}
}
总结与展望
通过本文的介绍,我们了解了如何在android-sunflower项目中集成USB Host模式功能。从设备检测、权限请求到数据通信,我们一步步实现了完整的USB设备交互流程,并将其与Jetpack Compose架构无缝结合。
关键要点回顾:
- USB Host模式允许Android设备作为主机与外部USB设备通信
- 在Compose中可以通过State和Flow实现USB设备状态的响应式更新
- MVVM架构为USB通信功能提供了清晰的模块划分
- 使用ViewModel管理USB连接和数据通信逻辑,确保配置变更时的状态保持
未来可以进一步探索的方向:
- 实现更复杂的USB设备通信协议
- 添加USB设备连接状态的通知功能
- 优化USB数据传输的性能和稳定性
- 支持多USB设备同时连接和通信
希望本文能帮助你在自己的Jetpack Compose项目中顺利实现USB Host功能。如果你有任何问题或建议,欢迎在评论区留言讨论。
点赞、收藏、关注三连,获取更多Android开发实用技巧!下期我们将介绍如何在android-sunflower中实现蓝牙设备通信功能,敬请期待。
更多推荐





所有评论(0)