news 2026/4/3 6:06:55

HarmonyOS 星闪快速实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS 星闪快速实战

一、什么是星闪?

星闪(NearLink)是华为研发的新一代短距离无线通信技术,可以理解为"华为版蓝牙"(仅限我们目前用的,有对标WiFi的版本),但比蓝牙更快、更稳、更省电。

星闪的优点:

  • 低延迟、高带宽、低功耗。

星闪的缺点:

  • 当前只能连接华为设备,不能连 iPhone 等其他设备
  • 设备还不多,在发展中

开发星闪应用需要什么?

  • 一台华为手机(运行 HarmonyOS 5.0+),星闪支持较好
  • 一个星闪设备(或另一台华为手机)
  • DevEco Studio 开发工具

二、第一步:导入需要的模块

// ====== common/NearLinkManager.ets ======// 这个文件放在 entry/src/main/ets/common/ 目录下// 星闪功能模块import{scan,advertising,ssap,constant}from'@kit.NearLinkKit';// scan: 扫描设备// advertising: 发送广播(可选)// ssap: 星闪应用层协议,类似蓝牙的 GATT// constant: 常量定义// 权限管理模块import{abilityAccessCtrl,Permissions,common}from'@kit.AbilityKit';// 错误处理模块import{BusinessError}from'@kit.BasicServicesKit';// 日志模块(可选)import{hilog}from'@kit.PerformanceAnalysisKit';

三、第二步:配置权限

星闪只需要一个权限,比蓝牙简单多了!

3.1 静态权限声明

// ====== entry/src/main/module.json5 ======// 只需要这一个权限,是不是超简单?{"module":{"requestPermissions":[{"name":"ohos.permission.ACCESS_NEARLINK",// 星闪权限(就这一个!)"reason":"$string:nearlink_permission_reason","usedScene":{"abilities":["EntryAbility"],"when":"inuse"}}]}}

3.2 权限说明字符串

// ====== entry/src/main/resources/base/element/string.json ======{"string":[{"name":"nearlink_permission_reason","value":"需要星闪权限以连接和控制星闪设备"}]}

3.3 动态权限申请(弹窗让用户确认)

// ====== common/PermissionManager.ets ======import{abilityAccessCtrl,Permissions,common}from'@kit.AbilityKit';exportclassPermissionManager{privatestaticinstance:PermissionManager;privatecontext?:common.UIAbilityContext;publicstaticgetInstance():PermissionManager{if(!PermissionManager.instance){PermissionManager.instance=newPermissionManager();}returnPermissionManager.instance;}publicsetContext(context:common.UIAbilityContext):void{this.context=context;}/** * 检查并申请星闪权限 * @returns 是否已授权 */publicasynccheckAndRequestNearLinkPermissions():Promise<boolean>{if(!this.context){console.error('Context not set');returnfalse;}constpermission:Permissions='ohos.permission.ACCESS_NEARLINK';try{constatManager=abilityAccessCtrl.createAtManager();// 检查权限constgrantStatus=awaitatManager.checkAccessToken(this.context.applicationInfo.accessTokenId,permission);if(grantStatus!==abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED){// 权限未授予,请求用户授权constrequestResult=awaitatManager.requestPermissionsFromUser(this.context,[permission]);if(requestResult.authResults[0]!==abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED){console.error('NearLink permission denied by user');returnfalse;}}console.info('NearLink permission granted');returntrue;}catch(error){console.error(`Permission request failed:${error}`);returnfalse;}}}

3.4 在 EntryAbility 中初始化(应用启动时设置)

// ====== EntryAbility.ets ======import{AbilityConstant,UIAbility,Want}from'@kit.AbilityKit';import{window}from'@kit.ArkUI';import{PermissionManager}from'../common/PermissionManager';import{NearLinkManager}from'../common/NearLinkManager';exportdefaultclassEntryAbilityextendsUIAbility{onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{console.info('EntryAbility onCreate');}onWindowStageCreate(windowStage:window.WindowStage):void{// 设置上下文PermissionManager.getInstance().setContext(this.context);NearLinkManager.getInstance().setContext(this.context);windowStage.loadContent('pages/Index',(err)=>{if(err.code){console.error('Failed to load content');return;}});}}

四、第三步:SSAP 客户端实现(核心代码)

SSAP 是什么?星闪应用层协议(StarShine Application Protocol),类似蓝牙的 GATT,用来定义数据如何传输。

术语对照:

蓝牙星闪说明
GATTSSAP应用层协议
CharacteristicProperty数据容器
ServiceService服务分组

4.1 SSAP 客户端管理器

// ====== common/SSAPClient.ets ======// 这个文件放在 entry/src/main/ets/common/ 目录下import{ssap,constant}from'@kit.NearLinkKit';import{BusinessError}from'@kit.BasicServicesKit';// ========== 数据类型定义 ==========// 连接状态枚举exportenumConnectionState{DISCONNECTED=0,// 已断开CONNECTING=1,// 连接中...CONNECTED=2,// 已连接DISCONNECTING=3// 断开中...}// 属性权限枚举(这个属性能读/能写/能通知)exportenumPropertyPermission{READ=0x01,// 可读WRITE=0x02,// 可写NOTIFY=0x04// 可接收通知}// 连接状态变化信息exportinterfaceConnectionChangeState{address:string;state:ConnectionState;}// 属性通知信息exportinterfacePropertyNotification{address:string;serviceId:string;propertyId:string;value:ArrayBuffer;}// 发现的服务exportinterfaceDiscoveredService{serviceId:string;properties:DiscoveredProperty[];}// 发现的属性exportinterfaceDiscoveredProperty{propertyId:string;permissions:PropertyPermission[];}// 客户端信息interfaceClientInfo{address:string;client:ssap.Client;services:Array<ssap.Service>;connectionState:ConnectionState;}exportclassSSAPClient{privatestaticinstance:SSAPClient;privateclientMap:Map<string,ClientInfo>=newMap();// 回调函数privateconnectionStateCallbacks:((state:ConnectionChangeState)=>void)[]=[];privatepropertyNotificationCallbacks:((notification:PropertyNotification)=>void)[]=[];privateconstructor(){}publicstaticgetInstance():SSAPClient{if(!SSAPClient.instance){SSAPClient.instance=newSSAPClient();}returnSSAPClient.instance;}/** * 连接到 SSAP 服务端(连接星闪设备) * @param address 设备地址 * @returns 是否连接成功 */publicasyncconnectToServer(address:string):Promise<boolean>{try{// 检查是否已连接,防止重复连接if(this.clientMap.has(address)){constexisting=this.clientMap.get(address);if(existing?.connectionState===ConnectionState.CONNECTED){console.info(`已经连接了这个设备:${address}`);returntrue;}}console.info(`正在连接星闪设备:${address}`);// 创建 SSAP 客户端constclient:ssap.Client=ssap.createClient(address);constclientInfo:ClientInfo={address:address,client:client,services:[],connectionState:ConnectionState.CONNECTING};this.clientMap.set(address,clientInfo);// 注册连接状态变化回调client.on('connectionStateChange',(data:ssap.ConnectionChangeState)=>{this.handleConnectionStateChange(address,data);});// 注册属性变化回调client.on('propertyChange',(data:ssap.Property)=>{this.handlePropertyChange(address,data);});// 连接awaitclient.connect();console.info(`Connection request sent:${address}`);returntrue;}catch(error){console.error(`Connect failed:${error}`);this.clientMap.delete(address);returnfalse;}}/** * 处理连接状态变化 */privatehandleConnectionStateChange(address:string,data:ssap.ConnectionChangeState):void{constclientInfo=this.clientMap.get(address);if(!clientInfo)return;letnewState:ConnectionState;if(data.state===constant.ConnectionState.STATE_CONNECTED){newState=ConnectionState.CONNECTED;clientInfo.connectionState=newState;// 连接成功后获取服务列表this.getServices(address);}elseif(data.state===constant.ConnectionState.STATE_DISCONNECTED){newState=ConnectionState.DISCONNECTED;clientInfo.connectionState=newState;clientInfo.services=[];}elseif(data.state===constant.ConnectionState.STATE_CONNECTING){newState=ConnectionState.CONNECTING;clientInfo.connectionState=newState;}elseif(data.state===constant.ConnectionState.STATE_DISCONNECTING){newState=ConnectionState.DISCONNECTING;clientInfo.connectionState=newState;}else{newState=ConnectionState.DISCONNECTED;}// 通知所有订阅者constchangeState:ConnectionChangeState={address,state:newState};this.connectionStateCallbacks.forEach(callback=>callback(changeState));}/** * 处理属性变化 */privatehandlePropertyChange(address:string,data:ssap.Property):void{constnotification:PropertyNotification={address:address,serviceId:data.serviceUuid,propertyId:data.propertyUuid,value:data.value};this.propertyNotificationCallbacks.forEach(callback=>callback(notification));}/** * 获取服务列表 */privateasyncgetServices(address:string):Promise<void>{try{constclientInfo=this.clientMap.get(address);if(!clientInfo)return;constservices:Array<ssap.Service>=awaitclientInfo.client.getServices();clientInfo.services=services;console.info(`Discovered${services.length}services`);// 自动设置属性通知for(constserviceofservices){for(constpropofservice.properties){awaitthis.subscribePropertyNotification(address,service.serviceUuid,prop.propertyUuid);}}}catch(error){console.error(`Get services failed:${error}`);}}/** * 读取属性值 */publicasyncreadProperty(address:string,serviceId:string,propertyId:string):Promise<ArrayBuffer|null>{try{constclientInfo=this.clientMap.get(address);if(!clientInfo||clientInfo.services.length===0){console.error('Client not found or services not discovered');returnnull;}constproperty:ssap.Property={serviceUuid:serviceId,propertyUuid:propertyId,value:newArrayBuffer(1)};constresult:ssap.Property=awaitclientInfo.client.readProperty(property);returnresult.value;}catch(error){console.error(`Read property failed:${error}`);returnnull;}}/** * 写入属性值(双重写入策略) */publicasyncwriteProperty(address:string,serviceId:string,propertyId:string,value:ArrayBuffer):Promise<boolean>{try{constclientInfo=this.clientMap.get(address);if(!clientInfo||clientInfo.services.length===0){console.error('Client not found or services not discovered');returnfalse;}constproperty:ssap.Property={serviceUuid:serviceId,propertyUuid:propertyId,value:value};// 双重写入策略try{awaitclientInfo.client.writeProperty(property,ssap.PropertyWriteType.WRITE);console.info('Write property success (WRITE mode)');returntrue;}catch(writeError){console.warn('WRITE mode failed, trying WRITE_NO_RESPONSE');awaitclientInfo.client.writeProperty(property,ssap.PropertyWriteType.WRITE_NO_RESPONSE);console.info('Write property success (WRITE_NO_RESPONSE mode)');returntrue;}}catch(error){console.error(`Write property failed:${error}`);returnfalse;}}/** * 订阅属性通知 */publicasyncsubscribePropertyNotification(address:string,serviceId:string,propertyId:string):Promise<boolean>{try{constclientInfo=this.clientMap.get(address);if(!clientInfo)returnfalse;constproperty:ssap.Property={serviceUuid:serviceId,propertyUuid:propertyId,value:newArrayBuffer(1)};awaitclientInfo.client.setPropertyNotification(property,true);returntrue;}catch(error){console.error(`Subscribe notification failed:${error}`);returnfalse;}}/** * 断开连接 */publicasyncdisconnectFromServer(address:string):Promise<void>{try{constclientInfo=this.clientMap.get(address);if(!clientInfo)return;awaitclientInfo.client.disconnect();clientInfo.client.off('propertyChange');clientInfo.client.off('connectionStateChange');clientInfo.client.close();this.clientMap.delete(address);console.info(`Disconnected from:${address}`);}catch(error){console.error(`Disconnect failed:${error}`);}}/** * 获取发现的服务 */publicgetDiscoveredServices(address:string):DiscoveredService[]{constclientInfo=this.clientMap.get(address);if(!clientInfo)return[];returnclientInfo.services.map((service:ssap.Service):DiscoveredService=>({serviceId:service.serviceUuid,properties:service.properties.map((prop:ssap.Property):DiscoveredProperty=>({propertyId:prop.propertyUuid,permissions:[PropertyPermission.READ,PropertyPermission.WRITE,PropertyPermission.NOTIFY]}))}));}// 回调管理publiconConnectionStateChange(callback:(state:ConnectionChangeState)=>void):void{this.connectionStateCallbacks.push(callback);}publiconPropertyNotification(callback:(notification:PropertyNotification)=>void):void{this.propertyNotificationCallbacks.push(callback);}}

四、第三步:星闪管理器(核心代码)

这是整个星闪功能的核心,包含扫描、连接、收发数据等所有功能。

// ====== common/NearLinkManager.ets ======import{scan,advertising}from'@kit.NearLinkKit';import{common}from'@kit.AbilityKit';import{BusinessError}from'@kit.BasicServicesKit';import{SSAPClient,ConnectionState,DiscoveredService,ConnectionChangeState,PropertyNotification}from'./SSAPClient';// 扫描结果接口exportinterfaceScanResult{deviceId:string;deviceName:string;address:string;rssi:number;}// 扫描过滤器接口exportinterfaceScanFilter{deviceName?:string;address?:string;}exportclassNearLinkManager{privatestaticinstance:NearLinkManager;privatecontext?:common.UIAbilityContext;privateisScanning:boolean=false;privatescanResults:Map<string,ScanResult>=newMap();privateconnectedDevice?:ScanResult;// 回调函数privateonDeviceFoundCallback?:(device:ScanResult)=>void;privateconnectionStateCallbacks:((device:ScanResult,state:ConnectionState)=>void)[]=[];privateonDataReceivedCallback?:(data:ArrayBuffer)=>void;// SSAP客户端privatessapClient:SSAPClient=SSAPClient.getInstance();privateconstructor(){this.setupSSAPClientCallback();}publicstaticgetInstance():NearLinkManager{if(!NearLinkManager.instance){NearLinkManager.instance=newNearLinkManager();}returnNearLinkManager.instance;}publicsetContext(context:common.UIAbilityContext):void{this.context=context;}/** * 设置 SSAP 客户端回调 */privatesetupSSAPClientCallback():void{// 连接状态变化回调this.ssapClient.onConnectionStateChange((state:ConnectionChangeState)=>{if(this.connectedDevice&&this.connectionStateCallbacks.length>0){letconnectionState:ConnectionState;switch(state.state){case0:connectionState=ConnectionState.DISCONNECTED;break;case1:connectionState=ConnectionState.CONNECTING;break;case2:connectionState=ConnectionState.CONNECTED;break;case3:connectionState=ConnectionState.DISCONNECTING;break;default:connectionState=ConnectionState.DISCONNECTED;}this.connectionStateCallbacks.forEach(callback=>{callback(this.connectedDevice!,connectionState);});}});// 属性通知回调this.ssapClient.onPropertyNotification((notification:PropertyNotification)=>{if(this.onDataReceivedCallback){this.onDataReceivedCallback(notification.value);}});}/** * 开始扫描 */publicstartScan(filters?:ScanFilter[]):void{if(this.isScanning){console.warn('Already scanning');return;}try{this.isScanning=true;this.scanResults.clear();// 注册设备发现回调scan.on('deviceFound',(data:Array<scan.ScanResults>)=>{data.forEach((result:scan.ScanResults)=>{constdevice:ScanResult={deviceId:result.address,deviceName:result.deviceName||'未知设备',address:result.address,rssi:result.rssi||-100};// 去重if(!this.scanResults.has(device.address)){this.scanResults.set(device.address,device);if(this.onDeviceFoundCallback){this.onDeviceFoundCallback(device);}}});});// 配置扫描参数constscanOptions:scan.ScanOptions={scanMode:2// 平衡扫描模式};// 构建过滤器constscanFilters:scan.ScanFilters[]=[];if(filters&&filters.length>0){filters.forEach(filter=>{constnearLinkFilter:scan.ScanFilters={};if(filter.deviceName){nearLinkFilter.deviceName=filter.deviceName;}if(filter.address){nearLinkFilter.address=filter.address;}scanFilters.push(nearLinkFilter);});}constfiltersToUse=scanFilters.length>0?scanFilters:[({}asscan.ScanFilters)];scan.startScan(filtersToUse,scanOptions).then(()=>{console.info('NearLink scan started');}).catch((err:BusinessError)=>{console.error(`Start scan failed:${err.message}`);this.isScanning=false;});}catch(error){console.error(`Start scan exception:${error}`);this.isScanning=false;}}/** * 停止扫描 */publicstopScan():void{if(!this.isScanning)return;try{scan.stopScan().then(()=>console.info('NearLink scan stopped')).catch((err:BusinessError)=>console.error(`Stop scan failed:${err.message}`));scan.off('deviceFound');this.isScanning=false;}catch(error){console.error(`Stop scan exception:${error}`);}}/** * 连接设备 */publicasyncconnectDevice(device:ScanResult):Promise<boolean>{try{this.connectedDevice=device;constsuccess=awaitthis.ssapClient.connectToServer(device.address);returnsuccess;}catch(error){console.error(`Connect device failed:${error}`);this.connectedDevice=undefined;returnfalse;}}/** * 断开连接 */publicasyncdisconnectDevice():Promise<void>{if(!this.connectedDevice)return;try{awaitthis.ssapClient.disconnectFromServer(this.connectedDevice.address);this.connectedDevice=undefined;}catch(error){console.error(`Disconnect failed:${error}`);}}/** * 发送数据 */publicasyncsendData(serviceId:string,propertyId:string,data:ArrayBuffer):Promise<boolean>{if(!this.connectedDevice)returnfalse;returnawaitthis.ssapClient.writeProperty(this.connectedDevice.address,serviceId,propertyId,data);}// 回调设置publicsetOnDeviceFoundCallback(callback:(device:ScanResult)=>void):void{this.onDeviceFoundCallback=callback;}publicsetOnConnectionStateChangedCallback(callback:(device:ScanResult,state:ConnectionState)=>void):void{if(!this.connectionStateCallbacks.includes(callback)){this.connectionStateCallbacks.push(callback);}}publicsetOnDataReceivedCallback(callback:(data:ArrayBuffer)=>void):void{this.onDataReceivedCallback=callback;}// GetterpublicgetIsScanning():boolean{returnthis.isScanning;}publicgetConnectedDevice():ScanResult|undefined{returnthis.connectedDevice;}publicgetDiscoveredServices():DiscoveredService[]{if(!this.connectedDevice)return[];returnthis.ssapClient.getDiscoveredServices(this.connectedDevice.address);}}

六、UI 组件实现

6.1 设备卡片组件

// ====== pages/NearLinkConnectionPage.ets ======import{ScanResult,ConnectionState}from'../common/NearLinkManager';@Component struct NearLinkDeviceCard{@Prop device:ScanResult;@Prop isConnected:boolean=false;onConnect?:()=>void;privategetSignalColor(rssi:number):string{if(rssi>=-50)return'#4CAF50';if(rssi>=-70)return'#FF9800';return'#FF5722';}build(){Row(){// 左侧:设备信息Column(){Row(){Text(this.device.deviceName).fontSize(16).fontWeight(FontWeight.Medium).fontColor(this.isConnected?'#9C27B0':'#333333').layoutWeight(1)// 星闪标识Text('NearLink').fontSize(10).fontColor('#9C27B0').backgroundColor('rgba(156, 39, 176, 0.1)').padding({left:6,right:6,top:2,bottom:2}).borderRadius(4)}.width('100%')Row(){Text(this.device.address).fontSize(12).fontColor('#999999').layoutWeight(1)Text(`${this.device.rssi}dBm`).fontSize(12).fontColor(this.getSignalColor(this.device.rssi))}.width('100%').margin({top:4})}.layoutWeight(1).alignItems(HorizontalAlign.Start)// 连接按钮Button(this.isConnected?'断开':'连接').fontSize(14).backgroundColor(this.isConnected?'#FF5722':'#9C27B0').fontColor(Color.White).height(36).width(70).margin({left:12}).onClick(()=>{if(this.onConnect){this.onConnect();}})}.width('100%').padding(16).backgroundColor(Color.White).borderRadius(12).shadow({radius:4,color:'rgba(0,0,0,0.1)',offsetY:2})}}

6.2 设备列表页面

// ====== pages/NearLinkConnectionPage.ets ======import{NearLinkManager,ScanResult,ConnectionState}from'../common/NearLinkManager';import{PermissionManager}from'../common/PermissionManager';import{promptAction}from'@kit.ArkUI';@Entry @Component struct NearLinkConnectionPage{@State discoveredDevices:ScanResult[]=[];@State isScanning:boolean=false;@State connectedDevice:ScanResult|null=null;privatenearLinkManager:NearLinkManager=NearLinkManager.getInstance();aboutToAppear():void{// 设置设备发现回调this.nearLinkManager.setOnDeviceFoundCallback((device:ScanResult)=>{constexists=this.discoveredDevices.find(d=>d.address===device.address);if(!exists){this.discoveredDevices=[...this.discoveredDevices,device];}});// 设置连接状态回调this.nearLinkManager.setOnConnectionStateChangedCallback((device:ScanResult,state:ConnectionState)=>{if(state===ConnectionState.CONNECTED){this.connectedDevice=device;promptAction.showToast({message:`已连接:${device.deviceName}`});}elseif(state===ConnectionState.DISCONNECTED){this.connectedDevice=null;promptAction.showToast({message:'设备已断开'});}});}aboutToDisappear():void{this.nearLinkManager.stopScan();}privateasyncstartScan():Promise<void>{// 检查并申请权限consthasPermission=awaitPermissionManager.getInstance().checkAndRequestNearLinkPermissions();if(!hasPermission){promptAction.showToast({message:'请授予星闪权限后重试'});return;}this.discoveredDevices=[];this.nearLinkManager.startScan();this.isScanning=true;// 10秒后自动停止扫描setTimeout(()=>{this.stopScan();},10000);}privatestopScan():void{this.nearLinkManager.stopScan();this.isScanning=false;}privateasynconDeviceClick(device:ScanResult):Promise<void>{if(this.connectedDevice?.address===device.address){awaitthis.nearLinkManager.disconnectDevice();}else{this.stopScan();awaitthis.nearLinkManager.connectDevice(device);}}build(){Column(){// 标题栏Row(){Text('星闪设备').fontSize(20).fontWeight(FontWeight.Bold)Blank()Button(this.isScanning?'停止扫描':'开始扫描').fontSize(14).backgroundColor(this.isScanning?'#FF5722':'#9C27B0').onClick(()=>{if(this.isScanning){this.stopScan();}else{this.startScan();}})}.width('100%').padding(16)// 扫描状态if(this.isScanning){Row(){LoadingProgress().width(20).height(20).color('#9C27B0')Text('正在扫描星闪设备...').fontSize(14).fontColor('#666666').margin({left:8})}.width('100%').justifyContent(FlexAlign.Center).padding(16)}// 设备列表if(this.discoveredDevices.length===0){Column(){Text('⚡').fontSize(48).margin({bottom:16})Text(this.isScanning?'正在搜索星闪设备...':'未发现设备').fontSize(16).fontColor('#999999')}.width('100%').height(200).justifyContent(FlexAlign.Center)}else{List({space:12}){ForEach(this.discoveredDevices,(device:ScanResult)=>{ListItem(){NearLinkDeviceCard({device:device,isConnected:this.connectedDevice?.address===device.address,onConnect:()=>this.onDeviceClick(device)})}},(device:ScanResult)=>device.address)}.width('100%').layoutWeight(1).padding({left:16,right:16})}}.width('100%').height('100%').backgroundColor('#F5F5F5')}}

6.3 服务/属性卡片组件

// ====== pages/NearLinkDeviceDetailPage.ets ======import{NearLinkManager,DiscoveredService,DiscoveredProperty}from'../common/NearLinkManager';import{router}from'@kit.ArkUI';@Entry @Component struct NearLinkDeviceDetailPage{@State services:DiscoveredService[]=[];privatenearLinkManager:NearLinkManager=NearLinkManager.getInstance();aboutToAppear():void{this.services=this.nearLinkManager.getDiscoveredServices();}privateimportUUID(serviceId:string,propertyId:string):void{router.pushUrl({url:'pages/SettingsPage',params:{importServiceUUID:serviceId,importCharacteristicUUID:propertyId}});}@BuilderServiceCard(service:DiscoveredService,index:number){Column(){// 服务标题Row(){Text(`服务${index+1}`).fontSize(14).fontWeight(FontWeight.Medium).fontColor('#333333')Blank()Text('SSAP Service').fontSize(12).fontColor('#9C27B0').backgroundColor('rgba(156, 39, 176, 0.1)').padding({left:8,right:8,top:2,bottom:2}).borderRadius(4)}.width('100%').margin({bottom:8})// 服务 UUIDText(service.serviceId).fontSize(12).fontColor('#666666').width('100%').margin({bottom:12})// 属性列表if(service.properties.length>0){Text('属性列表').fontSize(13).fontColor('#999999').width('100%').margin({bottom:8})ForEach(service.properties,(prop:DiscoveredProperty,propIndex:number)=>{Row(){Column(){Text(`属性${propIndex+1}`).fontSize(12).fontColor('#333333')Text(prop.propertyId).fontSize(11).fontColor('#999999').maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis})}.layoutWeight(1).alignItems(HorizontalAlign.Start)Button('导入').fontSize(12).height(28).backgroundColor('#9C27B0').fontColor(Color.White).onClick(()=>{this.importUUID(service.serviceId,prop.propertyId);})}.width('100%').padding(8).backgroundColor('#F5F5F5').borderRadius(4).margin({bottom:4})},(prop:DiscoveredProperty)=>prop.propertyId)}}.width('100%').padding(16).backgroundColor(Color.White).borderRadius(12).shadow({radius:2,color:'rgba(0,0,0,0.1)',offsetY:1})}build(){Column(){// 标题栏Row(){Button('返回').backgroundColor(Color.Transparent).fontColor('#9C27B0').onClick(()=>router.back())Text('设备详情').fontSize(18).fontWeight(FontWeight.Medium).layoutWeight(1).textAlign(TextAlign.Center)Text('').width(60)}.width('100%').padding(16)// 服务列表if(this.services.length===0){Column(){Text('未发现服务').fontSize(16).fontColor('#999999')}.width('100%').height(200).justifyContent(FlexAlign.Center)}else{Scroll(){Column({space:12}){ForEach(this.services,(service:DiscoveredService,index:number)=>{this.ServiceCard(service,index)},(service:DiscoveredService)=>service.serviceId)}.width('100%').padding(16)}.layoutWeight(1)}}.width('100%').height('100%').backgroundColor('#F5F5F5')}}

6.4 UUID 配置页面

// ====== pages/NearLinkSettingsPage.ets ======import{router}from'@kit.ArkUI';@Entry @Component struct NearLinkSettingsPage{@State serviceUUID:string='';@State propertyUUID:string='';@State filterEnabled:boolean=false;@State filterDeviceName:string='';aboutToAppear():void{constparams=router.getParams()asRecord<string,string>;if(params){if(params.importServiceUUID){this.serviceUUID=params.importServiceUUID;}if(params.importCharacteristicUUID){this.propertyUUID=params.importCharacteristicUUID;}}}@BuilderUUIDConfigSection(){Column(){Text('UUID 配置').fontSize(16).fontWeight(FontWeight.Medium).alignSelf(ItemAlign.Start).margin({bottom:16})// 服务 UUIDRow(){Text('服务UUID').fontSize(14).fontColor('#666666').width(80)TextInput({placeholder:'请输入服务UUID',text:this.serviceUUID}).layoutWeight(1).margin({left:12,right:12}).onChange((value:string)=>{this.serviceUUID=value;})Button('重置').fontSize(12).backgroundColor('#F5F5F5').fontColor('#9C27B0').width(60).height(32).onClick(()=>{this.serviceUUID='';})}.width('100%').margin({bottom:12})// 属性 UUIDRow(){Text('属性UUID').fontSize(14).fontColor('#666666').width(80)TextInput({placeholder:'请输入属性UUID',text:this.propertyUUID}).layoutWeight(1).margin({left:12,right:12}).onChange((value:string)=>{this.propertyUUID=value;})Button('重置').fontSize(12).backgroundColor('#F5F5F5').fontColor('#9C27B0').width(60).height(32).onClick(()=>{this.propertyUUID='';})}.width('100%').margin({bottom:8})Text('UUID格式:37bea880-fc70-11ea-b720-00000000fdee').fontSize(12).fontColor('#999999').alignSelf(ItemAlign.Start)}.width('100%').padding(16).backgroundColor(Color.White).borderRadius(12)}build(){Column(){// 标题栏Row(){Button('返回').backgroundColor(Color.Transparent).fontColor('#9C27B0').onClick(()=>router.back())Text('星闪设置').fontSize(18).fontWeight(FontWeight.Medium).layoutWeight(1).textAlign(TextAlign.Center)Button('保存').backgroundColor(Color.Transparent).fontColor('#9C27B0').onClick(()=>router.back())}.width('100%').padding(16)Scroll(){Column({space:16}){this.UUIDConfigSection()}.width('100%').padding(16)}.layoutWeight(1)}.width('100%').height('100%').backgroundColor('#F5F5F5')}}

七、广播发送(高级用法,可选)

什么是广播?让你的手机也能被其他星闪设备发现,类似于使你的手机变成一个星闪设备。

什么时候用?当你需要手机对手机通信,或者你在开发服务端功能时。

// ====== common/NearLinkManager.ets(补充)======/** * 开始广播 */publicstartAdvertising():void{try{constsetting:advertising.AdvertisingSettings={interval:160,power:2};constmanufactureData:advertising.ManufacturerData={manufacturerId:4567,manufacturerData:newUint8Array([1,2,3,4]).buffer};constserviceData:advertising.ServiceData={serviceUuid:'37bea880-fc70-11ea-b720-00000000fdee',serviceData:newUint8Array([5,6,7,8]).buffer};constadvData:advertising.AdvertisingData={serviceUuids:['37bea880-fc70-11ea-b720-00000000fdee'],manufacturerData:[manufactureData],serviceData:[serviceData],includeDeviceName:true};constadvertisingParams:advertising.AdvertisingParams={advertisingSettings:setting,advertisingData:advData};advertising.startAdvertising(advertisingParams).then((handle:number)=>{console.info(`Advertising started, handle:${handle}`);}).catch((err)=>{console.error(`Start advertising failed:${err.message}`);});}catch(error){console.error(`Start advertising exception:${error}`);}}/** * 停止广播 */publicstopAdvertising(handle:number):void{try{advertising.stopAdvertising(handle).then(()=>console.info('Advertising stopped')).catch((err)=>console.error(`Stop advertising failed:${err.message}`));}catch(error){console.error(`Stop advertising exception:${error}`);}}

八、最佳实践(踩坑总结)

8.1 必须遵守的规则

  1. 权限先行:扫描前必须检查并申请权限(只要1个!)
  2. 资源释放:页面销毁时停止扫描、断开连接
  3. 状态同步:使用回调机制同步 UI 状态
  4. 多设备管理:使用 Map 管理多个连接

8.2 常见问题排查

问题可能原因解决方法
扫描不到设备权限未授予检查应用设置里的权限
扫描不到设备设备不支持星闪确认是华为星闪设备
连接失败设备已被其他手机连接断开其他连接后重试
读写失败UUID 不对检查服务UUID和属性UUID

8.3 写入数据的技巧

// 双重写入策略:先尝试 WRITE,失败后尝试 WRITE_NO_RESPONSEpublicasyncsendData(serviceId:string,propId:string,data:ArrayBuffer):Promise<boolean>{try{// 第一次尝试:带响应的写入awaitthis.ssapClient?.writeProperty(prop,ssap.PropertyWriteType.WRITE);returntrue;}catch(error){try{// 第二次尝试:不带响应的写入awaitthis.ssapClient?.writeProperty(prop,ssap.PropertyWriteType.WRITE_NO_RESPONSE);returntrue;}catch(e){returnfalse;}}}

九、学习资源

  • ? 官方星闪开发指南
  • ? NearLink Kit API 文档
  • ? SSAP 客户端开发

十、常见问题 FAQ

Q1: 我的设备支持星闪吗?

如何确认:

  • HarmonyOS 6设备下拉控制中心,检查WLAN下方是不是有星闪标识

Q2: 星闪和蓝牙可以同时用吗?

可以!它们使用不同的无线频段,互不干扰。你可以同时连接一个蓝牙设备和一个星闪设备。

Q4: 星闪设备在哪买?

目前星闪设备还不太普及,可以购买WS63或者BS21E开发板作为初体验


作者声明:个人拙作,仅总结了本人开发经验,专业性指导请参考官方文档,欢迎各位大佬斧正。本文档不是最简星闪调试工具开发文档,代码截取自成熟应用,欢迎下载易管闪联(星闪端,蓝牙端正在突破3.5)体验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/31 4:57:02

LobeChat能否解释决策过程?可解释性增强

LobeChat能否解释决策过程&#xff1f;可解释性增强 在医疗咨询、投资建议或法律分析等高风险场景中&#xff0c;用户不会满足于AI只说“这是答案”——他们更想知道&#xff1a;“你为什么这么认为&#xff1f;” 这正是当前大语言模型&#xff08;LLM&#xff09;落地应用的核…

作者头像 李华
网站建设 2026/3/31 17:32:40

边缘计算所使用的知识蒸馏、轻量化模型具体依据什么进行操作

边缘计算中使用知识蒸馏&#xff08;Knowledge Distillation&#xff09;和轻量化模型&#xff08;Lightweight Models&#xff09;&#xff0c;主要是为了在资源受限的设备&#xff08;如移动终端、IoT设备、嵌入式系统等&#xff09;上实现高效、低延迟、低功耗的推理。其操作…

作者头像 李华
网站建设 2026/4/3 3:08:55

LobeChat能否实现AI润色功能?写作质量提升实战

LobeChat能否实现AI润色功能&#xff1f;写作质量提升实战 在内容创作日益高频的今天&#xff0c;写完一段文字后总忍不住问自己&#xff1a;“这段话够流畅吗&#xff1f;”“语气是否得体&#xff1f;”“有没有更精准的表达方式&#xff1f;”——这些困扰几乎每个写作者的问…

作者头像 李华
网站建设 2026/3/31 6:13:20

USB设备厂商与产品ID查询指南

USB设备厂商与产品ID查询指南 在一台边缘计算盒子前&#xff0c;开发者正尝试接入一个微型摄像头来驱动AI视频生成系统。他插入设备后却发现程序无法识别——日志里只显示一串冰冷的代码&#xff1a;0bda:571c。这不是故障&#xff0c;而是现代硬件生态中每天都在上演的“身份…

作者头像 李华
网站建设 2026/3/30 6:44:19

Langflow源码架构解析

Langflow源码架构解析 在 AI 应用开发日益普及的今天&#xff0c;LangChain 这类框架极大简化了大模型集成流程。然而对许多开发者而言&#xff0c;编写复杂的链式调用代码依然存在学习成本高、调试困难等问题。正是在这种背景下&#xff0c;Langflow 应运而生——它把 LangCh…

作者头像 李华