
Broadcast 广播实战开机广播、串口/外设状态广播、系统按键拦截广播这玩意儿用得好是千里眼顺风耳用得不好就是内存泄漏的无底洞——工控设备上一堆外设要监控、开机要自启不用广播怎么搞一、广播机制简介Android广播BroadcastReceiver是一种全局的事件通知机制。你可以把它想象成一个广播电台——有人发送消息sendBroadcast有兴趣的人收听BroadcastReceiver就这么简单。工控场景中广播的典型用途包括设备开机后自动启动采集程序监听USB摄像头/扫码枪的插拔事件检测网络状态变化及时切换离线模式接收自定义广播做跨组件通知1.1 静态注册 vs 动态注册两种注册方式各有优劣选错了可能让你的功能根本不起作用。静态注册Manifest注册在AndroidManifest.xml中声明即使APP没有启动系统也会在广播到来时自动拉起你的Receiver。receiverandroid:name.BootReceiverandroid:exportedtrueintent-filteractionandroid:nameandroid.intent.action.BOOT_COMPLETED//intent-filter/receiver优点是不依赖APP运行状态缺点是Android 8.0开始大部分隐式广播不再支持静态注册开机广播等少数几个系统广播例外。动态注册代码注册在代码中通过registerReceiver()注册APP活着时有效APP死了就收不到了。IntentFilterfilternewIntentFilter();filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);registerReceiver(usbReceiver,filter);优点是灵活可以随时注册和注销缺点是必须在组件生命周期内使用用完要记得unregisterReceiver()否则会内存泄漏。工控场景的推荐策略开机广播用静态注册因为开机时APP还没启动外设监控用动态注册因为设备运行时才需要监听而且可以精准控制生命周期。二、开机自启实现工控设备最刚需的功能之一设备上电后APP自动运行采集程序自动开始工作无需人工操作。2.1 BOOT_COMPLETED 广播接收publicclassBootReceiverextendsBroadcastReceiver{OverridepublicvoidonReceive(Contextcontext,Intentintent){if(Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())){// 开机后直接启动前台ServiceIntentserviceIntentnewIntent(context,IndustrialForegroundService.class);if(Build.VERSION.SDK_INTBuild.VERSION_CODES.O){context.startForegroundService(serviceIntent);}else{context.startService(serviceIntent);}}}}注意Android 8.0 后台启动Service有限制必须用startForegroundService()并在5秒内调用startForeground()否则系统会直接崩掉你的进程。2.2 权限与Manifest配置!-- 开机自启需要的权限 --uses-permissionandroid:nameandroid.permission.RECEIVE_BOOT_COMPLETED/receiverandroid:name.BootReceiverandroid:exportedtrueandroid:permissionandroid.permission.RECEIVE_BOOT_COMPLETEDintent-filterandroid:priority1000actionandroid:nameandroid.intent.action.BOOT_COMPLETED/actionandroid:nameandroid.intent.action.REBOOT/actionandroid:nameandroid.intent.action.QUICKBOOT_POWERON//intent-filter/receiver这里有个小技巧设置android:priority1000最高优先级确保你的Receiver在其他APP之前先收到开机广播。QUICKBOOT_POWERON和REBOOT两个action是为了兼容不同厂商的快速启动和重启场景。另外部分手机/工控设备需要在系统设置中手动给APP开启自启动权限。如果你维护的是自刷固件可以在固件中预置权限一劳永逸。三、外设状态监听工控设备上外设繁多——USB扫码枪、温湿度传感器、4G模块、网络摄像头……这些外设的状态变化都需要APP及时感知。3.1 USB设备插入/拔出监听publicclassUsbDeviceReceiverextendsBroadcastReceiver{OverridepublicvoidonReceive(Contextcontext,Intentintent){Stringactionintent.getAction();UsbDevicedeviceintent.getParcelableExtra(UsbManager.EXTRA_DEVICE);if(UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)){// USB设备插入Log.d(USB,设备插入: VIDdevice.getVendorId() PIDdevice.getProductId());handleUsbAttached(context,device);}elseif(UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)){// USB设备拔出Log.d(USB,设备拔出: device.getDeviceName());handleUsbDetached(device);}elseif(UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)){// USB配件如HID设备接入handleAccessoryAttached(intent);}}privatevoidhandleUsbAttached(Contextcontext,UsbDevicedevice){// 检查是否是我们的设备if(device.getVendorId()0x1234device.getProductId()0x5678){// 申请USB权限并打开设备PendingIntentpermissionIntentPendingIntent.getBroadcast(context,0,newIntent(ACTION_USB_PERMISSION),PendingIntent.FLAG_IMMUTABLE);UsbManagerusbManager(UsbManager)context.getSystemService(Context.USB_SERVICE);usbManager.requestPermission(device,permissionIntent);}}}注册和注销要配对使用通常在Activity/Service的onCreate/onDestroy中privateUsbDeviceReceiverusbReceiver;OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);IntentFilterfilternewIntentFilter();filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);usbReceivernewUsbDeviceReceiver();registerReceiver(usbReceiver,filter);}OverrideprotectedvoidonDestroy(){super.onDestroy();if(usbReceiver!null){unregisterReceiver(usbReceiver);}}3.2 网络状态变化监听工控设备经常需要根据网络状态切换在线/离线模式publicclassNetworkReceiverextendsBroadcastReceiver{OverridepublicvoidonReceive(Contextcontext,Intentintent){ConnectivityManagercm(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfoactiveNetworkcm.getActiveNetworkInfo();if(activeNetwork!nullactiveNetwork.isConnected()){if(activeNetwork.getType()ConnectivityManager.TYPE_WIFI){// WiFi已连接全量上传数据uploadPendingData(context);}elseif(activeNetwork.getType()ConnectivityManager.TYPE_ETHERNET){// 有线网络连接工控常见更稳定uploadPendingData(context);}}else{// 网络断开切换到离线缓存模式switchToOfflineMode();}}}四、自定义广播在工控中的应用除了系统广播工控场景下自定义广播能极大简化组件间通信。4.1 串口数据到达通知串口通信通常是异步的数据来了需要通知业务层处理// 定义自定义广播publicstaticfinalStringACTION_SERIAL_DATA_RECEIVEDcom.example.ACTION_SERIAL_DATA_RECEIVED;publicstaticfinalStringEXTRA_SERIAL_DATAserial_data;// 串口读取线程中发送广播publicvoidonDataReceived(byte[]data){IntentintentnewIntent(ACTION_SERIAL_DATA_RECEIVED);intent.putExtra(EXTRA_SERIAL_DATA,data);intent.setPackage(getPackageName());// 限制在本APP内防止泄露sendBroadcast(intent);}使用setPackage()是个好习惯保证广播只在你自己的APP内传播避免被其他APP偷听——工控设备上的数据可不能乱传。4.2 LocalBroadcastManager如果广播只在APP内部使用推荐用LocalBroadcastManager// 发送LocalBroadcastManager.getInstance(this).sendBroadcast(intent);// 注册LocalBroadcastManager.getInstance(this).registerReceiver(receiver,filter);// 注销LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);优势很明显更高效不走IPC、更安全广播不会离开APP、不会有跨进程的安全风险。AndroidX中LocalBroadcastManager已被标记为deprecated推荐改用LiveData或Flow但它们不是广播——如果需要松耦合的跨组件通信自定义广播依然是最简洁的方案。五、系统按键拦截思路5.1 监听HOME键HOME键不能直接拦截这是Android的安全机制防止APP劫持用户但可以监听OverrideprotectedvoidonUserLeaveHint(){super.onUserLeaveHint();// 用户按了HOME键// 工控场景不做处理后台Service继续运行}如果是工控平板或定制设备通常会在固件层禁用HOME键或替换Launcher让用户无法退出APP。5.2 拦截返回键返回键可以直接在Activity中拦截OverridepublicvoidonBackPressed(){// 工控场景询问是否确认退出或者直接禁用// super.onBackPressed(); // 不调用父类方法返回键不生效}OverridepublicbooleanonKeyDown(intkeyCode,KeyEventevent){if(keyCodeKeyEvent.KEYCODE_BACK){// 返回键被按下但不退出returntrue;// 消费事件不再传递}returnsuper.onKeyDown(keyCode,event);}5.3 外接物理按键监听工控设备常见很多工控平板带有物理按键F1-F5、扫描键等这些按键映射的是标准KeyEventOverridepublicbooleanonKeyDown(intkeyCode,KeyEventevent){switch(keyCode){caseKeyEvent.KEYCODE_F1:// 扫码枪触发键returntrue;caseKeyEvent.KEYCODE_F2:// 紧急停止键stopAllEquipment();returntrue;caseKeyEvent.KEYCODE_VOLUME_UP:// 将音量键映射为自定义功能captureScreenshot();returntrue;}returnsuper.onKeyDown(keyCode,event);}六、完整代码示例开机自启 USB设备监听下面给出一个工控场景的核心广播接收实现/** * 开机自启广播 USB设备监听 一体化Receiver */publicclassIndustrialBootReceiverextendsBroadcastReceiver{privatestaticfinalStringTAGIndustrialBoot;OverridepublicvoidonReceive(Contextcontext,Intentintent){Stringactionintent.getAction();if(actionnull)return;switch(action){caseIntent.ACTION_BOOT_COMPLETED:Log.i(TAG,设备开机启动工控服务);startIndustrialService(context);break;caseUsbManager.ACTION_USB_DEVICE_ATTACHED:UsbDevicedeviceintent.getParcelableExtra(UsbManager.EXTRA_DEVICE);Log.i(TAG,USB设备插入: device.getDeviceName());// 根据VID/PID判断设备类型并初始化initUsbDevice(context,device);break;caseUsbManager.ACTION_USB_DEVICE_DETACHED:UsbDevicedetachedintent.getParcelableExtra(UsbManager.EXTRA_DEVICE);Log.i(TAG,USB设备拔出: detached.getDeviceName());handleDeviceRemoved(context,detached);break;caseConnectivityManager.CONNECTIVITY_ACTION:handleNetworkChange(context);break;}}privatevoidstartIndustrialService(Contextcontext){IntentserviceIntentnewIntent(context,IndustrialForegroundService.class);if(Build.VERSION.SDK_INTBuild.VERSION_CODES.O){context.startForegroundService(serviceIntent);}}privatevoidinitUsbDevice(Contextcontext,UsbDevicedevice){// 根据设备类型分发处理if(device.getVendorId()0x2341){// Arduino设备connectArduino(context,device);}elseif(device.getVendorId()0x1A86){// CH340串口openSerialPort(context,device);}}privatevoidhandleDeviceRemoved(Contextcontext,UsbDevicedevice){// 设备移除后的清理逻辑}privatevoidhandleNetworkChange(Contextcontext){// 网络变化处理}}对应的Manifest配置uses-permissionandroid:nameandroid.permission.RECEIVE_BOOT_COMPLETED/receiverandroid:name.IndustrialBootReceiverandroid:exportedtrueandroid:permissionandroid.permission.RECEIVE_BOOT_COMPLETED!-- 开机广播 --intent-filterandroid:priority1000actionandroid:nameandroid.intent.action.BOOT_COMPLETED/actionandroid:nameandroid.intent.action.QUICKBOOT_POWERON//intent-filter!-- USB设备广播 --intent-filteractionandroid:nameandroid.hardware.usb.action.USB_DEVICE_ATTACHED/actionandroid:nameandroid.hardware.usb.action.USB_DEVICE_DETACHED//intent-filter!-- 网络状态广播 --intent-filteractionandroid:nameandroid.net.conn.CONNECTIVITY_CHANGE//intent-filter/receiver广播是Android四大组件中概念最简单但坑最多的一项——记住三个原则能动态注册就不静态注册、能Local就不Global、用完一定unregister。工控设备稳定运行的第一前提就是广播机制不能出乱子。