FyListen——生命周期监听器(设计原理之理解生命周期)
FyListen——生命周期监听器(设计原理之理解生命周期)
FyListen 的核心原理有两个:
- 通过子Fragment对Activity、Fragment进行生命周期监听
- Java8 接口特性 default
1. 什么是上下文Context

这是一个装饰器模式, ContextImpl 是 Context 的基础实现类, ContextWrapper 是对 ContextImpl 的包装类。ContextThemeWrapper 是对装饰器的再装饰,又增加了一些功能。
1.1 如何获取 Context?
获取 Context 的方法有很多:getContext(), getBaseContext(), getApplication(), getApplicationContext()。我们来看一下他们的区别:
getApplication()和 getApplicationContext() 获取到的就是同一个对象,只是使用的范围不一样:
- getApplication() 只有 Activity 和 Service 才有。
- 想要在其他地方获取 Application 只能通过 getApplicationContext().
getContext(), getBaseContext()和 getApplicationContext() 有什么区别?
-
Activity,Service 和 Application 都有 getBaseContext(),getApplicationContext() 这两个方法,但没有 getContext() 方法。
-
getContext() 方法只有在 Fragment 中才有,获取的是寄主对象,也就是 Activity。
所以 Fragment 必须依赖于 Activity 存在
1.2 Application 有什么用?
Application 是全局单例,可以通过 getApplication() 和 getApplicationContext() 获取。
需要注意的是,如果在 application 中调用 context 的方法,必须在 attachBaseContext() 之后,在此之前 context 是没有赋值的。
2. Activity 与 Fragment 生命周期绑定原理
先来看一张Fragment状态转移图:(图来自稀土掘金)

这张图的解读可以看到下面这张表,Activity 管理 Fragment 生命周期的方式是在 Activity 的生命周期方法中调用 FragmentManager 的对应方法,通过 FragmentManager 将现有的 Fragment 迁移到下一个状态,同时触发相应的生命周期函数:
| Activity生命周期函数 | FragmentManager触发的方法 | Fragmet你状态转移 | Fragment生命周期回调 |
|---|---|---|---|
| onCreate() | dispatchCreate | INITIALIZING->CREATED | onAttach()、onCreate() |
| dispatchActivityCreated() | CREATED->ACTIVITY_CREATED | onCreateView()、onActivityCreated()、 | |
| onStart() | dispatchStart | ACTIVITY_CREATED->STARTED | onStart() |
| onResume() | dispatchResume | STARTED->RESUMED | onResume() |
| onPause | dispatchPause | RESUMED->STARTED | onPause() |
| onStop | dispatchStop | STARTED->STOPPED | onStop() |
| onDestroy | dispatchDestroy | STOPPED->ACTIVITY_CREATED->CREATED->CREATED->INITIALIZING | onDestroyView()、onDestroy()、onDetach() |
我们从源码角度来看一下是如何绑定的生命周期,首先我们需要知道,Activity 生命周期是由 AMS(ActivityManagerService)管理、回调的,这部分请大家在 AMS 专题学习。便于理解,我们提前看到源码中关于Fragment的状态转移代码处理:
需要提醒的Java基础是,switch: 一旦switch表达式与某个case分支匹配,则从该分支的语句开始执行,一致执行下去!其后所有case分支的语句也会被执行,直到遇到break语句!!!
//[FragmentManager.java]
void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive) {//newState是Fragment将要被转移到的状态state//f.mState是Fragment现有的状态stateif(f.mState <= newState){switch(f.mState){case Fragment.INITIALIZING://Fragment当前状态还在INITIALIZING,Activity调用了onCreate(),newState是CREATED,Fragment进行状态转移if(newState > Fragment.INITIALIZING){f.onAttach();dispatchOnFragmentAttached();if(!f.mIsCreated){//如果fragment还没create()f.performCreate();dispatchOnFragmentCreated();}else{//如果fragment过去已经create()过了,只不过是detach()了,并没有销毁。也就是这个Fragment虽然生命结束了,但后来又被加载使用了(复用了)f.mState = Fragment.CREATED;}}case Fragment.CREATED:if(newState > Fragment.CREATED){//Fragment当前状态在CREATED,但Activity紧接着又将状态调整为ACTIVITY_CREATED,Fragment进行状态转移f.mView = f.performCreateView();if(f.mView!=null){//如果没有view,就不往它的子Fragment进行生命周期回调分发了dispatchOnFragmentViewCreated();}f.performActivityCreated();dispatchOnFragmentActivityCreated();}case Fragment.ACTIVITY_CREATED:if(newState > Fragment.ACTIVITY_CREATED){//只进行状态转移f.mState = Fragment.STOPPED;}case Fragment.STOPPED:if(newState > Fragment.STOPPED){f.performStart();dispatchOnFragmentStarted();}case Fragment.STARTED:if(newState > Fragment.STARTED){f.performResume();dispatchOnFragmentResumed();}}}else if(f.mState > newState){//反向转移switch(f.mState){case Fragment.RESUMED:if(newState < Fragment.RESUMED){f.performPause();dispatchOnFragmentPaused();}case Fragment.STARTED:if(newState < Fragment.STARTED){f.performStop();dispatchOnFragmentStopped();}case Fragment.STOPPED:case Fragment.ACTIVITY_CREATED:if(newState < Fragment.ACTIVITY_CREATED){f.performDestroyView();dispatchOnFragmentViewDestroyed();}case Fragment.CREATED:if(newState < Fragment.CREATED){f.performDestroy();dispatchOnFragmentDestroy();}f.performDetach();dispatchOnFragmentDetached();}if(f.mState != newState){f.mState = newState;}}
}
我们了解了 Fragment 的生命周期是由于 Activity 生命周期的回调,设定了 Fragment 应当到的 newState,然后让 Fragment 进行状态转移,转移的过程中回调 Fragment 的生命周期。所以我们主要需要看 Activity 是如何设置 newState 的。从 AMS 回调 Activity 的 performXXX() 看起:
2.1 AMS->Activity.performCreate()
- 在 Activity 的 onCreate() 中将 Fragment 的状态转移目标 newState 设置为了 CREATED,状态转移期间回调了Fragment的 onAttach() 和 onCreate()
- performCreate()又将 Fragment 的状态转移目标newState设置为了 ACTIVITY_CREATED,Fragment在状态转移期间回调了Fragment的 onCreateView() 和 onActivityCreate()
//[Activity.java]
final void performCreate(Bundle icicle, PersistableBundle persistentState) {//调用Activity的onCreate()方法,其中回调了Fragment的onAttach()和onCreate()方法if (persistentState != null) {onCreate(icicle, persistentState);} else {onCreate(icicle);}//调用到moveToState,且newState为Fragment.ACTIVITY_CREATED,状态转移过程中,回调了Fragment的onCreateView()和onActivityCreate().mFragments.dispatchActivityCreated();
}protected void onCreate(@Nullable Bundle savedInstanceState) {//调用到moveToState,且newState为Fragment.CREATED,在状态转移期间回调了Fragment的onAttach()和onCreate()方法mFragments.dispatchCreate();//Application的Lifecycle回调dispatchActivityCreated(savedInstanceState);
}
2.2 AMS->performStart()
- 调用了 Activity 的 onStart()
- 将Fragment的状态转移目标newState设置为了STARTED,Fragment在期间回调了Fragment的 onStart() 方法
//[Activity.java]
final void performStart(String reason){//回调了 activity.onStart(),其中进行Application的Lifecycle回调mInstrumentation.callActivityOnStart(this);//调用到moveToState,且newState为Fragment.STARTED,其中回调了onStart()方法。虽然在switch中将Fragment的状态停留在了STOPPED,但在moveToState()方法的最后,又将Fragment的状态设置到了STARTED。mFragments.dispatchStart()
}
2.3 AMS->performResume()
- 调用了 Activity 的 onResume()
- 将Fragment的状态转移目标newState设置为了RESUMED,Fragment在状态转移期间回调了Fragment的 onResume();
//[Activity.java]
final void performResume(boolean followedByPause, String reason){//如果activity之前不是started的,会进入此方法,确保将activity变为startperformRestart(true,reason);//如果activity之前是start的://回调activity.onResume(),里面回调了 Application的LifecyclemInstrumentation.callActivityOnResume(this);//调用到moveToState,且newState为Fragment.RESUMED,状态转移期间回调了Fragment的onResume()方法mFragments.dispatchResume();
}
2.4 AMS->performPause()
【注意!!!】我们发现,从这里开始,是先从Fragment开始状态转换,而且是往回转移,newState<f.mState,然后才调用Activity的生命周期回调
- 注意,状态转移设为newState=STARTED,开始往回转移!!!
- Fragment状态转移期间回调了onPause()方法
- 然后才调用Activity的onPause()方法
//[Activity.java]
final void performPause(boolean preserveWindow, String reason) {//在此之前状态为RESUMED,由于f.mState<newState,将进行往回状态转移!//Fragment状态转移到STARTED期间回调了Fragment的onPause()mFragments.dispatchPause();//调用Activity的onPause(),同时回调Application的LifecycleonPause();
}
2.5 AMS->performStop()
【注意!!!】先从Fragment开始状态转换,而且是往回转移,newState<f.mState,然后才调用Activity的生命周期回调
- 注意,状态转移目标设为newState = STOPPED,往回状态转移
- Fragment状态转移期间回调了 onStop()方法
- 然后才调用Activity的onStop()方法
//[Activity.java]
final void performStop(boolean preserveWindow, String reason) {//Fragment状态转移到STOPPED,往回状态转移期间,回调了Fragment的onStop()方法mFragments.dispatchStop();//其中回调了Activity的onStop(),同时回调Application中的监听LifecyclemInstrumentation.callActivityOnStop(this);
}
2.6 AMS->performRestart()
Activity可能会在onStop()之后又onRestart(),需要注意的是,这里并没有回调Fragment的生命周期,只调用了activity的onRestart(),并调用performStart(),后续的生命周期回调就和onStart()之后的一致了
//[Activity.java]
final void performRestart(boolean start, String reason) {//先判断之前是否为Stop状态if(mStopped){mStopped = false;//调用Activity的onRestart()回调mInstrumentation.callActivityOnRestart(this);//里面调用了Activity的onStart(),并进行Fragment状态转移与生命周期回调performStart();}
}final void performStart(String reason) {//调用Activity.onStart(),并回调Application的Lifecycle,通知Application这个activity的生命周期现状mInstrumentation.callActivityOnStart(this);//newState=STARTED,Fragment状态从STOPPED到STARTED状态转移,期间回调了Fragment的onStart()mFragments.dispatchStart();
}
2.7 AMS->performDestroy()
【注意!!!】先从Fragment开始状态转换,而且是往回转移,newState<f.mState,然后才调用Activity的生命周期回调
- 先进行Fragment状态转移,newState=INITIALIZING,期间回调了Fragment的onDestroyView,onDestroy,onDetach
- Fragment状态转移完成,才进行Activity的onDestroy()
//[Activity.java]
final void performDestroy(){//先进行Fragment状态转移,newState=INITIALIZING,期间回调了Fragment的onDestroyView,onDestroy,onDetachmFragments.dispatchDestroy();//Fragment状态转移完成,才进行Activity的onDestroy()onDestroy();
}
我们回到 moveToState 来看一下是怎么连着回调三个Fragment生命周期的,需要注意,此时f.mState = STOPPED,newState=INITIALIZING:
需要提醒的Java基础是,switch: 一旦switch表达式与某个case分支匹配,则从该分支的语句开始执行,一致执行下去!其后所有case分支的语句也会被执行,直到遇到break语句!!!
//[FragmentManager.java]
void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive) {if(f.mState 《= newState){//...}else if(f.mState > newState){switch(f.mState){//...case Fragment.STOPPED://分支判断成功,继续执行后续case分支语句,直到遇到breakcase Fragment.ACTIVITY_CREATE:if(newState < Fragment.ACTIVITY_CREATED){//1. 回调Fragment的onDestroyView()f.performDestroyView();//把该事件也分发给f的子FragmentdispatchOnFragmentViewDestroyed();}case Fragment.CREATED:if(newState < Fragment.ACTIVITY_CREATED){//2. 回调Fragment的onDestroy()f.performDestroy();//把该事件也分发给f的子FragmentdispatchOnFragmentDestroyed();//3. 回调Fragment的onDetach()f.performDetach();//把该事件也分发给f的子FragmentdispatchOnFragmentDetached(); }//switch end}}//状态转移完成,最终更新f.mState = INITIALIZINGif(f.mState != newState){f.mState = newState;}
}
至此,我们分析完了生命周期回调。如果你比较细心,会发现这里除了处理fragment的生命周期,还有的时候会处理fragment的动画和view的显示。
2. 补充:Fragment生命周期除了自动由 Activity 来回调,也可以由用户操控 FragmentTansaction 进行 Fragment 的调度时候回调生命周期:
先来看这张图:(图源稀土掘金)

我们经常使用 FragmentTransaction 中的 add()、remove()、replace()、attach()、detach()、hide()、show() 等方法对 Fragment进行操作,这些方法都会使 Fragment 的状态发生变化,出发对应的生命周期函数。默认 Activity 处于 Resume 状态:
- add/remove 操作会引起 Fragment 在 INITIALIZING 和 RESUMED 这两个状态之间迁移
- attach/detach操作会引起 Fragment 在 CREATED 和 RESUMED 两个状态之间迁移
add() 需要注意的是,如果Activity处于 STARTED 状态,Fragment 是无法进入 RESUMED 状态的,只有当 Activity 进入 RESUME 状态才会通知 Fragment 进入 RESUMED。
hide/show方法内部其实调用了 FragmentTransaction 的add/remove 方法。
3. Fragment 与 子Fragment 生命周期绑定原理
我们观察到 Fragment 中也持有一个 FragmentManager:mChildFragmentManage 用于管理子Fragment。所以在生命周期事件分发过程中,会呈树形结构向所有子Fragment进行分发:
//[Fragment.java]
public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {//上层管理当前Fragment的FragmentManagerFragmentManagerImpl mFragmentManager;//当前Fragment用于管理子Fragment的fm,在生命周期回调中,会同时分发给 MChildFragmentManager 中的所有子FragmentFragmentManagerImpl mChildFragmentManager;//所属的父FragmentFragment mParentFragment;
}
FragmentManager 中管理着 Fragments:mAdds 和 mActive:
//[FragmentManager.java]
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {//当前活跃的FragmentSparseArray<Fragment> mActive;//所有已添加的Fragmentfinal ArrayList<Fragment> mAdded = new ArrayList<>();//管理的其他信息ArrayList<BackStackRecord> mBackStack;ArrayList<Fragment> mCreatedMenus;//...//分发生命周期:以dispatchResume()为例public void dispatchResume(){mStateSaved = false;//进行Fragment的状态转移dispatchMoveToState(Fragment.RESUMED);}//其中通过moveToState先进行预处理,即找到所有当前FragmentManager所管理的Fragment进行逐个事件分发private void dispatchMoveToState(int state) {if (mAllowOldReentrantBehavior) {moveToState(state, false);} else {try {mExecutingActions = true;moveToState(state, false);} finally {mExecutingActions = false;}}execPendingActions();}//拿到 mAdds 和 mActive 中的Fragment去分发事件(逐个让他们去进行状态转移)//注意这个是两个参数的 moveToState(),真正状态转移的moveToState()方法是四个参数的,注意区分void moveToState(int newState,boolean always){if (mActive != null) {final int numAdded = mAdded.size();for (int i = 0; i < numAdded; i++) {Fragment f = mAdded.get(i);//逐个fragment进行状态转移moveFragmentToExpectedState(f);}final int numActive = mActive.size();for (int i = 0; i < numActive; i++) {Fragment f = mActive.valueAt(i);if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {//逐个fragment进行状态转移moveFragmentToExpectedState(f);}}}}//moveFragmentToExceptedState()通过四个参数的moveToState()让fragment进行状态转移void moveFragmentToExceptedState(final Fragment f){int nextState = mCurState;//新状态被标记在成员变量,不作为参数传入,简化代码//进行状态转移,具体的我们之前已经看过了moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);}}
4. Activity与Fragment的树形结构
既然我们已经知道了 Fragment 的生命周期可以由 Activity 或者 Fragment 进行回调,那么我们就可以通过这个特性进行 Activity 或者 父Fragment生命周期的监听!!!主流框架 Glide 中生命周期监听的方式,也是利用了这个特性。我画了两张图,大家先感性地认知一下 Fragment 监听 Activity 生命周期的构造:
Activity与Fragment的树形结构:

我们可以看到,这是一个可以找到父节点的多叉树。
fragment想要与Activity通信可以通过Fragment中的 FragmentHostCallback类型对象mHost:里面存了Fragment所依存的Activity实例、主线程Handler。Fragment中启动Activity,也是通过这个对象,让上层去执行启动Activity的请求。
5. 使用 Java8 接口特性 default 进行外观设计
java8 之前,往接口里新加一个方法,那么所有的实现类都需要变动,都需要同步实现这个方法。
java8 给接口新增了两个关键字:default static
使得可以往接口里添加默认方法,子类可以无需变动。
同时,这也使得接口中的方法并不需要全都实现,只需要重写需要的方法即可!
相关文章:
FyListen——生命周期监听器(设计原理之理解生命周期)
FyListen——生命周期监听器(设计原理之理解生命周期) FyListen 的核心原理有两个: 通过子Fragment对Activity、Fragment进行生命周期监听Java8 接口特性 default 1. 什么是上下文Context 这是一个装饰器模式, ContextImpl 是 …...
Element UI框架学习篇(六)
Element UI框架学习篇(六) 1 删除数据 1.1 前台核心函数 1.1.1 elementUI中的消息提示框语法 //①其中type类型和el-button中的type类型是一致的,有info灰色,success绿色,danger红色,warning黄色,primary蓝色 //②message是你所要填写的提示信息 //③建议都用,因为比双引号…...
Python如何安装模块,python模块安装失败的原因以及解决办法
前言 今天来给刚开始学习python的朋友讲解一下 如何安装python模块, python模块安装失败的原因以及解决办法 很多朋友拿到代码之后,就开始复制粘贴 --> 然后右键进行运行 结果就是报错说 没有这个模块 得安装啥的 Python模块安装 一. 打开命令提示符 win …...
《NFL橄榄球》:洛杉矶闪电·橄榄1号位
洛杉矶闪电(英语:Los Angeles Chargers),又译“洛杉磯衝鋒者”。是一支位于加利福尼亚州洛杉矶郡英格尔伍德的职业美式橄榄球球队,现为美国橄榄球联合会西区成员之一。该队曾于1961年搬迁到圣地亚哥而改叫圣地亚哥电光…...
4.7 Python设置代码格式
随着你编写的程序越来越长,有必要了解一些代码格式设置约定。请花时让你的代码尽可能易于阅读;让代码易于阅读有助于你掌握程序是做什么的,也可以帮助他人理解你编写的代码。为确保所有人编写的代码的结构都大致一致,Python程序员都遵循一些格…...
Zabbix 构建监控告警平台(五)
Zabbix 自动发现Zabbix 自动注册1.Zabbix 自动发现 1.1前言 为了满足监控企业成千上万台服务器,因此我们需要使用Zabbix批量监控来实现。自动发现和自动注册。 1.2zabbix-server (一)1、创建自动发现规则 在“配置”->“自动发现”->“…...
2023关键词:挑战
未失踪人口回归… 好久不见,不经意间拖更2个多月。今天周末,外面淅淅沥沥下着小雨,这种窝在床上的时刻最适合写点东西了。 但是建议大家在办公或者写博客的时候尽量还是端正坐姿,我就是因为喜欢这样靠在床背上,长时间…...
Wifi wpa_supplicant 到驱动的联系
同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 从framework到wpa_supplicant的适配层,其中framework部分需要了注意的是wifiservic…...
【状态估计】基于二进制粒子群优化 (BPSO) 求解最佳 PMU优化配置研究【IEEE30、39、57、118节点】(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
python 将 .pdf 文件转为 .md
环境准备 pip install aspose-words 代码 doc aw.Document(r"pdf 文件路径\xxx.pdf") doc.save("Output.md") 来源:https://products.aspose.com/words/zh/python-net/conversion/...
【C语言】操作符详解
每天一篇博客,卷死各位。 文章目录前言1. 算术操作符2. 移位进制位的表示移位操作符1. 》--左移操作符2. 《--右移操作符3.位操作符4.赋值操作符5.单目操作符6.关系操作符7. 逻辑操作符8.条件操作符9.逗号操作符总结前言 在c语言学习中操作符尤为重要,而…...
微信小程序 学生选课系统--nodejs+vue
系统分为学生和管理员,教师三个角色 学生小程序端的主要功能有: 1.用户注册和登陆系统 2.查看选课介绍信息 3.查看查看课程分类 4.查看课程详情,在线选课,提交选课信息 5.在线搜索课程信息 6.用户个人中心修改个人资料 7.用户查看…...
leaflet 加载geojson文件并显示图形(示例代码051)
第051个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中加载geojson文件,将图形显示在地图上。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetMap无法加载,请加载其他来练习 文章目录 示例效果配置方式示例源代码(…...
【Kafka】ZK和Kafka集群的安装和配置
一、集群环境说明1. 虚拟机:192.168.223.101/103/1052. 系统版本:CentOS 7.93. JDK版本:11.0.18.0.14. Zookeeper版本:3.7.15. Kafka版本:2.13-2.8.2备注:无论是ZK,还是Kafka,都需要…...
并发编程出现的问题以及解决方式
解决并发编程出现的问题基于java内存模式的设计出现的问题基于java内存模式的设计,多线程操作一些共享的数据时,出现以下三个问题:1.不可见性问题:多个线程同时在各自的工作内存对共享数据进行操作,彼此之间不可见。操…...
[ linux ] linux 命令英文全称及解释
🍬 博主介绍 👨🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】🎉点赞➕评论➕收藏 养成习…...
C++11新特性
文章目录说在前面花括号{}初始化new的列表初始化STL相关容器的列表初始化相关语法格式容器列表初始化的底层原理forward_list和array与类型相关的新特性decltype左值引用和右值引用什么是左值,什么是右值左值和右值的本质区别右值引用如何理解右值引用std::move移动…...
【宝塔部署SpringBoot前后端不分离项目】含域名访问部署、数据库、反向代理、Nginx等配置
一定要弄懂项目部署的方方面面。当服务器上部署的项目过多时,端口号什么时候该放行、什么时候才会发生冲突?多个项目使用redis怎么防止覆盖?Nginx的配置会不会产生站点冲突?二级域名如何合理配置?空闲的时候要自己用服…...
从0到1一步一步玩转openEuler--11 openEuler基础配置-设置磁盘调度算法
11 openEuler基础配置-设置磁盘调度算法 文章目录11 openEuler基础配置-设置磁盘调度算法11.1 设置磁盘调度算法11.1.1 临时修改调度策略11.1.2 永久设置调度策略11.1 设置磁盘调度算法 本节介绍如何设置磁盘调度算法。 11.1.1 临时修改调度策略 例如将所有IO调度算法修改为…...
河道治理漂浮物识别监测系统 yolov7
河道治理漂浮物识别监测系统通过yolov7网络模型深度视觉分析技术,河道治理漂浮物识别监测算法模型实时检测着河道水面是否存在漂浮物、水浮莲以及生活垃圾等,识别到河道水面存在水藻垃圾等漂浮物,立即抓拍存档预警。You Only Look Once说的是…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
PH热榜 | 2025-06-08
1. Thiings 标语:一套超过1900个免费AI生成的3D图标集合 介绍:Thiings是一个不断扩展的免费AI生成3D图标库,目前已有超过1900个图标。你可以按照主题浏览,生成自己的图标,或者下载整个图标集。所有图标都可以在个人或…...
