首页 文章 精选 留言 我的

精选列表

搜索[harmonyos开发],共10000篇文章
优秀的个人博客,低调大师

鸿蒙内核源码分析(中断管理篇) | 硬中断的实现类似观察者模式 | 百篇博客分析HarmonyOS | v44.02

百万汉字注解 >> 精读内核源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< gitee | github | csdn | coding > 百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,主流站点定期更新中< oschina | csdn | weharmony > 关于中断部分系列篇将用三篇详细说明整个过程. 中断概念篇 中断概念很多,比如中断控制器,中断源,中断向量,中断共享,中断处理程序等等.本篇做一次整理.先了解透概念才好理解中断过程.用海公公打比方说明白中断各个概念.可前往鸿蒙内核源码分析(总目录)查看. 中断管理篇(本篇) 从中断初始化HalIrqInit开始,到注册中断的LOS_HwiCreate函数,到消费中断函数的 HalIrqHandler,剖析鸿蒙内核实现中断的过程,很像设计模式中的观察者模式. 中断切换篇 用自下而上的方式,从中断源头纯汇编代码往上跟踪代码细节.说清楚保存和恢复中断现场TaskIrqContext过程.可前往鸿蒙内核源码分析(总目录)查看. 编译开关 系列篇编译平台为 hi3516dv300,整个工程可前往查看. 预编译处理过程会自动生成编译开关 menuconfig.h ,供编译阶段选择编译,可前往查看. //.... #define LOSCFG_ARCH_ARM_VER "armv7-a" #define LOSCFG_ARCH_CPU "cortex-a7" #define LOSCFG_PLATFORM "hi3516dv300" #define LOSCFG_PLATFORM_BSP_GIC_V2 1 #define LOSCFG_PLATFORM_ROOTFS 1 #define LOSCFG_KERNEL_CPPSUPPORT 1 #define LOSCFG_HW_RANDOM_ENABLE 1 #define LOSCFG_ARCH_CORTEX_A7 1 #define LOSCFG_DRIVERS_HDF_PLATFORM_RTC 1 #define LOSCFG_DRIVERS_HDF_PLATFORM_UART 1 中断初始化 hi3516dv300 中断控制器选择了 LOSCFG_PLATFORM_BSP_GIC_V2 ,对应代码为 gic_v2.c GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器. 看这种代码因为涉及硬件部分,需要对照ARM中断控制器 gic_v2.pdf文档看.可前往地址下载查看. //硬件中断初始化 VOID HalIrqInit(VOID) { UINT32 i; /* set externel interrupts to be level triggered, active low. */ //将外部中断设置为电平触发,低电平激活 for (i = 32; i < OS_HWI_MAX_NUM; i += 16) { GIC_REG_32(GICD_ICFGR(i / 16)) = 0; } /* set externel interrupts to CPU 0 */ //将外部中断设置为CPU 0 for (i = 32; i < OS_HWI_MAX_NUM; i += 4) { GIC_REG_32(GICD_ITARGETSR(i / 4)) = 0x01010101; } /* set priority on all interrupts */ //设置所有中断的优先级 for (i = 0; i < OS_HWI_MAX_NUM; i += 4) { GIC_REG_32(GICD_IPRIORITYR(i / 4)) = GICD_INT_DEF_PRI_X4; } /* disable all interrupts. */ //禁用所有中断。 for (i = 0; i < OS_HWI_MAX_NUM; i += 32) { GIC_REG_32(GICD_ICENABLER(i / 32)) = ~0; } HalIrqInitPercpu();//初始化当前CPU中断信息 /* enable gic distributor control */ GIC_REG_32(GICD_CTLR) = 1; //使能分发中断寄存器,该寄存器作用是允许给CPU发送中断信号 #if (LOSCFG_KERNEL_SMP == YES) /* register inter-processor interrupt *///注册核间中断,啥意思?就是CPU各核直接可以发送中断信号 //处理器间中断允许一个CPU向系统其他的CPU发送中断信号,处理器间中断(IPI)不是通过IRQ线传输的,而是作为信号直接放在连接所有CPU本地APIC的总线上。 LOS_HwiCreate(LOS_MP_IPI_WAKEUP, 0xa0, 0, OsMpWakeHandler, 0);//注册唤醒CPU的中断处理函数 LOS_HwiCreate(LOS_MP_IPI_SCHEDULE, 0xa0, 0, OsMpScheduleHandler, 0);//注册调度CPU的中断处理函数 LOS_HwiCreate(LOS_MP_IPI_HALT, 0xa0, 0, OsMpScheduleHandler, 0);//注册停止CPU的中断处理函数 #endif } //给每个CPU core初始化硬件中断 VOID HalIrqInitPercpu(VOID) { /* unmask interrupts */ //取消中断屏蔽 GIC_REG_32(GICC_PMR) = 0xFF; /* enable gic cpu interface */ //启用gic cpu接口 GIC_REG_32(GICC_CTLR) = 1; } 解读 上来四个循环,是对中断控制器寄存器组的初始化,也就是驱动程序,驱动程序是配置硬件寄存器的过程.寄存器分通用和专用寄存器.下图为 gic_v2 的寄存器功能 ,这里对照代码和datasheet重点说下中断配置寄存器(GICD_ICFGRn) 以下是GICD_ICFGRn的介绍 The GICD_ICFGRs provide a 2-bit Int_config field for each interrupt supported by the GIC. This field identifies whether the corresponding interrupt is edge-triggered or level-sensitive GICD_ICFGRs为GIC支持的每个中断提供一个2位配置字段。此字段标识相应的中断是边缘触发的还是电平触发的 0xC00 - 0xCFC GICD_ICFGRn RW IMPLEMENTATION DEFINED Interrupt Configuration Registers #define GICD_ICFGR(n) (GICD_OFFSET + 0xc00 + (n) * 4) /* Interrupt Configuration Registers */ //中断配置寄存器 如此一个32位寄存器可以记录16个中断的信息,这也是代码中出现 GIC_REG_32(GICD_ICFGR(i / 16))的原因. GIC-v2支持三种类型的中断 PPI:私有外设中断(Private Peripheral Interrupt),是每个CPU私有的中断。最多支持16个PPI中断,硬件中断号从ID16~ID31。PPI通常会送达到指定的CPU上,应用场景有CPU本地时钟。 SPI:公用外设中断(Shared Peripheral Interrupt),最多可以支持988个外设中断,硬件中断号从ID32~ID1019。 SGI:软件触发中断(Software Generated Interrupt)通常用于多核间通讯,最多支持16个SGI中断,硬件中断号从ID0~ID15。SGI通常在内核中被用作 IPI 中断(inter-processor interrupts),并会送达到系统指定的CPU上,函数的最后就注册了三个核间中断的函数. typedef enum {//核间中断 LOS_MP_IPI_WAKEUP, //唤醒CPU LOS_MP_IPI_SCHEDULE,//调度CPU LOS_MP_IPI_HALT, //停止CPU } MP_IPI_TYPE; 中断相关的结构体 size_t g_intCount[LOSCFG_KERNEL_CORE_NUM] = {0};//记录每个CPUcore的中断数量 HwiHandleForm g_hwiForm[OS_HWI_MAX_NUM];//中断注册表 @note_why 用 form 来表示?有种写 HTML的感觉 :P STATIC CHAR *g_hwiFormName[OS_HWI_MAX_NUM] = {0};//记录每个硬中断的名称 STATIC UINT32 g_hwiFormCnt[OS_HWI_MAX_NUM] = {0};//记录每个硬中断的总数量 STATIC UINT32 g_curIrqNum = 0; //记录当前中断号 typedef VOID (*HWI_PROC_FUNC)(VOID); //中断函数指针 typedef struct tagHwiHandleForm { HWI_PROC_FUNC pfnHook; //中断处理函数 HWI_ARG_T uwParam; //中断处理函数参数 struct tagHwiHandleForm *pstNext; //节点,指向下一个中断,用于共享中断的情况 } HwiHandleForm; typedef struct tagIrqParam { //中断参数 int swIrq; // 软件中断 VOID *pDevId; // 设备ID const CHAR *pName; //名称 } HwiIrqParam; 注册硬中断 /****************************************************************************** 创建一个硬中断 中断创建,注册中断号、中断触发模式、中断优先级、中断处理程序。中断被触发时, handleIrq会调用该中断处理程序 ******************************************************************************/ LITE_OS_SEC_TEXT_INIT UINT32 LOS_HwiCreate(HWI_HANDLE_T hwiNum, //硬中断句柄编号 默认范围[0-127] HWI_PRIOR_T hwiPrio, //硬中断优先级 HWI_MODE_T hwiMode, //硬中断模式 共享和非共享 HWI_PROC_FUNC hwiHandler,//硬中断处理函数 HwiIrqParam *irqParam) //硬中断处理函数参数 { UINT32 ret; (VOID)hwiPrio; if (hwiHandler == NULL) {//中断处理函数不能为NULL return OS_ERRNO_HWI_PROC_FUNC_NULL; } if ((hwiNum > OS_USER_HWI_MAX) || ((INT32)hwiNum < OS_USER_HWI_MIN)) {//中断数区间限制 [32,96] return OS_ERRNO_HWI_NUM_INVALID; } #ifdef LOSCFG_NO_SHARED_IRQ //不支持共享中断 ret = OsHwiCreateNoShared(hwiNum, hwiMode, hwiHandler, irqParam); #else ret = OsHwiCreateShared(hwiNum, hwiMode, hwiHandler, irqParam); #endif return ret; } //创建一个共享硬件中断,共享中断就是一个中断能触发多个响应函数 STATIC UINT32 OsHwiCreateShared(HWI_HANDLE_T hwiNum, HWI_MODE_T hwiMode, HWI_PROC_FUNC hwiHandler, const HwiIrqParam *irqParam) { UINT32 intSave; HwiHandleForm *hwiFormNode = NULL; HwiHandleForm *hwiForm = NULL; HwiIrqParam *hwiParam = NULL; HWI_MODE_T modeResult = hwiMode & IRQF_SHARED; if (modeResult && ((irqParam == NULL) || (irqParam->pDevId == NULL))) { return OS_ERRNO_HWI_SHARED_ERROR; } HWI_LOCK(intSave);//中断自旋锁 hwiForm = &g_hwiForm[hwiNum];//获取中断处理项 if ((hwiForm->pstNext != NULL) && ((modeResult == 0) || (!(hwiForm->uwParam & IRQF_SHARED)))) { HWI_UNLOCK(intSave); return OS_ERRNO_HWI_SHARED_ERROR; } while (hwiForm->pstNext != NULL) {//pstNext指向 共享中断的各处理函数节点,此处一直撸到最后一个 hwiForm = hwiForm->pstNext;//找下一个中断 hwiParam = (HwiIrqParam *)(hwiForm->uwParam);//获取中断参数,用于检测该设备ID是否已经有中断处理函数 if (hwiParam->pDevId == irqParam->pDevId) {//设备ID一致时,说明设备对应的中断处理函数已经存在了. HWI_UNLOCK(intSave); return OS_ERRNO_HWI_ALREADY_CREATED; } } hwiFormNode = (HwiHandleForm *)LOS_MemAlloc(m_aucSysMem0, sizeof(HwiHandleForm));//创建一个中断处理节点 if (hwiFormNode == NULL) { HWI_UNLOCK(intSave); return OS_ERRNO_HWI_NO_MEMORY; } hwiFormNode->uwParam = OsHwiCpIrqParam(irqParam);//获取中断处理函数的参数 if (hwiFormNode->uwParam == LOS_NOK) { HWI_UNLOCK(intSave); (VOID)LOS_MemFree(m_aucSysMem0, hwiFormNode); return OS_ERRNO_HWI_NO_MEMORY; } hwiFormNode->pfnHook = hwiHandler;//绑定中断处理函数 hwiFormNode->pstNext = (struct tagHwiHandleForm *)NULL;//指定下一个中断为NULL,用于后续遍历找到最后一个中断项(见于以上 while (hwiForm->pstNext != NULL)处) hwiForm->pstNext = hwiFormNode;//共享中断 if ((irqParam != NULL) && (irqParam->pName != NULL)) { g_hwiFormName[hwiNum] = (CHAR *)irqParam->pName; } g_hwiForm[hwiNum].uwParam = modeResult; HWI_UNLOCK(intSave); return LOS_OK; } 解读 内核将硬中断进行编号,如: #define NUM_HAL_INTERRUPT_TIMER0 33 #define NUM_HAL_INTERRUPT_TIMER1 33 #define NUM_HAL_INTERRUPT_TIMER2 34 #define NUM_HAL_INTERRUPT_TIMER3 34 #define NUM_HAL_INTERRUPT_TIMER4 35 #define NUM_HAL_INTERRUPT_TIMER5 35 #define NUM_HAL_INTERRUPT_TIMER6 36 #define NUM_HAL_INTERRUPT_TIMER7 36 #define NUM_HAL_INTERRUPT_DMAC 60 #define NUM_HAL_INTERRUPT_UART0 38 #define NUM_HAL_INTERRUPT_UART1 39 #define NUM_HAL_INTERRUPT_UART2 40 #define NUM_HAL_INTERRUPT_UART3 41 #define NUM_HAL_INTERRUPT_UART4 42 #define NUM_HAL_INTERRUPT_TIMER NUM_HAL_INTERRUPT_TIMER4 例如:时钟节拍处理函数 OsTickHandler 就是在 HalClockInit中注册的 //硬时钟初始化 VOID HalClockInit(VOID) { // ... (void)LOS_HwiCreate(NUM_HAL_INTERRUPT_TIMER, 0xa0, 0, OsTickHandler, 0);//注册OsTickHandler到中断向量表 } //节拍中断处理函数 ,鸿蒙默认10ms触发一次 LITE_OS_SEC_TEXT VOID OsTickHandler(VOID) { UINT32 intSave; TICK_LOCK(intSave);//tick自旋锁 g_tickCount[ArchCurrCpuid()]++;// 累加当前CPU核tick数 TICK_UNLOCK(intSave); OsTimesliceCheck();//时间片检查 OsTaskScan(); /* task timeout scan *///扫描超时任务 例如:delay(300) #if (LOSCFG_BASE_CORE_SWTMR == YES) OsSwtmrScan();//扫描定时器,查看是否有超时定时器,加入队列 #endif } 鸿蒙是支持中断共享的,在OsHwiCreateShared中,将函数注册到g_hwiForm中.中断向量完成注册后,就是如何触发和回调的问题.触发在 鸿蒙内核源码分析(总目录)中断切换篇中已经讲清楚,触发是从底层汇编向上调用,调用的C函数就是HalIrqHandler 中断怎么触发的? 分两种情况: 通过硬件触发,比如按键,USB的插拔这些中断源向中断控制器发送电信号(高低电平触发或是上升/下降沿触发),中断控制器经过过滤后将信号发给对应的CPU处理,通过硬件改变PC和CPSR寄存值,直接跳转到中断向量(固定地址)执行. b reset_vector @开机代码 b _osExceptUndefInstrHdl @异常处理之CPU碰到不认识的指令 b _osExceptSwiHdl @异常处理之:软中断 b _osExceptPrefetchAbortHdl @异常处理之:取指异常 b _osExceptDataAbortHdl @异常处理之:数据异常 b _osExceptAddrAbortHdl @异常处理之:地址异常 b OsIrqHandler @异常处理之:硬中断 b _osExceptFiqHdl @异常处理之:快中断 通过软件触发,常见于核间中断的情况, 核间中断指的是几个CPU之间相互通讯的过程.以下为某一个CPU向其他CPU(可以是多个)发起让这些CPU重新调度LOS_MpSchedule的中断请求信号.最终是写了中断控制器的GICD_SGIR寄存器,这是一个由软件触发中断的寄存器.中断控制器会将请求分发给对应的CPU处理中断,即触发了OsIrqHandler. //给参数CPU发送调度信号 VOID LOS_MpSchedule(UINT32 target)//target每位对应CPU core { UINT32 cpuid = ArchCurrCpuid(); target &= ~(1U << cpuid);//获取除了自身之外的其他CPU HalIrqSendIpi(target, LOS_MP_IPI_SCHEDULE);//向目标CPU发送调度信号,核间中断(Inter-Processor Interrupts),IPI } //SGI软件触发中断(Software Generated Interrupt)通常用于多核间通讯 STATIC VOID GicWriteSgi(UINT32 vector, UINT32 cpuMask, UINT32 filter) { UINT32 val = ((filter & 0x3) << 24) | ((cpuMask & 0xFF) << 16) | (vector & 0xF); GIC_REG_32(GICD_SGIR) = val;//写SGI寄存器 } //向指定核发送核间中断 VOID HalIrqSendIpi(UINT32 target, UINT32 ipi) { GicWriteSgi(ipi, target, 0); } 中断统一处理入口函数 HalIrqHandler //硬中断统一处理函数,这里由硬件触发,调用见于 ..\arch\arm\arm\src\los_dispatch.S VOID HalIrqHandler(VOID) { UINT32 iar = GIC_REG_32(GICC_IAR);//从中断确认寄存器获取中断ID号 UINT32 vector = iar & 0x3FFU;//计算中断向量号 /* * invalid irq number, mainly the spurious interrupts 0x3ff, * gicv2 valid irq ranges from 0~1019, we use OS_HWI_MAX_NUM * to do the checking. */ if (vector >= OS_HWI_MAX_NUM) { return; } g_curIrqNum = vector;//记录当前中断ID号 OsInterrupt(vector);//调用上层中断处理函数 /* use orignal iar to do the EOI */ GIC_REG_32(GICC_EOIR) = iar;//更新中断结束寄存器 } VOID OsInterrupt(UINT32 intNum)//中断实际处理函数 { HwiHandleForm *hwiForm = NULL; UINT32 *intCnt = NULL; intCnt = &g_intCount[ArchCurrCpuid()];//当前CPU的中断总数量 ++ *intCnt = *intCnt + 1;//@note_why 这里没看明白为什么要 +1 #ifdef LOSCFG_CPUP_INCLUDE_IRQ //开启查询系统CPU的占用率的中断 OsCpupIrqStart();//记录本次中断处理开始时间 #endif #ifdef LOSCFG_KERNEL_TICKLESS OsTicklessUpdate(intNum); #endif hwiForm = (&g_hwiForm[intNum]);//获取对应中断的实体 #ifndef LOSCFG_NO_SHARED_IRQ //如果没有定义不共享中断 ,意思就是如果是共享中断 while (hwiForm->pstNext != NULL) { //一直撸到最后 hwiForm = hwiForm->pstNext;//下一个继续撸 #endif if (hwiForm->uwParam) {//有参数的情况 HWI_PROC_FUNC2 func = (HWI_PROC_FUNC2)hwiForm->pfnHook;//获取回调函数 if (func != NULL) { UINTPTR *param = (UINTPTR *)(hwiForm->uwParam); func((INT32)(*param), (VOID *)(*(param + 1)));//运行带参数的回调函数 } } else {//木有参数的情况 HWI_PROC_FUNC0 func = (HWI_PROC_FUNC0)hwiForm->pfnHook;//获取回调函数 if (func != NULL) { func();//运行回调函数 } } #ifndef LOSCFG_NO_SHARED_IRQ } #endif ++g_hwiFormCnt[intNum];//中断号计数器总数累加 *intCnt = *intCnt - 1; //@note_why 这里没看明白为什么要 -1 #ifdef LOSCFG_CPUP_INCLUDE_IRQ //开启查询系统CPU的占用率的中断 OsCpupIrqEnd(intNum);//记录中断处理时间完成时间 #endif } 解读 统一中断处理函数是一个通过一个中断号去找到注册函数的过程,分四步走: 第一步:取号,这号是由中断控制器的 GICC_IAR寄存器提供的,这是一个专门保存当前中断号的寄存器. 第二步:从注册表g_hwiForm中查询注册函数,同时取出参数. 第三步:执行函数,也就是回调注册函数,分有参和无参两种情况 func(...),在中断共享的情况下,注册函数会指向下一个注册函数pstNext,依次执行回调函数,这是中断共享的实现细节. typedef struct tagHwiHandleForm { HWI_PROC_FUNC pfnHook; //中断处理函数 HWI_ARG_T uwParam; //中断处理函数参数 struct tagHwiHandleForm *pstNext; //节点,指向下一个中断,用于共享中断的情况 } HwiHandleForm; 第四步:销号,本次中断完成了就需要消除记录,中断控制器也有专门的销号寄存器GICC_EOIR 另外的是一些统一数据,每次中断号处理内核都会记录次数,和耗时,以便定位/跟踪/诊断问题. 参与贡献 访问注解仓库地址 Fork 本仓库 >> 新建 Feat_xxx 分支 >> 提交代码注解 >> 新建 Pull Request 新建 Issue 喜欢请大方 点赞+关注+收藏 吧 各大站点搜 "鸿蒙内核源码分析" .欢迎转载,请注明出处. 进入 >> 百万汉字注解 百篇博客分析 精读鸿蒙源码 深挖地基工程 < gitee | csdn | oschina >

优秀的个人博客,低调大师

鸿蒙内核源码分析(时间管理篇) | Tick是操作系统的基本时间单位 | 中文注解HarmonyOS源码 | v35.01

百万汉字注解 >> 精读内核源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< Gitee | Github | CSDN | Coding > 百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,多站点每日同步更新< OSCHINA | CSDN | WeHarmony > 本篇说清楚时间概念 读本篇之前建议先读鸿蒙内核源码分析(总目录)其他篇. 时间概念太重要了,在鸿蒙内核又是如何管理和使用时间的呢? 时间管理以系统时钟 g_sysClock 为基础,给应用程序提供所有和时间有关的服务。 用户以秒、毫秒为单位计时. 操作系统以Tick为单位计时,这个认识很重要. 每秒的tick大小很大程度上决定了内核调度的次数多少. 当用户需要对系统进行操作时,例如任务挂起、延时等,此时需要时间管理模块对Tick和秒/毫秒进行转换。 熟悉两个概念: Cycle(周期):系统最小的计时单位。Cycle的时长由系统主时钟频率决定,系统主时钟频率就是每秒钟的Cycle数。 Tick(节拍):Tick是操作系统的基本时间单位,由用户配置的每秒Tick数决定,可大可小. 怎么去理解他们之间的关系呢?看几个宏定义就清楚了. #ifndef OS_SYS_CLOCK //HZ:是每秒中的周期性变动重复次数的计量 #define OS_SYS_CLOCK (get_bus_clk()) //系统主时钟频率 例如:50000000 即50微秒 #endif #ifndef LOSCFG_BASE_CORE_TICK_PER_SECOND #define LOSCFG_BASE_CORE_TICK_PER_SECOND 100 //每秒Tick数,意味着正常情况下每秒调度100次 #endif #define OS_CYCLE_PER_TICK (g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND) //每个tick多少机器周期 时钟周期(振荡周期) 在鸿蒙g_sysClock表示时钟周期,是CPU的赫兹,也就是上面说的Cycle,这是固定不变的,由硬件晶振的频率决定的. OsMain是内核运行的第一个C函数,首个子函数就是 osRegister,完成对g_sysClock的赋值 LITE_OS_SEC_TEXT_INIT VOID osRegister(VOID) { g_sysClock = OS_SYS_CLOCK; //获取CPU HZ g_tickPerSecond = LOSCFG_BASE_CORE_TICK_PER_SECOND;//每秒节拍数 默认100 即一个tick = 10ms return; } CPU周期也叫(机器周期) 在鸿蒙宏OS_CYCLE_PER_TICK表示机器周期,Tick由用户根据实际情况配置. 例如:主频为1G的CPU,其振荡周期为: 1吉赫 (GHz 109 Hz) = 1 000 000 000 Hz 当Tick为100时,则1 000 000 000/100 = 10000000 ,即一秒内可产生1千万个CPU周期.CPU就是用这1千万个周期去执行指令的. 指令周期 指令周期是执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期数也不同。 对于一些简单的的单字节指令,在取指令周期中,指令取出到指令寄存器后,立即译码执行,不再需要其它的机器周期。 对于一些比较复杂的指令,例如转移指令、乘法指令,则需要两个或者两个以上的机器周期。 通常含一个机器周期的指令称为单周期指令,包含两个机器周期的指令称为双周期指令。 Tick硬中断函数 LITE_OS_SEC_BSS volatile UINT64 g_tickCount[LOSCFG_KERNEL_CORE_NUM] = {0};//tick计数器,系统一旦启动,一直在++, 为防止溢出,这是一个 UINT64 的变量 LITE_OS_SEC_DATA_INIT UINT32 g_sysClock;//系统时钟,是绝大部分部件工作的时钟源,也是其他所有外设的时钟的来源 LITE_OS_SEC_DATA_INIT UINT32 g_tickPerSecond;//每秒Tick数,鸿蒙默认是每秒100次,即:10ms LITE_OS_SEC_BSS DOUBLE g_cycle2NsScale; //周期转纳秒级 /* spinlock for task module */ LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_tickSpin); //节拍器自旋锁 #define TICK_LOCK(state) LOS_SpinLockSave(&g_tickSpin, &(state)) /* * Description : Tick interruption handler *///节拍中断处理函数 ,鸿蒙默认10ms触发一次 LITE_OS_SEC_TEXT VOID OsTickHandler(VOID) { UINT32 intSave; TICK_LOCK(intSave); g_tickCount[ArchCurrCpuid()]++;//当前CPU核计数器 TICK_UNLOCK(intSave); #ifdef LOSCFG_KERNEL_VDSO OsUpdateVdsoTimeval(); #endif #ifdef LOSCFG_KERNEL_TICKLESS OsTickIrqFlagSet(OsTicklessFlagGet()); #endif #if (LOSCFG_BASE_CORE_TICK_HW_TIME == YES) HalClockIrqClear(); /* diff from every platform */ #endif OsTimesliceCheck();//时间片检查 OsTaskScan(); /* task timeout scan *///任务扫描 #if (LOSCFG_BASE_CORE_SWTMR == YES) OsSwtmrScan();//定时器扫描,看是否有超时的定时器 #endif } #ifdef __cplusplus #if __cplusplus } 解读 g_tickCount记录每个CPU核tick的数组,每次硬中断都触发 OsTickHandler,每个CPU核单独计数. OsTickHandler是内核调度的动力,其中会检查任务时间片是否用完,定时器是否超时.主动delay的任务是否需要被唤醒,其本质是个硬中断,在HalClockInit硬时钟初始化时创建的,具体在硬中断篇中会详细讲解. TICK_LOCK是tick操作的自旋锁,宏原型LOS_SpinLockSave在自旋锁篇中已详细介绍. 功能函数 #define OS_SYS_MS_PER_SECOND 1000 //一秒多少毫秒 //获取自系统启动以来的Tick数 LITE_OS_SEC_TEXT_MINOR UINT64 LOS_TickCountGet(VOID) { UINT32 intSave; UINT64 tick; /* * use core0's tick as system's timeline, * the tick needs to be atomic. */ TICK_LOCK(intSave); tick = g_tickCount[0];//使用CPU core0作为系统的 tick数 TICK_UNLOCK(intSave); return tick; } //每个Tick多少Cycle数 LITE_OS_SEC_TEXT_MINOR UINT32 LOS_CyclePerTickGet(VOID) { return g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND; } //毫秒转换成Tick LITE_OS_SEC_TEXT_MINOR UINT32 LOS_MS2Tick(UINT32 millisec) { if (millisec == OS_MAX_VALUE) { return OS_MAX_VALUE; } return ((UINT64)millisec * LOSCFG_BASE_CORE_TICK_PER_SECOND) / OS_SYS_MS_PER_SECOND; } //Tick转化为毫秒 LITE_OS_SEC_TEXT_MINOR UINT32 LOS_Tick2MS(UINT32 tick) { return ((UINT64)tick * OS_SYS_MS_PER_SECOND) / LOSCFG_BASE_CORE_TICK_PER_SECOND; } 说明 在CPU篇中讲过,0号CPU核默认为主核,默认获取自系统启动以来的Tick数使用的是g_tickCount[0] 因每个CPU核的tick是独立计数的,所以g_tickCount中各值是不一样的. 系统的Tick数在关中断的情况下不进行计数,因为OsTickHandler本质是由硬中断触发的,屏蔽硬中断的情况下就不会触发OsTickHandler,自然也就不会有g_tickCount[ArchCurrCpuid()]++的计数,所以系统Tick数不能作为准确时间使用. 追问下,什么情况下硬中断会被屏蔽? 编程示例 前提条件: 使用每秒的Tick数LOSCFG_BASE_CORE_TICK_PER_SECOND的默认值100。 配好OS_SYS_CLOCK系统主时钟频率。 时间转换 VOID Example_TransformTime(VOID) { UINT32 ms; UINT32 tick; tick = LOS_MS2Tick(10000); // 10000ms转换为tick dprintf("tick = %d \n",tick); ms = LOS_Tick2MS(100); // 100tick转换为ms dprintf("ms = %d \n",ms); } 时间转换结果 tick = 1000 ms = 1000 时间统计和时间延迟 LITE_OS_SEC_TEXT UINT32 LOS_TaskDelay(UINT32 tick); VOID Example_GetTime(VOID) { UINT32 cyclePerTick; UINT64 tickCount; cyclePerTick = LOS_CyclePerTickGet(); if(0 != cyclePerTick) { dprintf("LOS_CyclePerTickGet = %d \n", cyclePerTick); } tickCount = LOS_TickCountGet(); if(0 != tickCount) { dprintf("LOS_TickCountGet = %d \n", (UINT32)tickCount); } LOS_TaskDelay(200);//延迟200个tick tickCount = LOS_TickCountGet(); if(0 != tickCount) { dprintf("LOS_TickCountGet after delay = %d \n", (UINT32)tickCount); } } 时间统计和时间延迟结果 LOS_CyclePerTickGet = 495000 //取决于CPU的频率 LOS_TickCountGet = 1 //实际情况不一定是1的 LOS_TickCountGet after delay = 201 //实际情况不一定是201,但二者的差距会是200 喜欢就大方 点赞+关注+收藏 吧 各大站点搜 "鸿蒙内核源码分析" ,快速找到组织. 百万汉字注解 >> 精读内核源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< Gitee | Github | CSDN | Coding > 百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,多站点每日同步更新< OSCHINA | CSDN | WeHarmony >

优秀的个人博客,低调大师

| 中文注解HarmonyOS源码 | v31.01

百万汉字注解 >> 精读内核源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< Gitee | Github | CSDN | Coding > 百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,多站点每日同步更新< OSCHINA | CSDN | WeHarmony > 本篇说清楚定时器的实现 读本篇之前建议先读鸿蒙内核源码分析(总目录)其余篇. 运作机制 软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器。当经过设定的Tick数后,会触发用户自定义的回调函数。 软件定时器是系统资源,在模块初始化的时候已经分配了一块连续内存。 软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。定时时间短的定时器总是比定时时间长的靠近队列头,满足优先触发的准则。 软件定时器以Tick为基本计时单位,当创建并启动一个软件定时器时,鸿蒙会根据当前系统Tick时间及设置的定时时长确定该定时器的到期Tick时间,并将该定时器控制结构挂入计时全局链表。 当Tick中断到来时,在Tick中断处理函数中扫描软件定时器的计时全局链表,检查是否有定时器超时, 若有则将超时的定时器记录下来。Tick中断处理函数结束后,软件定时器任务(优先级为最高)被唤醒,在该任务中调用已经记录下来的定时器的回调函数。 定时器长什么样? typedef VOID (*SWTMR_PROC_FUNC)(UINTPTR arg);//函数指针, 赋值给 SWTMR_CTRL_S->pfnHandler,回调处理 typedef struct tagSwTmrCtrl {//软件定时器控制块 SortLinkList stSortList;//通过它挂到对应CPU核定时器链表上 UINT8 ucState; /**< Software timer state *///软件定时器的状态 UINT8 ucMode; /**< Software timer mode *///软件定时器的模式 UINT8 ucOverrun; /**< Times that a software timer repeats timing *///软件定时器重复计时的次数 UINT16 usTimerID; /**< Software timer ID *///软件定时器ID,唯一标识,由软件计时器池分配 UINT32 uwCount; /**< Times that a software timer works *///软件定时器工作的时间 UINT32 uwInterval; /**< Timeout interval of a periodic software timer *///周期性软件定时器的超时间隔 UINT32 uwExpiry; /**< Timeout interval of an one-off software timer *///一次性软件定时器的超时间隔 #if (LOSCFG_KERNEL_SMP == YES) UINT32 uwCpuid; /**< The cpu where the timer running on *///多核情况下,定时器运行的cpu #endif UINTPTR uwArg; /**< Parameter passed in when the callback function that handles software timer timeout is called *///回调函数的参数 SWTMR_PROC_FUNC pfnHandler; /**< Callback function that handles software timer timeout */ //处理软件计时器超时的回调函数 UINT32 uwOwnerPid; /** Owner of this software timer *///软件定时器所属进程ID号 } SWTMR_CTRL_S;//变量前缀 uc:UINT8 us:UINT16 uw:UINT32 解读 在多CPU核情况下,定时器是跟着CPU走的,每个CPU核都维护着独立的定时任务链表,上面挂的都是CPU核要处理的定时器. stSortList的背后是双向链表,这对钩子在定时器创建的那一刻会钩到CPU的swtmrSortLink上去. pfnHandler定时器时间到了的执行函数,由外界指定.uwArg为回调函数的参数 ucMode 为定时器模式,软件定时器提供了三类模式 单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动删除。 周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动停止定时器,否则将永远持续执行下去。 单次触发定时器,但这类定时器超时触发后不会自动删除,需要调用定时器删除接口删除定时器。 ucState 定时器状态. OS_SWTMR_STATUS_UNUSED(定时器未使用) 系统在定时器模块初始化时,会将系统中所有定时器资源初始化成该状态。 OS_SWTMR_STATUS_TICKING(定时器处于计数状态) 在定时器创建后调用LOS_SwtmrStart接口启动,定时器将变成该状态,是定时器运行时的状态。 OS_SWTMR_STATUS_CREATED(定时器创建后未启动,或已停止) 定时器创建后,不处于计数状态时,定时器将变成该状态。 定时器怎么管理? LITE_OS_SEC_BSS SWTMR_CTRL_S *g_swtmrCBArray = NULL; /* First address in Timer memory space *///定时器池 LITE_OS_SEC_BSS UINT8 *g_swtmrHandlerPool = NULL; /* Pool of Swtmr Handler *///用于注册软时钟的回调函数 LITE_OS_SEC_BSS LOS_DL_LIST g_swtmrFreeList; /* Free list of Software Timer *///空闲定时器链表 typedef struct {//处理软件定时器超时的回调函数的结构体 SWTMR_PROC_FUNC handler; /**< Callback function that handles software timer timeout */ //处理软件定时器超时的回调函数 UINTPTR arg; /**< Parameter passed in when the callback function that handles software timer timeout is called */ //调用处理软件计时器超时的回调函数时传入的参数 } SwtmrHandlerItem; 解读 三个全局变量可知,定时器是通过池来管理,在初始化阶段赋值. g_swtmrCBArray 定时器池,初始化中一次性创建1024个定时器控制块供使用 g_swtmrHandlerPool 回调函数池,回调函数也是统一管理的,申请了静态内存保存. 池中放的是 SwtmrHandlerItem 回调函数描述符. g_swtmrFreeList 空闲可供分配的定时器链表,鸿蒙的进程池,任务池,事件池都是这么处理的,没有印象的自行去 鸿蒙内核源码分析(总目录)< OSCHINA | CSDN > 翻看. g_swtmrFreeList上挂的是一个个的 SWTMR_CTRL_S 要搞明白 SWTMR_CTRL_S 和 SwtmrHandlerItem的关系,前者是一个定时器,后者是定时器时间到了去哪里干活. 初始化 -> OsSwtmrInit #define LOSCFG_BASE_CORE_SWTMR_LIMIT 1024 // 最大支持的软件定时器数 LITE_OS_SEC_TEXT_INIT UINT32 OsSwtmrInit(VOID) { UINT32 size; UINT16 index; UINT32 ret; SWTMR_CTRL_S *swtmr = NULL; UINT32 swtmrHandlePoolSize; UINT32 cpuid = ArchCurrCpuid(); if (cpuid == 0) {//确保以下代码块由一个CPU执行,g_swtmrCBArray和g_swtmrHandlerPool 是所有CPU共用的 size = sizeof(SWTMR_CTRL_S) * LOSCFG_BASE_CORE_SWTMR_LIMIT;//申请软时钟内存大小 swtmr = (SWTMR_CTRL_S *)LOS_MemAlloc(m_aucSysMem0, size); /* system resident resource */ //常驻内存 if (swtmr == NULL) { return LOS_ERRNO_SWTMR_NO_MEMORY; } (VOID)memset_s(swtmr, size, 0, size);//清0 g_swtmrCBArray = swtmr;//软时钟 LOS_ListInit(&g_swtmrFreeList);//初始化空闲链表 for (index = 0; index < LOSCFG_BASE_CORE_SWTMR_LIMIT; index++, swtmr++) { swtmr->usTimerID = index;//按顺序赋值 LOS_ListTailInsert(&g_swtmrFreeList, &swtmr->stSortList.sortLinkNode);//通过sortLinkNode将节点挂到空闲链表 } //想要用静态内存池管理,就必须要使用LOS_MEMBOX_SIZE来计算申请的内存大小,因为需要点前缀内存承载头部信息. swtmrHandlePoolSize = LOS_MEMBOX_SIZE(sizeof(SwtmrHandlerItem), OS_SWTMR_HANDLE_QUEUE_SIZE);//计算所有注册函数内存大小 //规划一片内存区域作为软时钟处理函数的静态内存池。 g_swtmrHandlerPool = (UINT8 *)LOS_MemAlloc(m_aucSysMem1, swtmrHandlePoolSize); /* system resident resource *///常驻内存 if (g_swtmrHandlerPool == NULL) { return LOS_ERRNO_SWTMR_NO_MEMORY; } ret = LOS_MemboxInit(g_swtmrHandlerPool, swtmrHandlePoolSize, sizeof(SwtmrHandlerItem));//初始化软时钟注册池 if (ret != LOS_OK) { return LOS_ERRNO_SWTMR_HANDLER_POOL_NO_MEM; } } //每个CPU都会创建一个属于自己的 OS_SWTMR_HANDLE_QUEUE_SIZE 的队列 ret = LOS_QueueCreate(NULL, OS_SWTMR_HANDLE_QUEUE_SIZE, &g_percpu[cpuid].swtmrHandlerQueue, 0, sizeof(CHAR *));//为当前CPU core 创建软时钟队列 maxMsgSize:sizeof(CHAR *) if (ret != LOS_OK) { return LOS_ERRNO_SWTMR_QUEUE_CREATE_FAILED; } ret = OsSwtmrTaskCreate();//每个CPU独自创建属于自己的软时钟任务,统一处理队列 if (ret != LOS_OK) { return LOS_ERRNO_SWTMR_TASK_CREATE_FAILED; } ret = OsSortLinkInit(&g_percpu[cpuid].swtmrSortLink);//每个CPU独自对自己软时钟链表排序初始化,为啥要排序因为每个定时器的时间不一样,鸿蒙把用时短的排在前面 if (ret != LOS_OK) { return LOS_ERRNO_SWTMR_SORTLINK_CREATE_FAILED; } return LOS_OK; } 代码解读: 每个CPU核都是独立处理定时器任务的,所以需要独自管理.OsSwtmrInit是负责初始化各CPU核定时模块功能的,注意在多CPU核时,OsSwtmrInit会被多次调用. cpuid == 0代表主CPU核, 它最早执行这个函数,所以g_swtmrCBArray和g_swtmrHandlerPool是共用的,系统默认最多支持 1024 个定时器和回调函数. 每个CPU核都创建了自己独立的 LOS_QueueCreate队列和任务OsSwtmrTaskCreate,并初始化了swtmrSortLink链表,关于链表排序可前往系列篇总目录 排序链表篇查看. 定时任务 -> 最高优先级 LITE_OS_SEC_TEXT_INIT UINT32 OsSwtmrTaskCreate(VOID) { UINT32 ret, swtmrTaskID; TSK_INIT_PARAM_S swtmrTask; UINT32 cpuid = ArchCurrCpuid();//获取当前CPU id (VOID)memset_s(&swtmrTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));//清0 swtmrTask.pfnTaskEntry = (TSK_ENTRY_FUNC)OsSwtmrTask;//入口函数 swtmrTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;//16K默认内核任务栈 swtmrTask.pcName = "Swt_Task";//任务名称 swtmrTask.usTaskPrio = 0;//哇塞! 逮到一个最高优先级的任务 @note_thinking 这里应该用 OS_TASK_PRIORITY_HIGHEST 表示 swtmrTask.uwResved = LOS_TASK_STATUS_DETACHED;//分离模式 #if (LOSCFG_KERNEL_SMP == YES) swtmrTask.usCpuAffiMask = CPUID_TO_AFFI_MASK(cpuid);//交给当前CPU执行这个任务 #endif ret = LOS_TaskCreate(&swtmrTaskID, &swtmrTask);//创建任务并申请调度 if (ret == LOS_OK) { g_percpu[cpuid].swtmrTaskID = swtmrTaskID;//全局变量记录 软时钟任务ID OS_TCB_FROM_TID(swtmrTaskID)->taskStatus |= OS_TASK_FLAG_SYSTEM_TASK;//告知这是一个系统任务 } return ret; } 代码解读: 内核为每个CPU处理单独创建任务来处理定时器, 任务即线程, 外界可理解为内核开设了一个线程跑定时器. 注意看任务的优先级 swtmrTask.usTaskPrio = 0; 0是最高优先级! 这并不多见! 内核会在第一时间响应软时钟任务. 系列篇CPU篇中讲过每个CPU都有自己的任务链表和定时器任务,g_percpu[cpuid].swtmrTaskID = swtmrTaskID; 表示创建的任务和CPU具体核进行了捆绑.从此swtmrTaskID负责这个CPU的定时器处理. 定时任务是一个系统任务,除此之外还有哪些是系统任务? 任务入口函数OsSwtmrTask ,是任务的执行体,类似于[Java 线程中的run()函数] usCpuAffiMask代表这个任务只能由这个CPU核来跑 队列消费者 -> OsSwtmrTask //软时钟的入口函数,拥有任务的最高优先级 0 级! LITE_OS_SEC_TEXT VOID OsSwtmrTask(VOID) { SwtmrHandlerItemPtr swtmrHandlePtr = NULL; SwtmrHandlerItem swtmrHandle; UINT32 ret, swtmrHandlerQueue; swtmrHandlerQueue = OsPercpuGet()->swtmrHandlerQueue;//获取定时器超时队列 for (;;) {//死循环获取队列item,一直读干净为止 ret = LOS_QueueRead(swtmrHandlerQueue, &swtmrHandlePtr, sizeof(CHAR *), LOS_WAIT_FOREVER);//一个一个读队列 if ((ret == LOS_OK) && (swtmrHandlePtr != NULL)) { swtmrHandle.handler = swtmrHandlePtr->handler;//超时中断处理函数,也称回调函数 swtmrHandle.arg = swtmrHandlePtr->arg;//回调函数的参数 (VOID)LOS_MemboxFree(g_swtmrHandlerPool, swtmrHandlePtr);//静态释放内存,注意在鸿蒙内核只有软时钟注册用到了静态内存 if (swtmrHandle.handler != NULL) { swtmrHandle.handler(swtmrHandle.arg);//回调函数处理函数 } } } } 代码解读: OsSwtmrTask是任务的执行体,只做一件事,消费定时器回调函数队列. 任务在跑一个死循环,不断在读队列.关于队列的具体操作不在此处细说,系列篇中已有专门的文章讲解,可前往查看. 每个CPU核都有属于自己的定时器回调函数队列,里面存放的是时间到了回调函数. 但队列的数据怎么来呢? OsSwtmrTask只是在不断的消费队列,那生产者在哪里呢? 就是 OsSwtmrScan 队列生产者 -> OsSwtmrScan LITE_OS_SEC_TEXT VOID OsSwtmrScan(VOID)//扫描定时器,如果碰到超时的,就放入超时队列 { SortLinkList *sortList = NULL; SWTMR_CTRL_S *swtmr = NULL; SwtmrHandlerItemPtr swtmrHandler = NULL; LOS_DL_LIST *listObject = NULL; SortLinkAttribute* swtmrSortLink = &OsPercpuGet()->swtmrSortLink;//拿到当前CPU的定时器链表 swtmrSortLink->cursor = (swtmrSortLink->cursor + 1) & OS_TSK_SORTLINK_MASK; listObject = swtmrSortLink->sortLink + swtmrSortLink->cursor; //由于swtmr是在特定的sortlink中,所以需要很小心的处理它,但其他CPU Core仍然有机会处理它,比如停止计时器 /* * it needs to be carefully coped with, since the swtmr is in specific sortlink * while other cores still has the chance to process it, like stop the timer. */ LOS_SpinLock(&g_swtmrSpin); if (LOS_ListEmpty(listObject)) { LOS_SpinUnlock(&g_swtmrSpin); return; } sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); ROLLNUM_DEC(sortList->idxRollNum); while (ROLLNUM(sortList->idxRollNum) == 0) { sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); LOS_ListDelete(&sortList->sortLinkNode); swtmr = LOS_DL_LIST_ENTRY(sortList, SWTMR_CTRL_S, stSortList); swtmrHandler = (SwtmrHandlerItemPtr)LOS_MemboxAlloc(g_swtmrHandlerPool);//取出一个可用的软时钟处理项 if (swtmrHandler != NULL) { swtmrHandler->handler = swtmr->pfnHandler; swtmrHandler->arg = swtmr->uwArg; if (LOS_QueueWrite(OsPercpuGet()->swtmrHandlerQueue, swtmrHandler, sizeof(CHAR *), LOS_NO_WAIT)) { (VOID)LOS_MemboxFree(g_swtmrHandlerPool, swtmrHandler); } } if (swtmr->ucMode == LOS_SWTMR_MODE_ONCE) { OsSwtmrDelete(swtmr); if (swtmr->usTimerID < (OS_SWTMR_MAX_TIMERID - LOSCFG_BASE_CORE_SWTMR_LIMIT)) { swtmr->usTimerID += LOSCFG_BASE_CORE_SWTMR_LIMIT; } else { swtmr->usTimerID %= LOSCFG_BASE_CORE_SWTMR_LIMIT; } } else if (swtmr->ucMode == LOS_SWTMR_MODE_NO_SELFDELETE) { swtmr->ucState = OS_SWTMR_STATUS_CREATED; } else { swtmr->ucOverrun++; OsSwtmrStart(swtmr); } if (LOS_ListEmpty(listObject)) { break; } sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); } LOS_SpinUnlock(&g_swtmrSpin); } 代码解读: OsSwtmrScan 函数是在系统时钟处理函数 OsTickHandler 中调用的,它就干一件事,不停的比较定时器是否超时 一旦超时就把定时器的回调函数扔到队列中,让 OsSwtmrTask去消费. 总结 定时器池 g_swtmrCBArray 存储内核所有的定时器,默认1024个,各CPU共享这个池 定时器响应函数池g_swtmrHandlerPool 存储内核所有的定时器响应函数,默认1024个,各CPU也共享这个池 每个CPU核都有独立的任务(线程)来处理定时器, 这个任务叫定时任务 每个CPU核都有独立的响应函数队列swtmrHandlerQueue,队列中存放该核时间到了的响应函数SwtmrHandlerItem 定时任务的优先级最高,循环读取队列swtmrHandlerQueue, swtmrHandlerQueue中存放是定时器时间到了的响应函数.并一一回调这些响应函数. OsSwtmrScan负责扫描定时器的时间是否到了,到了就往队列swtmrHandlerQueue中扔. 定时器有多种模式,包括单次,循环.所以循环类定时器的响应函数会多次出现在swtmrHandlerQueue中. 喜欢就请收藏吧 各大站点搜 "鸿蒙内核源码分析" ,快速找到组织. 百万汉字注解 >> 精读内核源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< Gitee | Github | CSDN | Coding > 百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,多站点每日同步更新< OSCHINA | CSDN | WeHarmony >

优秀的个人博客,低调大师

【移动开发】Android中WIFI开发总结(二)

搞了好几天终于有点眉目了,这里接着总结一下Android中WiFi的使用,在前面(http://smallwoniu.blog.51cto.com/3911954/1334951)我们已经简单了解了Wifi实际操作中所用到的几个主要类,今天就剖析一个WIFI搜索、创建、关闭、连接及一些简单的动画的综合案例。 首先,我们还是看一下我们项目的框架 MainActivity类:(处理wifi消息,加载UI) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 /** * 主界面 * @author ZHF * */ public class MainActivity extends Activity implements EventHandler{ //消息事件 public static final int m_nWifiSearchTimeOut = 0 ; // 搜索超时 public static final int m_nWTScanResult = 1 ; // 搜索到wifi返回结果 public static final int m_nWTConnectResult = 2 ; // 连接上wifi热点 public static final int m_nCreateAPResult = 3 ; // 创建热点结果 public static final int m_nUserResult = 4 ; // 用户上线人数更新命令(待定) public static final int m_nWTConnected = 5 ; // 点击连接后断开wifi,3.5秒后刷新adapter //一些常量 public static final String PACKAGE_NAME = "com.zhf.wifidemo.wifi" ; //应用包名 public static final String FIRST_OPEN_KEY = "version" ; //版本号信息 public static final String WIFI_AP_HEADER = "zhf_" ; public static final String WIFI_AP_PASSWORD = "zhf12345" ; //wifi操作事件(枚举)-->方便弹出对话框处理事件 private int wFOperateEnum = WFOperateEnum.NOTHING; //三个重要的类 public WFSearchProcess m_wtSearchProcess; //WiFi搜索进度条线程 public WifiAdmin m_wiFiAdmin; //Wifi管理类 public CreateAPProcess m_createAPProcess; //创建Wifi热点线程 //相关控件 private WFSearchAnimationFrameLayout m_FrameLWTSearchAnimation; //自定义雷达动画布局 private GifView m_gifRadar; //wifi信号动画布局 private LinearLayout m_linearLCreateAP; //创建热点View private ProgressBar m_progBarCreatingAP; //创建热点进度条 private TextView m_textVPromptAP; //创建热点进度条文字 private Button m_btnBack; //左上角返回按钮 private Button m_btnSearchWF; //右上角wifi搜索按钮 private Button m_btnCreateWF; //创建wifi热点 private ListView m_listVWT; //显示信息 private LinearLayout m_LinearLDialog; //提醒对话框 private TextView m_textVContentDialog; //对话框文本内容 private Button m_btnConfirmDialog, m_btnCancelDialog; //提醒对话框上的按钮 private TextView m_textVWTPrompt; //中间文字提示 private boolean m_isFirstOpen = true ; //标记是否是第一次打开 ArrayList<ScanResult> m_listWifi = new ArrayList(); //检测到热点信息列表 private WTAdapter m_wTAdapter; //网络列表适配器 public Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case m_nWifiSearchTimeOut: // 搜索超时 m_wtSearchProcess.stop(); m_FrameLWTSearchAnimation.stopAnimation(); m_listWifi.clear(); //网络列表 //设置控件 m_textVWTPrompt.setVisibility(View.VISIBLE); m_textVWTPrompt.setText( "需要重新搜索,点右上角重新搜索或创建新的热点..." ); break ; case m_nWTScanResult: //扫描到结果 m_listWifi.clear(); if (m_wiFiAdmin.mWifiManager.getScanResults() != null ) { for ( int i = 0 ; i < m_wiFiAdmin.mWifiManager.getScanResults().size(); i++) { ScanResult scanResult = m_wiFiAdmin.mWifiManager.getScanResults().get(i); //和指定连接热点比较,将其他的过滤掉! if (scanResult.SSID.startsWith(WIFI_AP_HEADER)) { m_listWifi.add(scanResult); } } if (m_listWifi.size() > 0 ) { m_wtSearchProcess.stop(); m_FrameLWTSearchAnimation.stopAnimation(); m_textVWTPrompt.setVisibility(View.GONE); //更新列表,显示出搜索到的热点 m_wTAdapter.setData(m_listWifi); m_wTAdapter.notifyDataSetChanged(); } } break ; case m_nWTConnectResult: //连接结果 m_wTAdapter.notifyDataSetChanged(); //刷新适配器数据 break ; case m_nCreateAPResult: //创建wifi热点结果 m_createAPProcess.stop(); m_progBarCreatingAP.setVisibility(View.GONE); //旋转进度条 if ((m_wiFiAdmin.getWifiApState() == 3 || m_wiFiAdmin.getWifiApState() == 13 ) && (m_wiFiAdmin.getApSSID().startsWith(WIFI_AP_HEADER))) { //设置控件 m_textVWTPrompt.setVisibility(View.GONE); m_linearLCreateAP.setVisibility(View.VISIBLE); m_btnCreateWF.setVisibility(View.VISIBLE); m_gifRadar.setVisibility(View.VISIBLE); m_btnCreateWF.setBackgroundResource(R.drawable.x_ap_close); m_textVPromptAP.setText( "热点创建成功!" + "\n热点名:" + m_wiFiAdmin.getApSSID()+ "\n连接密码:zhf12345" ); } else { m_btnCreateWF.setVisibility(View.VISIBLE); m_btnCreateWF.setBackgroundResource(R.drawable.x_wt_create); m_textVPromptAP.setText( "热点创建失败,您可以重新创建或者搜索其它热点" ); } break ; case m_nUserResult : //更新用户上线人数 break ; case m_nWTConnected: //点击连接后断开wifi,3.5s后刷新 m_wTAdapter.notifyDataSetChanged(); break ; } }; }; @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.wt_main); //搜索Wifi m_wtSearchProcess = new WFSearchProcess( this ); //创建Wifi热点 m_createAPProcess = new CreateAPProcess( this ); //wifi管理类 m_wiFiAdmin = WifiAdmin.getInstance( this ); //初始化View initView(); } /**初始化View**/ private void initView() { // 监听广播 WIFIBroadcast.ehList.add( this ); /******************************实例化布局**************************************/ m_linearLCreateAP = (LinearLayout) findViewById(R.id.create_ap_llayout_wt_main); //创建热点View m_progBarCreatingAP = (ProgressBar) findViewById(R.id.creating_progressBar_wt_main); //创建热点进度条 m_textVPromptAP = (TextView) findViewById(R.id.prompt_ap_text_wt_main); //创建热点进度条文字 m_FrameLWTSearchAnimation = ((WFSearchAnimationFrameLayout) findViewById(R.id.search_animation_wt_main)); // 搜索时的动画 m_listVWT = ((ListView) findViewById(R.id.wt_list_wt_main)); // 搜索到的热点listView //注意此处 m_wTAdapter = new WTAdapter( this , m_listWifi); m_listVWT.setAdapter(m_wTAdapter); m_textVWTPrompt = (TextView) findViewById(R.id.wt_prompt_wt_main); //中间提醒文字 m_gifRadar = (GifView) findViewById(R.id.radar_gif_wt_main); //gif动画 //提醒对话框布局 m_LinearLDialog = (LinearLayout) findViewById(R.id.dialog_layout_wt_main); m_textVContentDialog = (TextView) findViewById(R.id.content_text_wtdialog); m_btnConfirmDialog = (Button) findViewById(R.id.confirm_btn_wtdialog); m_btnCancelDialog = (Button) findViewById(R.id.cancel_btn_wtdialog); //左上角返回键 m_btnBack = (Button) findViewById(R.id.back_btn_wt_main); m_btnBack.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { onBackPressed(); //相当于调用系统返回键,结束当前Activity } }); //右上角搜索热点按钮 m_btnSearchWF = (Button) findViewById(R.id.search_btn_wt_main); m_btnSearchWF.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { if (!m_wtSearchProcess.running) { //搜索线程没有开启 //1.当前热点或wifi连接着 WIFI_STATE_ENABLED 3 //WIFI_AP_STATE_ENABLED 13 if (m_wiFiAdmin.getWifiApState() == 3 || m_wiFiAdmin.getWifiApState() == 13 ) { wFOperateEnum = WFOperateEnum.SEARCH; //搜索wifi事件 m_LinearLDialog.setVisibility(View.VISIBLE); ///wifi提示对话框显示 m_textVContentDialog.setText( "是否关闭当前热点去搜索其他热点?" ); return ; //跳出此方法,交由对话框来处理事件 } //2.当前没有热点或wifi连接着 if (!m_wiFiAdmin.mWifiManager.isWifiEnabled()) { //如果wifi没打开 m_wiFiAdmin.OpenWifi(); } m_textVWTPrompt.setVisibility(View.VISIBLE); //中间提示文字 m_textVWTPrompt.setText( "正在搜索附近的热点..." ); m_linearLCreateAP.setVisibility(View.GONE); //创建wifi热点布局消失 m_gifRadar.setVisibility(View.GONE); //热点连接动画消失 m_btnCreateWF.setBackgroundResource(R.drawable.x_wt_create); //更改按钮文字“创建” //开始搜索wifi m_wiFiAdmin.startScan(); m_wtSearchProcess.start(); //开启搜索线程 m_FrameLWTSearchAnimation.startAnimation(); //开启波纹动画 } else { //搜索线程开启着,再次点击按钮 //重新启动 m_wtSearchProcess.stop(); m_wiFiAdmin.startScan(); //开始搜索wifi m_wtSearchProcess.start(); } } }); //中间创建wifi热点按钮 m_btnCreateWF = (Button) findViewById(R.id.create_btn_wt_main); m_btnCreateWF.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { if (m_wiFiAdmin.getWifiApState() == 4 ) { // WIFI_STATE_UNKNOWN Toast.makeText(getApplicationContext(), "您的设备不支持热点创建!" , Toast.LENGTH_SHORT).show(); return ; } if (m_wiFiAdmin.mWifiManager.isWifiEnabled()) { //目前连着wifi wFOperateEnum = WFOperateEnum.CREATE; //wifi热点创建事件 m_LinearLDialog.setVisibility(View.VISIBLE); //对话框可用 m_textVContentDialog.setText( "创建热点会关闭当前的WiFi,确认继续?" ); return ; } if ((m_wiFiAdmin.getWifiApState() == 3 || m_wiFiAdmin.getWifiApState() == 13 ) && (!m_wiFiAdmin.getApSSID().startsWith(WIFI_AP_HEADER))) { //目前连接着其他热点名 wFOperateEnum = WFOperateEnum.CREATE; //wifi热点创建事件 m_LinearLDialog.setVisibility(View.VISIBLE); m_textVContentDialog.setText( "系统热点被占用,点确定开启热点以传输文件!" ); return ; } if (((m_wiFiAdmin.getWifiApState() == 3 ) || (m_wiFiAdmin.getWifiApState() == 13 )) && (m_wiFiAdmin.getApSSID().startsWith(WIFI_AP_HEADER))) { //目前连接着自己指定的Wifi热点 wFOperateEnum = WFOperateEnum.CLOSE; //wifi热点关闭事件 m_LinearLDialog.setVisibility(View.VISIBLE); m_textVContentDialog.setText( "关闭热点会中断当前传输,您确定这么做?" ); return ; } if (m_wtSearchProcess.running) { m_wtSearchProcess.stop(); //停止线程 m_FrameLWTSearchAnimation.stopAnimation(); //停止动画 } /******************点击创建热点时没有连接wifi或热点的情况*****************************/ //关闭Wifi m_wiFiAdmin.closeWifi(); //创建热点(名字,密码,加密类型,wifi/ap类型) m_wiFiAdmin.createWifiAP(m_wiFiAdmin.createWifiInfo(WIFI_AP_HEADER + getLocalHostName(), WIFI_AP_PASSWORD, 3 , "ap" ), true ); m_createAPProcess.start(); //开启创建热点线程 //将wifi信息列表设置到listview中 m_listWifi.clear(); m_wTAdapter.setData(m_listWifi); m_wTAdapter.notifyDataSetChanged(); //设置布局 m_linearLCreateAP.setVisibility(View.VISIBLE); //热点布局可用 m_progBarCreatingAP.setVisibility(View.VISIBLE); m_textVPromptAP.setText( "正在创建热点" ); //进度条文字 m_btnCreateWF.setVisibility(View.GONE); //点击一次不可再点 m_textVWTPrompt.setVisibility(View.GONE); } }); //对话框确认按钮 m_btnConfirmDialog.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { m_LinearLDialog.setVisibility(View.GONE); //让对话框布局消失 switch (wFOperateEnum) { //根据wifi操作事件 case WFOperateEnum.CLOSE: //关闭wifi热点 //设置布局 m_textVWTPrompt.setVisibility(View.VISIBLE); m_textVWTPrompt.setText( "热点已关闭!可以进行其他操作了!" ); //中间提醒文字 m_btnCreateWF.setBackgroundResource(R.drawable.x_wt_create); //按钮文字改回“创建” m_gifRadar.setVisibility(View.GONE); //热点动画停止 m_linearLCreateAP.setVisibility(View.GONE); //下部创建热点布局不可用 //关闭热点 m_wiFiAdmin.createWifiAP(m_wiFiAdmin.createWifiInfo(m_wiFiAdmin.getApSSID(), "zhf123456" , 3 , "ap" ), false ); break ; case WFOperateEnum.CREATE: //创建wifi热点 if (m_wtSearchProcess.running) { m_wtSearchProcess.stop(); //搜索wifi线程停止 m_FrameLWTSearchAnimation.stopAnimation(); //搜索wifi动画停止 } //关闭wifi m_wiFiAdmin.closeWifi(); //创建WiFi热点 m_wiFiAdmin.createWifiAP(m_wiFiAdmin.createWifiInfo(WIFI_AP_HEADER + getLocalHostName(), WIFI_AP_PASSWORD, 3 , "ap" ), true ); m_createAPProcess.start(); //刷新listView布局 m_listWifi.clear(); m_wTAdapter.setData(m_listWifi); m_wTAdapter.notifyDataSetChanged(); //设置布局 m_linearLCreateAP.setVisibility(View.VISIBLE); m_progBarCreatingAP.setVisibility(View.VISIBLE); //旋转进度条 m_btnCreateWF.setVisibility(View.GONE); m_textVWTPrompt.setVisibility(View.GONE); m_textVPromptAP.setText( "正在创建热点..." ); //进度条文字 break ; case WFOperateEnum.SEARCH: //搜索可用热点 //设置布局 m_textVWTPrompt.setVisibility(View.VISIBLE); m_textVWTPrompt.setText( "正在搜索附近的热点..." ); m_linearLCreateAP.setVisibility(View.GONE); //创建热点布局不可用 m_btnCreateWF.setVisibility(View.VISIBLE); m_btnCreateWF.setBackgroundResource(R.drawable.x_wt_create); //按钮文字改回“创建” m_gifRadar.setVisibility(View.GONE); //热点动画停止 m_linearLCreateAP.setVisibility(View.GONE); //下部创建热点布局不可用 //搜索热点线程关闭 if (m_createAPProcess.running) m_createAPProcess.stop(); //关闭热点 m_wiFiAdmin.createWifiAP(m_wiFiAdmin.createWifiInfo(m_wiFiAdmin.getApSSID(), WIFI_AP_PASSWORD, 3 , "ap" ), false ); //打开wifi m_wiFiAdmin.OpenWifi(); m_wtSearchProcess.start(); m_FrameLWTSearchAnimation.startAnimation(); break ; } } }); //对话框取消按钮事件 m_btnCancelDialog.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { //对话框布局消失 m_LinearLDialog.setVisibility(View.GONE); } }); } /**Wifi是否连接**/ private boolean isWifiConnect() { boolean isConnect = true ; //用于网络连接状态的检测 ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); if (!cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected()) isConnect = false ; return isConnect; } /**获取wifi热点状态**/ public boolean getWifiApState() { try { WifiManager localWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); Method m = localWifiManager.getClass().getMethod( "getWifiApState" , new Class[ 0 ]); int i = (Integer)(m.invoke(localWifiManager, new Object[ 0 ])); return ( 3 == i) || ( 13 == i); //WIFI_STATE_ENABLED 3 //WIFI_AP_STATE_ENABLED 13 } catch (Exception e) { e.printStackTrace(); } return false ; } /**获取手机信息**/ public String getLocalHostName() { String str1 = Build.BRAND; //主板 String str2 = Build.MODEL; //机型 if (- 1 == str2.toUpperCase().indexOf(str1.toUpperCase())) str2 = str1 + "_" + str2; return str2; } private void init() { //线程是否在运行 if ( this .m_wtSearchProcess.running || this .m_createAPProcess.running) { return ; } //没有连接上wifi或者是wifi热点 if (!isWifiConnect() && !getWifiApState()) { m_wiFiAdmin.OpenWifi(); m_wtSearchProcess.start(); //开启搜索wifi超时检测线程 m_wiFiAdmin.startScan(); //开启搜索wifi //开启搜索动画 m_FrameLWTSearchAnimation.startAnimation(); //设置控件 m_textVWTPrompt.setVisibility(View.VISIBLE); m_textVWTPrompt.setText( " 正在搜索附近的热点..." ); m_linearLCreateAP.setVisibility(View.GONE); m_btnCreateWF.setBackgroundResource(R.drawable.x_wt_create); } //连接上wifi if (isWifiConnect()) { this .m_wiFiAdmin.startScan(); this .m_wtSearchProcess.start(); this .m_FrameLWTSearchAnimation.startAnimation(); //设置控件 this .m_textVWTPrompt.setVisibility( 0 ); this .m_textVWTPrompt.setText( "正在搜索附近的热点..." ); this .m_linearLCreateAP.setVisibility(View.GONE); this .m_btnCreateWF.setBackgroundResource(R.drawable.x_wt_create); this .m_gifRadar.setVisibility(View.GONE); m_listWifi.clear(); if (m_wiFiAdmin.mWifiManager.getScanResults() != null ) { for ( int i = 0 ; i < m_wiFiAdmin.mWifiManager.getScanResults().size(); i++) { //识别出自己自定连接的wifi if (m_wiFiAdmin.mWifiManager.getScanResults().get(i).SSID.startsWith(WIFI_AP_HEADER)) { m_listWifi.add(m_wiFiAdmin.mWifiManager.getScanResults().get(i)); //将指定wifi添加进去 } } m_wTAdapter.setData(m_listWifi); //将连接的信息添加到listView中 m_wTAdapter.notifyDataSetChanged(); } //连接上wifi热点 if (getWifiApState()){ if (m_wiFiAdmin.getApSSID().startsWith(WIFI_AP_HEADER)) { //设置控件 m_textVWTPrompt.setVisibility(View.GONE); m_linearLCreateAP.setVisibility(View.VISIBLE); m_progBarCreatingAP.setVisibility(View.GONE); m_btnCreateWF.setVisibility(View.VISIBLE); m_gifRadar.setVisibility(View.VISIBLE); m_btnCreateWF.setBackgroundResource(R.drawable.x_ap_close); m_textVPromptAP.setText( "\n热点名:" + m_wiFiAdmin.getApSSID() + "\n连接密码:zhf12345" ); } } } } @Override protected void onDestroy() { // TODO Auto-generated method stub super .onDestroy(); WIFIBroadcast.ehList.remove( this ); } @Override public void handleConnectChange() { Message msg = mHandler.obtainMessage(m_nWTConnectResult); mHandler.sendMessage(msg); } @Override public void scanResultsAvaiable() { Message msg = mHandler.obtainMessage(m_nWTScanResult); mHandler.sendMessage(msg); } @Override public void wifiStatusNotification() { m_wiFiAdmin.mWifiManager.getWifiState(); //获取当前wifi状态 } } 貌似有点长。。。简单说明一下: 1.首先,初始化主界面(xml有些复杂,慢慢理一下就好了,不是什么大问题)接下来,处理三个重要的按钮事件:右上角的搜索wifi按钮、中间创建或关闭热点按钮、对话框中确定按钮。处理前两者事件时,需关注当前wifi状态,若为占用,需弹出提醒对话框来根据当前WiFi操作(WFOperateEnum类中定义好了)事件,来创建、关闭、搜索等具体事务。 2.之所以定义广播接收者,是为了监听当wifi状态发生变化时,能够及时更新界面 广播接收者WIFIBroadcast类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.zhf.wifidemo.broadcast; import java.util.ArrayList; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.net.wifi.WifiManager; import android.util.Log; /** * 监听wifi热点变化 * @author ZHF * */ public class WIFIBroadcast extends BroadcastReceiver{ public static ArrayList<EventHandler> ehList = new ArrayList<EventHandler>(); @Override public void onReceive(Context context, Intent intent) { //搜索到wifi热点结果的广播: "android.net.wifi.SCAN_RESULTS" if (intent.getAction().endsWith(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { Log.i( "WTScanResults---->扫描到了可用网络---->" , "android.net.wifi.SCAN_RESULTS" ); //遍历通知各个监听接口 for ( int i = 0 ; i < ehList.size(); i++) { ((EventHandler)ehList.get(i)).scanResultsAvaiable(); } //wifi打开或关闭状态变化 "android.net.wifi.WIFI_STATE_CHANGED" } else if (intent.getAction().endsWith(WifiManager.WIFI_STATE_CHANGED_ACTION)) { Log.i( "WTScanResults----->wifi状态变化--->" , "android.net.wifi.WIFI_STATE_CHANGED" ); //这里不需要连接一个SSID(wifi名称) for ( int j = 0 ; j < ehList.size(); j++) { ((EventHandler)ehList.get(j)).wifiStatusNotification(); } //连接上一个SSID后发出的广播,(注:与android.net.wifi.WIFI_STATE_CHANGED的区别) } else if (intent.getAction().endsWith(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { Log.i( "WTScanResults----->网络状态变化---->" , "android.net.wifi.STATE_CHANGE" ); for ( int m = 0 ; m < ehList.size(); m++) { ((EventHandler)ehList.get(m)).handleConnectChange(); } } } /** * 事件监听接口 * @author ZHF * */ public static abstract interface EventHandler { /**处理连接变化事件**/ public abstract void handleConnectChange(); /**扫描结果是有效事件**/ public abstract void scanResultsAvaiable(); /**wifi状态变化事件**/ public abstract void wifiStatusNotification(); } } 说明:监听系统wifi状态变化,内部定义了一个事件处理接口,当wifi状态发生变化时,让其实现类去发送消息更新界面。 CreateAPProcess类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package com.zhf.wifidemo.ui; import android.net.wifi.WifiManager; import android.os.Message; import com.zhf.wifidemo.wifi.MainActivity; /** * 创建Wifi热点 * @author ZHF * */ public class CreateAPProcess implements Runnable{ public MainActivity context; public boolean running = false ; private long startTime = 0L; private Thread thread = null ; public CreateAPProcess(MainActivity context) { super (); this .context = context; } @Override public void run() { while ( true ) { if (!running) return ; //WIFI_STATE_ENABLED 3 //WIFI_AP_STATE_ENABLED 13 if ((context.m_wiFiAdmin.getWifiApState() == WifiManager.WIFI_STATE_ENABLED) || (context.m_wiFiAdmin.getWifiApState() == 13 ) || (System.currentTimeMillis() - this .startTime >= 30000L)){ //wifi可用 或 热点可用 Message msg = context.mHandler.obtainMessage(context.m_nCreateAPResult); context.mHandler.sendMessage(msg); } try { Thread.sleep(5L); } catch (Exception e) { e.printStackTrace(); } } } public void start() { try { thread = new Thread( this ); running = true ; startTime = System.currentTimeMillis(); thread.start(); //开启线程 } catch (Exception e) { e.printStackTrace(); } } public void stop() { try { running = false ; thread = null ; startTime = 0L; } catch (Exception e) { // TODO: handle exception } } } 其他类文件具体看源码,当创建一个热点成功后,等待其他客户端搜索 上图: WIFIAdapte类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 /** * 网络列表适配器 * @author ZHF * */ public class WTAdapter extends BaseAdapter { private LayoutInflater mInflater; private List<ScanResult> mList; //扫描到的网络结果列表 private MainActivity mContext; public WTAdapter(MainActivity context, List<ScanResult> list) { // TODO Auto-generated constructor stub this .mContext = context; this .mList = list; this .mInflater = LayoutInflater.from(context); } //新加的一个函数,用来更新数据 public void setData(List<ScanResult> list) { this .mList = list; notifyDataSetChanged(); } @Override public int getCount() { return mList.size(); } @Override public Object getItem( int position) { return mList.get(position); } @Override public long getItemId( int position) { return position; } @Override public View getView( final int position, View convertView, ViewGroup parent) { //获取点击向的扫描结果 final ScanResult localScanResult = mList.get(position); //获取wifi类 // final WifiAdmin wifiAdmin = WifiAdmin.getInstance(mContext); final WifiAdmin wifiAdmin = mContext.m_wiFiAdmin; final ViewHolder viewHolder; if (convertView == null ) { viewHolder = new ViewHolder(); convertView = mInflater.inflate(R.layout.wtitem, null ); //加载布局模板控件 viewHolder.textVName = ((TextView) convertView.findViewById(R.id.name_text_wtitem)); viewHolder.textConnect = ((TextView) convertView.findViewById(R.id.connect_text_wtitem)); viewHolder.linearLConnectOk = ((LinearLayout) convertView.findViewById(R.id.connect_ok_layout_wtitem)); viewHolder.progressBConnecting = ((ProgressBar) convertView.findViewById(R.id.connecting_progressBar_wtitem)); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } //点击连接处理事件 viewHolder.textConnect.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { //创建wifi网络 WifiConfiguration localWifiConfiguration = wifiAdmin.createWifiInfo(localScanResult.SSID, MainActivity.WIFI_AP_PASSWORD, 3 , "wt" ); //添加到网络 wifiAdmin.addNetwork(localWifiConfiguration); //"点击链接"消失,显示进度条, viewHolder.textConnect.setVisibility(View.GONE); viewHolder.progressBConnecting.setVisibility(View.VISIBLE); viewHolder.linearLConnectOk.setVisibility(View.GONE); //点击后3.5s发送消息 mContext.mHandler.sendEmptyMessageDelayed(mContext.m_nWTConnected, 3500L); } }); // 点击断开处理事件 viewHolder.linearLConnectOk.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { //断开指定wifi热点 wifiAdmin.disconnectWifi(wifiAdmin.getWifiInfo().getNetworkId()); //"断开连接"消失,进度条显示 viewHolder.textConnect.setVisibility(View.GONE); viewHolder.progressBConnecting.setVisibility(View.VISIBLE); viewHolder.linearLConnectOk.setVisibility(View.GONE); //点击后3.5s发送消息 mContext.mHandler.sendEmptyMessageDelayed(mContext.m_nWTConnected, 3500L); } }); //初始化布局 viewHolder.textConnect.setVisibility(View.GONE); viewHolder.progressBConnecting.setVisibility(View.GONE); viewHolder.linearLConnectOk.setVisibility(View.GONE); viewHolder.textVName.setText(localScanResult.SSID); //显示热点名称 // 正连接的wifi信息 WifiInfo localWifiInfo = wifiAdmin.getWifiInfo(); if (localWifiInfo != null ) { try { //正在连接 if ((localWifiInfo.getSSID() != null )&& (localWifiInfo.getSSID().equals(localScanResult.SSID))) { viewHolder.linearLConnectOk.setVisibility(View.VISIBLE); return convertView; } } catch (NullPointerException e) { e.printStackTrace(); return convertView; } viewHolder.textConnect.setVisibility(View.VISIBLE); } return convertView; } public final class ViewHolder { public LinearLayout linearLConnectOk; public ProgressBar progressBConnecting; public TextView textConnect; public TextView textVName; } } 点击连接,实现客户连接,此时他们将处于同一网段内,接下来就可以自己实现传输数据,操控等一系列操作了。 在下一篇中我会继实现一个基于wif热点的多人聊天室,希望能帮助到大家! 源码:http://down.51cto.com/data/1036876 本文转自zhf651555765 51CTO博客,原文链接:http://blog.51cto.com/smallwoniu/1338618,如需转载请自行联系原作者

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册