SEO基础

SEO基础

Products

当前位置:首页 > SEO基础 >

如何优化php mysql网站源码以提升用户体验?

96SEO 2026-02-20 08:43 12


文章目录

Android消息机制分析ThreadLocal的工作原理ThreadLocal基础ThreadLocal实现原理

如何优化php mysql网站源码以提升用户体验?

MessageQueueLooperHandler的工作原理总结

本文用于记录Android的消息机制主要是指Handler的运行机制。

部分内容参考自《Android开发艺术探索》感兴趣可以阅读原书。

核心组件概述

Handler是Android消息机制的上层接口通过它可以轻松将一个任务从一线程切换到Handler所在的线程执行如由子线程切换到主线程更新UI。

具体来说有时候我们需要在子线程中执行耗时操作当耗时操作结束时希望将UI更新这时候我们就需要将线程切换到主线程了也就是UI线程因为Android不允许在子线程更新UI。

Handler的运行需要底层MessageQueue和Looper的支持。

MessageQueue也就是消息队列它可以存储消息并以队列的形式提供插入消息和删除消息操作注意虽然叫消息队列但它内存存储结构并不是队列而是单链表。

Looper我们可以将它理解为消息循环它会以无限循环的方式去消息队列中查找是否存在消息有的话就对消息进行处理没有的话就一直等待。

此外Looper中还有一个特殊的概念——ThreadLocalThreadLocal不是线程它的作用是在每个线程中独立的存储数据。

Handler在创建的时候需要使用当前线程的Looper来构建消息循环系统那么如何获取当前线程的Looper呢此时就要用到ThreadLocal我们知道ThreadLocal可以在不同线程互不干扰的存储并提供数据我们就可以通过ThreadLocal存储并获取每个线程的Looper了。

需要注意的是除主线程外其他线程默认是没有Looper的想要使用Handler就必须为线程创建Looper。

Android消息机制概述

Android的消息机制主要指Handler的运行机制以及Handler附带的MessageQueue和Looper的工作过程。

系统之所以提供Handler就是为了解决在子线程中无法更新UI的矛盾。

试想一下当我们通过网络请求从服务端拉取了一些数据需要更新在UI上为了避免造成ANR我们需要在子线程中执行网络请求操作那该怎么访问UI呢这时候我们就可以使用Handler因为它允许我们将一个任务由某个线程切换到另一个线程执行。

系统为什么不允许在子线程更新UI

Android的UI控件是线程不安全的如果在多线程并发访问可能导致UI控件处于不可预期的状态。

首先加锁会让UI访问的逻辑变得十分复杂其次加锁会降低UI的访问效率因为加锁会阻碍某些线程的执行。

接着概述一下Android的消息机制首先我们需要创建得到HandlerHandler创建时获取当前线程的Looper来构建内部的消息循环系统除主线程外都需要手动创建Looper。

Handler创建完毕后此时内部的Looper和MessageQueue就可以和Handler协调工作了。

Android消息机制分析

ThreadLocal是一个线程内部的数据存储类通过它可以在指定的线程中存储数据数据存储以后只有在指定线程中才可以获取到存储到数据对于其他线程数据是不可访问的。

当某些数据是以线程为作用域而且不同线程需要不同的数据副本时就可以考虑使用ThreadLocal。

如对于Handler来说他需要获取当前线程的Looper因为Looper的作用域是线程且不同线程有不同的Looper此时我们就可以通过ThreadLocal实现Looper在线程内的存取。

ThreadLocal的另一个使用场景是实现复杂逻辑下的对象传递。

比如监听器的传递有时一个线程中的任务过于复杂——函数调用栈比较深或代码入口多样我们需要监听器能够贯彻整个线程的执行过程这时候就可以使用ThreadLocal让监听器作为一个全局变量而存在在线程内部只需通过get方法获取监听器即可。

上面介绍了这么多理论知识接下来我们通过一个具体的代码实例来加深理解在这个实例中我们会创建一个Integer类型的ThreadLocal对象接着在主线程和三个子线程中分别设置并输出它的值

//创建ThreadLocalThreadLocalInteger

ThreadLocal();booleanThreadLocal.set(1);//设置主线程的值为1System.out.println(ThreadLocal的值为booleanThreadLocal.get());//子线程1new

Thread(new

{booleanThreadLocal.set(2);//设置子线程1的值为2System.out.println(ThreadLocal的值为booleanThreadLocal.get());}}).start();//子线程2new

Thread(new

{booleanThreadLocal.set(3);//设置子线程2的值为3System.out.println(ThreadLocal的值为booleanThreadLocal.get());}}).start();//子线程3new

Thread(new

{System.out.println(ThreadLocal的值为booleanThreadLocal.get());}}).start();打印结果如下

可以看到每个线程打印的都是我们设置的值因为在子线程3我们没有设置值所以打印的是null。

ThreadLocal之所以能实现上述的效果是因为不同线程访问同一个ThreadLocal的get方法ThreadLocal内部会从各自线程中取出一个数组然后根据当前ThreadLocal的索引去查找对应的value。

显然每个线程的数组是不同的所以通过ThreadLocal可以在指定线程存储数据其他线程无法访问不同线程间彼此互不干扰。

ThreadLocal实现原理

想弄清ThreadLocal的实现原理需要弄清它的set方法和get方法

set方法

1.首先我们会获取当前的线程然后根据当前的线程获取当前线程的ThreadLocalMap对象ThreadLocalMap使用一个数组作为存储结构数组元素为Entry传入的的ThreadLocal对象作为Entry的key要存入的值为value

2.如果获取到了当前线程的ThreadLocalMap对象那么就将数据存进去

3.如果获取的ThreadLocalMap对象是null那么就为当前线程创建一个ThreadLocalMap对象

源码分析

Thread.currentThread();//获取当前线程ThreadLocalMap

map

getMap(t);//获取当前线程对应的ThreadLocalMap对象if

(map

value);//这里的this就是当前线程的ThreadLocal对象}

else

value);}}这个set方法接受一个value参数这个参数可以指定为任何类型我们先获取当前线程接着获取当前线程的ThreadLocalMap对象如果当前线程有ThreadLocalMap对象那么就将数据直接存进去存进一个Entry对象当前线程的ThreadLocal对象作为key要存储的数据作为value如果当前线程没有ThreadLocalMap对象那么我们就需要去创建一个ThreadLocalMap对象

void

1;setThreshold(INITIAL_CAPACITY);}可以看到当调用createMap方法后回去创建一个ThreadLocalMap实例在创建ThreadLocalMap实例时会初始化一个数组这个数组就是我们真正用于存取数据的数据结构这个数组存储数据的数据类型是Entry也就是一个key-value的数据结构。

接着我们会通过传给我们的ThreadLocal参数计算出这个key的储存位置并将数据存到计算出的位置。

小结一下这个过程我们的每个Thread线程都有唯一的一个ThreadLocalMap对象这个ThreadLocalMap对象使用Enrty[]数组存储数据Entry[]数组的每个元素都是以当前线程的ThreadLocal对象作为key要存储的数据作为value的数据结构。

另外提一下使用数组是因为同一线程中可能有多个不同类型的ThreadLocal对象它们计算出的key是不同的就对应了在数组上不同的存储位置。

get方法

Thread.currentThread();//获取当前线程ThreadLocalMap

map

getMap(t);//获取当前线程对应的ThreadLocalMap对象if

(map

setInitialValue();//表示当前线程都没有ThreadLocalMap对象或者有ThreadLocalMap对象当不存在ThreadLocal关联的Entry对象去做一些初始化的工作}同样先获取当前线程再获取当前线程的ThreadLocalMap对象如果对象不为空那么就根据key也就是ThreadLocal对象去获取Entry对象最后获取到的Entry对象不为空时将数据返回获取Entry对象的方法如下

private

e);}根据传入的key计算出key的存储位置这个过程在我们set()方法存储数据时是相对应的都是根据keyThreadLocal对象计算出在数组上的存储位置接着直接存储位置直接获得Entry对象返回。

通过ThreadLocal的get方法和set方法可以看出不同线程操作的都是各自线程中的ThreadLocalMap对象的Entry[]数组所以ThreadLocal可以实现线程间存取数据互不干扰。

MessageQueue

MessageQueue也就是消息队列虽然它叫做消息队列但它的实现方法并不是队列而是使用一个单链表来实现消息列表。

MessageQueue主要有两个方法enqueueMessage()插入消息和next()读取消息需要注意的是读取消息时会伴随着消息的删除操作也就是在读取完这条消息后将其从消息队列中移除。

Looper

Looper在Android消息机制中扮演循环者的角色它会不停在MessageQueue中查看是否有新消息如果有新消息就立即处理否则就一直阻塞在那。

在创建Looper时会创建一个MessageQueue对象并获取当前的线程源码如下

private

MessageQueue(quitAllowed);mThread

Thread.currentThread();}上面我们介绍过Handler的工作需要Looper如果一个线程没有Looper对象使用Handler对象就会报错那么我们怎么获取Looper对象呢答案是直接调用Looper.prepear()方法即可创建一个Looper对象然后通过Looper.loop()来开启消息循环如下

//子线程1new

Handler();Looper.loop();}}).start();Looper也是可以退出的有quit()和quitSafely()两种方法使用quit()会直接退出Looper而quitSafely()会设置一个退出标记接着把消息队列中已有的消息处理完毕才能安全退出。

当Looper退出后使用Handler的send方法发消息会返回false。

如果我们在子线程中创建了Looper那么我们就应该在所有事做完之后调用上述两个方法退出Looper。

上面我们还提到了一个loop()方法只有调用Looper.loop()方法后消息循环系统才会真正的起作用我们可以看看它的源码

for

}首先我们可以知道loop方法是一个死循环只有当!loopOnce(me,

ident,

thresholdOverride条件满足时才会跳出循环接着我们打开loopOnce方法看看首先里面有段代码

Message

}loop()方法会调用MessageQueue的next()方法一直获取消息此时一直会处于循环状态。

只有当MessageQueue的next()方法返回了null循环才会结束那么什么情况下MessageQueue的next()方法才会返回null呢。

答案是当Looper调用了quit()方法或quitSafely()方法时我们看看这两个方法

public

}可以看到Looper的quit()方法和quitSefely()方法这两个方法调用了MessageQueue的quit()方法我们打开MessageQueue来看看它的quit()方法

void

{removeAllFutureMessagesLocked();}

else

false.nativeWake(mPtr);}}MessageQueue的quit()方法接收一个布尔型参数用来表示是不是安全的也就是区分Looper的quit()方法和quitSafely()方法。

MessageQueue的quit()方法中有段代码用来真正进行处理

(safe)

{//quitSafely()安全退出removeAllFutureMessagesLocked();

else

{//quit()removeAllMessagesLocked();

}点进这两个方法你会发现这两个方法都是将MessageQueue.next()方法返回的值设为null这样我们的Looper.quit()和Looper.quitSafely()方法就会执行Looper退出。

如果MessageQueue的next()方法返回的值不为null那么就会调用loopOnce()方法里的msg.target.dispatchMessage(msg);方法来处理这条消息msg.target表示发送这条消息的Handler对象也就是说Handler发送完消息这条消息最终又交给Handler的dispatchMessage()方法来处理了但dispatchMessage()方法是在Looper.loop()方法中执行的。

这样我们就可以实现在子线程执行耗时操作然后在子线程调用Looper.loop()方法执行Handler的dispatchMessage()方法通知Handler来处理消息最后到主线程也就是Handler所在的线程进行处理消息的操作了。

Handler主要包括消息的发送和接收。

发送就是通过post的一系列方法以及send的一系列方法来完成的其实post的一系列方法最终也是通过send的一系列方法来实现的。

Handler的sendMessage()方法经过层层调用最终会调用到一个enqueueMessage()方法是不是有点眼熟在MessageQueue中我们就是通过这个方法插入消息在Handler的enqueueMessage()方法就是调用MessageQueue.enqueueMessage()方法来向消息队列中添加一条消息Handler的enqueueMessage()方法如下

private

ThreadLocalWorkSource.getUid();if

(mAsynchronous)

{msg.setAsynchronous(true);}return

uptimeMillis);//调用MessageQueue的enqueueMessage()方法插入一条消息}当调用Handler的send()方法时MessageQueue的enqueueMessage()方法执行向消息队列中插入一条消息同时MessageQueue的next()方法执行将这条消息返回给LooperLooper通过loop()方法一直在MessageQueue中获取消息Looper接收到消息就开始处理最终消息交给Handler处理即Handler.dispatchMessage()方法被调用此时Handler进入处理消息阶段我们可以打开Handler的源码看看dispatchMessage()方法

public

{return;}}handleMessage(msg);}}这个方法中首先检查Message的callback对象一个Runnable对象就是Handler的post()方法传递的Runnable参数不为空使用handleCallback(msg)方法处理消息。

如果没有传递Runnable参数也就是msg.callback为空那么就接着判断mCallback是否为空Callback是一个空接口其源码定义如下

public

msg);}通常我们在开发过程中创建Handler的方式是派生一个Handler的子类并重写其handlerMessage()方法来进行具体的消息处理。

当我们不想派生子类时就可以通过Callback这样创建一个Handler对象Handler

handlernew

Handler(callback)这样我们就不需要派生Handler的子类。

其实上面代码不是特别重要重要的是handleMessage(msg)这个方法。

Handler的dispatchMessage()方法最终调用handleMessage()方法来进行具体的消息处理。

Handler通过sendMessage()方法向MessageQueue中添加MessageMessageQueue的enqueueMessage()方法执行Looper通过loop()方法不停检查MessageQueue中是否有新消息(除非调用了Looper的quit()方法或quitSafely()方法)有就执行消息MessageQueue的next()方法执行处理消息时Looper调用Handler的dispatchMessage()方法将消息交给Handler的HandleMessage()方法执行

最终我们就可以实现在子线程执行耗时操作再在子线程通过dispatchMessage()方法通知主线程执行handleMessage()方法在主线程更新UI。

分享到此结束共勉



SEO优化服务概述

作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。

百度官方合作伙伴 白帽SEO技术 数据驱动优化 效果长期稳定

SEO优化核心服务

网站技术SEO

  • 网站结构优化 - 提升网站爬虫可访问性
  • 页面速度优化 - 缩短加载时间,提高用户体验
  • 移动端适配 - 确保移动设备友好性
  • HTTPS安全协议 - 提升网站安全性与信任度
  • 结构化数据标记 - 增强搜索结果显示效果

内容优化服务

  • 关键词研究与布局 - 精准定位目标关键词
  • 高质量内容创作 - 原创、专业、有价值的内容
  • Meta标签优化 - 提升点击率和相关性
  • 内容更新策略 - 保持网站内容新鲜度
  • 多媒体内容优化 - 图片、视频SEO优化

外链建设策略

  • 高质量外链获取 - 权威网站链接建设
  • 品牌提及监控 - 追踪品牌在线曝光
  • 行业目录提交 - 提升网站基础权威
  • 社交媒体整合 - 增强内容传播力
  • 链接质量分析 - 避免低质量链接风险

SEO服务方案对比

服务项目 基础套餐 标准套餐 高级定制
关键词优化数量 10-20个核心词 30-50个核心词+长尾词 80-150个全方位覆盖
内容优化 基础页面优化 全站内容优化+每月5篇原创 个性化内容策略+每月15篇原创
技术SEO 基本技术检查 全面技术优化+移动适配 深度技术重构+性能优化
外链建设 每月5-10条 每月20-30条高质量外链 每月50+条多渠道外链
数据报告 月度基础报告 双周详细报告+分析 每周深度报告+策略调整
效果保障 3-6个月见效 2-4个月见效 1-3个月快速见效

SEO优化实施流程

我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:

1

网站诊断分析

全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。

2

关键词策略制定

基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。

3

技术优化实施

解决网站技术问题,优化网站结构,提升页面速度和移动端体验。

4

内容优化建设

创作高质量原创内容,优化现有页面,建立内容更新机制。

5

外链建设推广

获取高质量外部链接,建立品牌在线影响力,提升网站权威度。

6

数据监控调整

持续监控排名、流量和转化数据,根据效果调整优化策略。

SEO优化常见问题

SEO优化一般需要多长时间才能看到效果?
SEO是一个渐进的过程,通常需要3-6个月才能看到明显效果。具体时间取决于网站现状、竞争程度和优化强度。我们的标准套餐一般在2-4个月内开始显现效果,高级定制方案可能在1-3个月内就能看到初步成果。
你们使用白帽SEO技术还是黑帽技术?
我们始终坚持使用白帽SEO技术,遵循搜索引擎的官方指南。我们的优化策略注重长期效果和可持续性,绝不使用任何可能导致网站被惩罚的违规手段。作为百度官方合作伙伴,我们承诺提供安全、合规的SEO服务。
SEO优化后效果能持续多久?
通过我们的白帽SEO策略获得的排名和流量具有长期稳定性。一旦网站达到理想排名,只需适当的维护和更新,效果可以持续数年。我们提供优化后维护服务,确保您的网站长期保持竞争优势。
你们提供SEO优化效果保障吗?
我们提供基于数据的SEO效果承诺。根据服务套餐不同,我们承诺在约定时间内将核心关键词优化到指定排名位置,或实现约定的自然流量增长目标。所有承诺都会在服务合同中明确约定,并提供详细的KPI衡量标准。

SEO优化效果数据

基于我们服务的客户数据统计,平均优化效果如下:

+85%
自然搜索流量提升
+120%
关键词排名数量
+60%
网站转化率提升
3-6月
平均见效周期

行业案例 - 制造业

  • 优化前:日均自然流量120,核心词无排名
  • 优化6个月后:日均自然流量950,15个核心词首页排名
  • 效果提升:流量增长692%,询盘量增加320%

行业案例 - 电商

  • 优化前:月均自然订单50单,转化率1.2%
  • 优化4个月后:月均自然订单210单,转化率2.8%
  • 效果提升:订单增长320%,转化率提升133%

行业案例 - 教育

  • 优化前:月均咨询量35个,主要依赖付费广告
  • 优化5个月后:月均咨询量180个,自然流量占比65%
  • 效果提升:咨询量增长414%,营销成本降低57%

为什么选择我们的SEO服务

专业团队

  • 10年以上SEO经验专家带队
  • 百度、Google认证工程师
  • 内容创作、技术开发、数据分析多领域团队
  • 持续培训保持技术领先

数据驱动

  • 自主研发SEO分析工具
  • 实时排名监控系统
  • 竞争对手深度分析
  • 效果可视化报告

透明合作

  • 清晰的服务内容和价格
  • 定期进展汇报和沟通
  • 效果数据实时可查
  • 灵活的合同条款

我们的SEO服务理念

我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。

提交需求或反馈

Demand feedback