当前位置: 首页 > news >正文

FyListen——生命周期监听器(设计原理之理解生命周期)

FyListen——生命周期监听器(设计原理之理解生命周期)

FyListen 的核心原理有两个:

  1. 通过子Fragment对Activity、Fragment进行生命周期监听
  2. 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状态转移图:(图来自稀土掘金)

img

这张图的解读可以看到下面这张表,Activity 管理 Fragment 生命周期的方式是在 Activity 的生命周期方法中调用 FragmentManager 的对应方法,通过 FragmentManager 将现有的 Fragment 迁移到下一个状态,同时触发相应的生命周期函数:

Activity生命周期函数FragmentManager触发的方法Fragmet你状态转移Fragment生命周期回调
onCreate()dispatchCreateINITIALIZING->CREATEDonAttach()、onCreate()
dispatchActivityCreated()CREATED->ACTIVITY_CREATEDonCreateView()、onActivityCreated()、
onStart()dispatchStartACTIVITY_CREATED->STARTEDonStart()
onResume()dispatchResumeSTARTED->RESUMEDonResume()
onPausedispatchPauseRESUMED->STARTEDonPause()
onStopdispatchStopSTARTED->STOPPEDonStop()
onDestroydispatchDestroySTOPPED->ACTIVITY_CREATED->CREATED->CREATED->INITIALIZINGonDestroyView()、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当前状态还在INITIALIZINGActivity调用了onCreate(),newState是CREATEDFragment进行状态转移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()

  1. 在 Activity 的 onCreate() 中将 Fragment 的状态转移目标 newState 设置为了 CREATED,状态转移期间回调了Fragment的 onAttach() 和 onCreate()
  2. 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()

  1. 调用了 Activity 的 onStart()
  2. 将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()

  1. 调用了 Activity 的 onResume()
  2. 将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的生命周期回调

  1. 注意,状态转移设为newState=STARTED,开始往回转移!!!
  2. Fragment状态转移期间回调了onPause()方法
  3. 然后才调用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的生命周期回调

  1. 注意,状态转移目标设为newState = STOPPED,往回状态转移
  2. Fragment状态转移期间回调了 onStop()方法
  3. 然后才调用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的生命周期回调

  1. 先进行Fragment状态转移,newState=INITIALIZING,期间回调了Fragment的onDestroyView,onDestroy,onDetach
  2. 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 的调度时候回调生命周期:

先来看这张图:(图源稀土掘金)

img

我们经常使用 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——生命周期监听器&#xff08;设计原理之理解生命周期&#xff09; FyListen 的核心原理有两个&#xff1a; 通过子Fragment对Activity、Fragment进行生命周期监听Java8 接口特性 default 1. 什么是上下文Context 这是一个装饰器模式&#xff0c; 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模块安装失败的原因以及解决办法 很多朋友拿到代码之后&#xff0c;就开始复制粘贴 --> 然后右键进行运行 结果就是报错说 没有这个模块 得安装啥的 Python模块安装 一. 打开命令提示符 win …...

《NFL橄榄球》:洛杉矶闪电·橄榄1号位

洛杉矶闪电&#xff08;英语&#xff1a;Los Angeles Chargers&#xff09;&#xff0c;又译“洛杉磯衝鋒者”。是一支位于加利福尼亚州洛杉矶郡英格尔伍德的职业美式橄榄球球队&#xff0c;现为美国橄榄球联合会西区成员之一。该队曾于1961年搬迁到圣地亚哥而改叫圣地亚哥电光…...

4.7 Python设置代码格式

随着你编写的程序越来越长&#xff0c;有必要了解一些代码格式设置约定。请花时让你的代码尽可能易于阅读&#xff1b;让代码易于阅读有助于你掌握程序是做什么的,也可以帮助他人理解你编写的代码。为确保所有人编写的代码的结构都大致一致&#xff0c;Python程序员都遵循一些格…...

Zabbix 构建监控告警平台(五)

Zabbix 自动发现Zabbix 自动注册1.Zabbix 自动发现 1.1前言 为了满足监控企业成千上万台服务器&#xff0c;因此我们需要使用Zabbix批量监控来实现。自动发现和自动注册。 1.2zabbix-server &#xff08;一&#xff09;1、创建自动发现规则 在“配置”->“自动发现”->“…...

2023关键词:挑战

未失踪人口回归… 好久不见&#xff0c;不经意间拖更2个多月。今天周末&#xff0c;外面淅淅沥沥下着小雨&#xff0c;这种窝在床上的时刻最适合写点东西了。 但是建议大家在办公或者写博客的时候尽量还是端正坐姿&#xff0c;我就是因为喜欢这样靠在床背上&#xff0c;长时间…...

Wifi wpa_supplicant 到驱动的联系

同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 从framework到wpa_supplicant的适配层,其中framework部分需要了注意的是wifiservic…...

【状态估计】基于二进制粒子群优化 (BPSO) 求解最佳 PMU优化配置研究【IEEE30、39、57、118节点】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

python 将 .pdf 文件转为 .md

环境准备 pip install aspose-words 代码 doc aw.Document(r"pdf 文件路径\xxx.pdf") doc.save("Output.md") 来源&#xff1a;https://products.aspose.com/words/zh/python-net/conversion/...

【C语言】操作符详解

每天一篇博客&#xff0c;卷死各位。 文章目录前言1. 算术操作符2. 移位进制位的表示移位操作符1. 》--左移操作符2. 《--右移操作符3.位操作符4.赋值操作符5.单目操作符6.关系操作符7. 逻辑操作符8.条件操作符9.逗号操作符总结前言 在c语言学习中操作符尤为重要&#xff0c;而…...

微信小程序 学生选课系统--nodejs+vue

系统分为学生和管理员&#xff0c;教师三个角色 学生小程序端的主要功能有&#xff1a; 1.用户注册和登陆系统 2.查看选课介绍信息 3.查看查看课程分类 4.查看课程详情&#xff0c;在线选课&#xff0c;提交选课信息 5.在线搜索课程信息 6.用户个人中心修改个人资料 7.用户查看…...

leaflet 加载geojson文件并显示图形(示例代码051)

第051个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中加载geojson文件,将图形显示在地图上。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetMap无法加载,请加载其他来练习 文章目录 示例效果配置方式示例源代码(…...

【Kafka】ZK和Kafka集群的安装和配置

一、集群环境说明1. 虚拟机&#xff1a;192.168.223.101/103/1052. 系统版本&#xff1a;CentOS 7.93. JDK版本&#xff1a;11.0.18.0.14. Zookeeper版本&#xff1a;3.7.15. Kafka版本&#xff1a;2.13-2.8.2备注&#xff1a;无论是ZK&#xff0c;还是Kafka&#xff0c;都需要…...

并发编程出现的问题以及解决方式

解决并发编程出现的问题基于java内存模式的设计出现的问题基于java内存模式的设计&#xff0c;多线程操作一些共享的数据时&#xff0c;出现以下三个问题&#xff1a;1.不可见性问题&#xff1a;多个线程同时在各自的工作内存对共享数据进行操作&#xff0c;彼此之间不可见。操…...

[ linux ] linux 命令英文全称及解释

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】&#x1f389;点赞➕评论➕收藏 养成习…...

C++11新特性

文章目录说在前面花括号{}初始化new的列表初始化STL相关容器的列表初始化相关语法格式容器列表初始化的底层原理forward_list和array与类型相关的新特性decltype左值引用和右值引用什么是左值&#xff0c;什么是右值左值和右值的本质区别右值引用如何理解右值引用std::move移动…...

【宝塔部署SpringBoot前后端不分离项目】含域名访问部署、数据库、反向代理、Nginx等配置

一定要弄懂项目部署的方方面面。当服务器上部署的项目过多时&#xff0c;端口号什么时候该放行、什么时候才会发生冲突&#xff1f;多个项目使用redis怎么防止覆盖&#xff1f;Nginx的配置会不会产生站点冲突&#xff1f;二级域名如何合理配置&#xff1f;空闲的时候要自己用服…...

从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网络模型深度视觉分析技术&#xff0c;河道治理漂浮物识别监测算法模型实时检测着河道水面是否存在漂浮物、水浮莲以及生活垃圾等&#xff0c;识别到河道水面存在水藻垃圾等漂浮物&#xff0c;立即抓拍存档预警。You Only Look Once说的是…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...