Fragment 要你何用?2.0版本

news2024/11/17 3:29:22

前言

在之前的文章里有分析过Activity、Fragment、View之间的关联,也简单分析了Fragment的原理。
本篇将对Fragment被高频使用的场景以及一些坑点作分析,通过本篇文章,你将了解到:

  1. 老生常谈:为什么需要Fragment?
  2. Fragment 的生命周期与重建流程
  3. Fragment add/remove/replace/hide/show的快速理解
  4. Fragment与ViewModel的结合
  5. DialogFragment(Dialog和Fragment的结晶)
  6. ViewPager2(Fragment与RecyclerView的结晶)

1. 老生常谈:为什么需要Fragment?

先看Activity、Fragment、View三者的关系:
image.png

Activity 拥有生命周期,但是需要和AMS通信(跨进程),比较臃肿。
View 不需要和AMS通信,但没有生命周期,不好处理复杂的逻辑(如网络请求数据渲染到View上)。
而Fragment介于两者之间,它拥有生命周期(借助于Activity),无需与AMS通信,速度快。

Fragment更多时候充当着中介的作用:

将Activity里复杂的UI逻辑分离出来放到Fragment里,将对View复杂的操作分离到Fragment里。

一言蔽之,使用Fragment的理由:

  1. 无需跨进程,轻量级
  2. 拥有生命周期

2. Fragment 的生命周期与重建流程

与Activity生命周期对比

Fragment创建分为动态和静态,以动态创建Fragment为例分析:
image.png

这么看Fragment生命周期的回调方法比Activity更多,还好大部分是成对出现有迹可循。
通常情况下,我们只需要重写onCreateView(),提供自定义界面就好。

Fragment 重建流程

现象

先看简单Demo:在Activity的onCreate里添加Fragment:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_fragment )
        val fragment = FishPureFragment()
        fragment.arguments = Bundle().apply {
            putString("hello", "fragment:${count++}")
        }
        supportFragmentManager.beginTransaction().add(R.id.root, fragment).commitNow()
    }

Activity 显示的同时Fragment也显示了,一切看起来很正常,此时我们将手机横竖屏切换一下:
fragment (1).gif

可以看出,随着横竖屏的切换,创建的Fragment越来越多。
此种重建现象在很多场景下并不符合需求,比如进入Activity后拉取接口并显示弹窗,若使用DialogFragment展示弹窗,则Activity重建后会出现多个弹窗。

原理

我们知道,Fragment的生命周期依赖于Activity,当横竖屏切换时候Activity进行了重建,同时会查看关联该Activity的所有Fragment是否需要重建,若是则进行重建。
第一次显示Fragment后,横竖屏切换导致Activity重建,此时也会重建Fragment,而在Activity.onCreate()里又新建了Fragment,因此此时Activity里关联了2个Fragment。
image.png

核心点在于红色部分:

  1. Activity 销毁时保存Fragment状态
  2. Activity 重建时根据状态恢复并展示Fragment

除了横竖屏会导致Activity重建,其它配置项变更、系统内存紧张kill掉Activity等也会引起Activity重建
更详细请移步:Android onSaveInstanceState/onRestoreInstanceState 原来要这么理解

Fragment重建引发的Crash

和Activity等四大组件不一样的是:我们可以直接创建Fragment实例。
若是我们重写了Fragment默认构造函数:

class FishPureFragment(val str:String):Fragment() 

在Fragment重建的时候会Crash:

    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.fish.kotlindemo/com.fish.kotlindemo.fragment.dialogFragment.FishFragmentActivity}: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.fish.kotlindemo.fragment.dialogFragment.FishPureFragment: could not find Fragment constructor

原因是Fragment重建时会去寻找默认构造函数构造新的实例,而我们重写了构造函数,此时已经不存在默认的无参构造函数,当然会Crash。
因此,通常来说无需重写Fragment构造函数,若是想要传递参数给Fragment,可使用Fragment.setArguments()。

如何禁止Fragment重建

有以下几种方式:
第一种:
既然是Activity重建引发的Fragment重建,那么釜底抽薪,禁止Activity重建。
比如禁止屏幕旋转时重建Activity,可以在AndroidManifest.xml里配置:

android:configChanges="orientation|screenSize"

第二种:
不想改配置,也可以从Activity保存的状态入手,Frament重建依赖于恢复状态:

    //FragmentActivity.java
    private void init() {
        addOnContextAvailableListener(new OnContextAvailableListener() {
            @Override
            public void onContextAvailable(@NonNull Context context) {
                //Activity.onCreate里会调用此方法
                mFragments.attachHost(null /*parent*/);
                //找到需要恢复的Fragment状态
                Bundle savedInstanceState = getSavedStateRegistry()
                        .consumeRestoredStateForKey(FRAGMENTS_TAG);

                if (savedInstanceState != null) {
                    //不为空则进行Fragment恢复,重建
                    Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
                    mFragments.restoreSaveState(p);
                }
            }
        });
    }

也就是说只要恢复状态为空,那么Fragment就不会进行重建,而该状态是从Activity.onCreate(Bundle)里传递过来的,因此只需要在Activity里进行如下设置:

    override fun onCreate(savedInstanceState: Bundle?) {
//        super.onCreate(savedInstanceState)
        //状态置空
        super.onCreate(null)
        setContentView(R.layout.layout_fragment )
        val fragment = FishPureFragment()
        fragment.arguments = Bundle().apply {
            putString("hello", "fragment:${count++}")
        }
        supportFragmentManager.beginTransaction().add(R.id.root, fragment).commitNow()
    }

从super.onCreate(savedInstanceState)替换为super.onCreate(null)。

第三种:
将Activity下的所有恢复状态置空有点粗暴了,会影响到其它组件如View的恢复,而我们仅仅只需要禁止Fragment重建,使用如下方式:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        savedInstanceState?.let {
            it.getBundle("androidx.lifecycle.BundleSavedStateRegistry.key")?.remove("android:support:fragments");
        }
    }

注:此种方式与系统实现版本有关,有些版本取不到value

3. Fragment add/remove/replace/hide/show的快速理解

add

添加一个Fragment实例到Fragment管理栈里,此间Fragment生命周期经历了从onAttach()逐渐到onActivityCreated()。
此时Fragment所绑定的布局(View)已经添加到指定的父布局里了。

当管理栈里已有一个Fragment实例:Fragment1,此时再往里面添加Fragment2,Fragment2入栈并将其绑定的布局添加到父布局里。
依据父布局属性不同,有不同的展示方式:

  1. 若父布局是FrameLayout,则Fragment2绑定的布局将会覆盖Fragment1绑定的布局。
  2. 若父布局是LinearLayout,则Fragment2绑定的布局将会添加到Fragment1绑定的布局左边/右边。

remove

将一个Fragment实例从Fragment管理栈里移除,此间Fragment生命周期经历了从onDestroyView()逐渐到onDetach()。
此时Fragment所绑定的布局(View)已经从指定的父布局里移除了。

replace

当Fragment管理栈里没有Fragment实例时,replace与add效果一致。
当Fragment管理栈里有Fragment1实例时,replace(Fragment2)将触发Fragment1的生命周期从onDestroyView()逐渐到onDetach(),而Fragment2生命周期从onAttach()逐渐到onActivityCreated(),最终展示Fragment2。
replace 可简单理解为remove+add。

hide

不会触发Fragment生命周期,仅仅只是把Fragment绑定的布局隐藏(GONE)。

show

hide的反向操作,不会触发Fragment生命周期,仅仅只是把Fragment绑定的布局显示(VISIBLE)。

4. Fragment与ViewModel的结合

Activity ViewModel与FragmentManagerViewModel

image.png

如上图,Activity ViewModelStore里存储着不同的ViewModel实例,其中FragmentManagerViewModel是与Fragment有关的。
当Activity销毁重建时,在Activity销毁阶段ViewModelStore并没有被释放,而是被保留下来,等到Activity重建时继续使用,而ViewModelStore实例不变,其内部的ViewModel也不变,这就是ViewModel的原理。

那么FragmentManagerViewModel 是什么时候添加到ViewModelStore里的呢?
入口在这:

    #FragmentActivity.java
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        //绑定FragmentManager
        mFragments.attachHost(null /*parent*/);
    }

最终调用到:

    #FragmentManager.java
    void attachController(@NonNull FragmentHostCallback<?> host,
                          @NonNull FragmentContainer container, @Nullable final Fragment parent) {
        //...
        if (parent != null) {
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
            //host 即为承载的Activity
            //取出Activity ViewModelStore
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }

    #FragmentManagerViewModel.java
    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
                FACTORY);
        //从Activity的ViewModelStore 取出FragmentManagerViewModel
        //若没有,则创建FragmentManagerViewModel实例
        return viewModelProvider.get(FragmentManagerViewModel.class);
    }

答案是:Activity.onCreate()将FragmentManagerViewModel添加到Activity ViewModelStore里。

FragmentManagerViewModel与Fragment的ViewModel

现在FragmentManagerViewModel可以在Activity重建时恢复,那么它和Fragment里的ViewModel又是如何关联的呢?

FragmentManagerViewModel 里有个成员变量:

private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();

在Fragment里声明ViewModel:

private val vm by viewModels<MyViewModel2>()

查看viewModels调用链,关键要找到对应的ViewModelStore,而Fragment的ViewModelStore获取方式如下:

#Fragment.java
    public ViewModelStore getViewModelStore() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        return mFragmentManager.getViewModelStore(this);
    }

    #FragmentManagerViewModel.java
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        //mViewModelStores 是Map,key 是Fragment唯一标识符 value 为Fragment对应的ViewModelStore
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore == null) {
            //不存在则创建
            viewModelStore = new ViewModelStore();
            //放入map
            mViewModelStores.put(f.mWho, viewModelStore);
        }
        return viewModelStore;
    }

到此,流程就比较清晰了:

FragmentManagerViewModel里的mViewModelStores里存储着所有Fragment的ViewModelStore。也即是Fragment的ViewModel实际上是存储在FragmentManagerViewModel里的。

接着来整体捋一下Fragment的ViewModel是如何在重建时保持的。
image.png

显而易见,Fragment ViewModel实际上是间接依赖Activity ViewModeStore。
更详细的请移步:Jetpack ViewModel 抽丝剥茧

Fragment 关联的lifecycleScope

lifecycleScope 监听着Fragment生命周期,若是Fragment被销毁,则lifecycleScope也会被取消。

Fragment ViewModel关联的viewModelScope

viewModelScope 与ViewModel 生命周期保持一致,若是ViewModel被销毁(Fragment被销毁而非重建),则viewModelScope也会被取消。

5. DialogFragment(Dialog和Fragment的结晶)

DialogFragment 使用

普通的Dialog并没有生命周期,而不关联生命周期的Dialog处理异步请求比较麻烦,此时DialogFragment出现了。

class MyDialogFragment : DialogFragment() { 
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val tv = TextView(context).apply {
            text = "hello world"
            setTextColor(Color.RED)
            textSize = 30f
        }
        return tv
    }
}

显示DialogFragment:

       MyDialogFragment().show(supportFragmentManager, "dd")

我们只需要定义Fragment所绑定的布局,最终布局将会显示在Dialog里。

DialogFragment 原理

Dialog 显示Fragment绑定的布局

你也许比较好奇:之前添加的Fragment都是指定父布局,将Fragment所绑定的布局添加到父布局里,那此时的DialogFragment所指定的父布局在哪呢?
从show方法入手:

    #DialogFragment.java
    public void show(@NonNull FragmentManager manager, @Nullable String tag) {
        //就是添加普通的Fragment流程
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commit();
    }

    #FragmentTransaction.java
    public FragmentTransaction add(@NonNull Fragment fragment, @Nullable String tag)  {
        //没有指定父布局
        doAddOp(0, fragment, tag, OP_ADD);
        return this;
    }

与添加普通Fragment不同的是此时并没有指定Fragment的父布局。

Dialog会监听Fragment生命周期:

    #DialogFragment.java
    private Observer<LifecycleOwner> mObserver = new Observer<LifecycleOwner>() {
        public void onChanged(LifecycleOwner lifecycleOwner) {
            if (lifecycleOwner != null && mShowsDialog) {
                //拿到Fragment绑定的View
                View view = requireView();
                if (mDialog != null) {
                    //将View添加到Dialog里
                    mDialog.setContentView(view);
                }
            }
        }
    };

当生命周期回调后拿到Fragment绑定的View添加到Dialog里。
如此一来,Fragment就完成了和Dialog的配合显示界面。

Dialog的创建时机

    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        return super.onCreateDialog(savedInstanceState);
    }

重写该方法可以自定义Dialog或是监听默认Dialog的创建。
值得注意的是:在onAttach()/onCreate()里是拿不到Dialog对象的,因为那时候还没有创建Dialog,它在onCreateView()之前创建的。

6. ViewPager2(Fragment与RecyclerView的结晶)

ViewPager2使用

除了DialogFragment,Fragment也与RecyclerView配合,形成了ViewPager2。
创建Adapter:

public class VPAdapter extends FragmentStateAdapter {

    private List<FishPureFragment> list = new ArrayList<>();

    public VPAdapter(@NonNull FragmentActivity fragmentActivity, List<FishPureFragment> list) {
        super(fragmentActivity);
        this.list = list;
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return list.get(position);
    }

    @Override
    public int getItemCount() {
        return list.size();
    }
}

ViewPager2绑定Adapter:

        VPAdapter vpAdapter = new VPAdapter(this, list);
        viewPager2.setAdapter(vpAdapter);

可以看出,使用起来很简洁,ViewPager2的显示依靠着Fragment。

ViewPager2原理

RecyclerView缓存

在此之前先简单介绍一下RecyclerView(简称RV)的缓存设计。
RV缓存的是什么呢?
缓存的是ViewHolder(简称VH),VH里持有待显示的子布局(View)。

RecyclerView里有个内部类:Recycler,里面有5个缓存变量,归为3种缓存,分别为:
一级缓存(默认2个+1个预拉取)、二级缓存、三级缓存(默认5个,区分itemType)。
当RecyclerView渲染布局显示item时,先分别从一、二、三级缓存寻找可用的VH,若没有找到则重新创建子布局及其所属的VH,最终渲染。

VH有两种状态:

  1. VH保持着当前的数据状态,此种状态下当VH复用时可直接使用,对应一级缓存。
  2. VH仅仅保持着View,没有绑定数据,此种状态下当VH复用时需要重新绑定数据,也就是走onBindViewHolder()方法,对应三级缓存。

二级缓存是暴露给调用者设置自定义缓存的,此处先忽略。
先看看一、三级缓存是如何填充数据的。
image.png

再看RV是如何从缓存取数据的:
image.png

RecyclerView与Fragment联动

核心代码在FragmentStateAdapter.java里。
RecyclerView关键动作与Fragment的联动:
image.png
本质上还是将Fragment的View添加到RV提前构造的ItemView内。

我们来梳理一下ViewPager2(简称VP2)的滑动场景。

  1. VP2展示第一个元素,实际展示的是RV的第一个元素,此时RV回调了onCreateViewHolder、onBindViewHolder等回调,Fragment也走了生命周期,最终Fragment绑定的View添加到了RV的子Item里
  2. 当滑动VP2到第二个元素(下标为1)时,RV的第一个元素被放到一级缓存,此时RV触发移除子Item,注意这个时候并没有销毁Fragment
  3. 当滑动VP2回到第一个元素时,RV仅仅只需要addView即可,不会触发Fragment生命周期
  4. 当滑动VP2到后面几个元素时,此时一级缓存已满,将放到三级缓存里,然后触发子Item的回收,这个时候会移除对应的Fragment,Frament走生命周期里的销毁流程
  5. RV的预取元素时,会走onCreateViewHolder、onBindViewHolder回调,但不会触发addView,也就是不会触发Fragment的生命周期

VP2的缓存

public void setOffscreenPageLimit(@OffscreenPageLimit int limit)

设置VP2左右缓存的数量,默认是没有缓存的,也不能将缓存设置为0,只能是>=1。

VP2的缓存和RV的缓存是什么关系呢?
假设VP2缓存limit=1,再来梳理一下ViewPager2(简称VP2)的滑动场景。

  1. VP2展示第一个元素,实际展示的是RV的第一个元素,此时RV回调了onCreateViewHolder、onBindViewHolder等回调,Fragment也走了生命周期,最终Fragment绑定的View添加到了RV的子Item里。与此同时,VP2继续渲染第二个元素,与第一个元素步骤一致
  2. 当VP2滑动到第二个元素时,由于之前已经渲染过,此时是直接展示(RV无需addView)。于此同时会继续提前缓存第三个元素。
  3. 当VP2滑动到第一个元素时,,由于之前已经渲染过,此时是直接展示。

可以看出VP2的缓存实际上是提前将RV的元素渲染了,若设置了limit=1,那么此时RV活跃的Item有三个:当前1个+左右各一个。
当设置了VP2的缓存后,意味着多缓存了Fragment实例。

以上是Fragment实际应用与原理的相关内容。
本文基于Android 10.0

您若喜欢,请点赞、关注、收藏,您的鼓励是我前进的动力

持续更新中,和我一起步步为营系统、深入学习Android/Kotlin

1、Android各种Context的前世今生
2、Android DecorView 必知必会
3、Window/WindowManager 不可不知之事
4、View Measure/Layout/Draw 真明白了
5、Android事件分发全套服务
6、Android invalidate/postInvalidate/requestLayout 彻底厘清
7、Android Window 如何确定大小/onMeasure()多次执行原因
8、Android事件驱动Handler-Message-Looper解析
9、Android 键盘一招搞定
10、Android 各种坐标彻底明了
11、Android Activity/Window/View 的background
12、Android Activity创建到View的显示过
13、Android IPC 系列
14、Android 存储系列
15、Java 并发系列不再疑惑
16、Java 线程池系列
17、Android Jetpack 前置基础系列
18、Android Jetpack 易学易懂系列
19、Kotlin 轻松入门系列
20、Kotlin 协程系列全面解读

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/546862.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 创建一个大文件

有时候&#xff0c;我们在对文件进行测试的时候&#xff0c;可能需要创建一个临时的大文件。 那么问题来了&#xff0c;在 Java 中如何创建大文件呢&#xff1f; 问题和解决 有些人想到的办法就是定义一个随机的字符串&#xff0c;然后重复很多次&#xff0c;然后将这个字符…

第一篇:强化学习基本原理通俗介绍

你好&#xff0c;我是zhenguo&#xff08;郭震&#xff09; 今天强化学习第一篇&#xff1a;白话介绍强化学习的基本原理 强化学习是一种机器学习方法&#xff0c;旨在让智能体&#xff08;agent&#xff09;通过与环境的交互学习如何做出最优的行动选择以获得最大的累积奖励。…

Rust每日一练(Leetday0004) 正则表达、盛水容器、转罗马数字

目录 10. 正则表达式匹配 Regular Expression Matching &#x1f31f;&#x1f31f;&#x1f31f; 11. 盛最多水的容器 Container with most water &#x1f31f;&#x1f31f; 12. 整数转罗马数字 Integer to Roman &#x1f31f;&#x1f31f; &#x1f31f; 每日一练…

new和delete用法详解

本篇文章对C中的new和delete进行详解。在讲解new和delete时&#xff0c;我们会对比C语言中的malloc和free&#xff0c;看看两者的区别和相似之点。希望本篇文章会对你有所帮助。 文章目录 一、什么是new和delete 二、new和delete的用法 2、1 new和delete操作内置类型 2、2 new和…

中青宝两大议案被否!散户又“赢了”?

21.93万股&#xff0c;就能决定股东大会上的议案成败——离奇的一幕在中青宝上演。 5月18日&#xff0c;中青宝召开2022年度股东大会。会上&#xff0c;《关于2023年度日常关联交易预计的议案》《关于非独立董事2023年度薪酬方案的议案》两项议案被否。 此次股东大会上&#x…

linux设置静态ip与windows互相ping通、设置静态ip之后不能联网和网络服务重启失败的问题

1.虚拟机linux设置静态ip与windows互相ping通及设置静态ip之后不能联网问题一站式解决&#xff1a; 转载&#xff1a;https://www.codenong.com/cs105332412/ 2.遇到网络服务重启失败的问题 按照提示查看网络服务的状态 看到这篇博文https://www.cyberithub.com/failed-to-s…

Ae 效果详解:Keylight(1.2)

Ae菜单&#xff1a;效果/Keying/Keylight(1.2) Effects/Keying/Keylight(1.2) Keylight 是一款工业级的蓝幕或绿幕键控器&#xff0c;核心算法由 Computer Film 公司开发&#xff0c;并由 The Foundry 公司进一步开发移植到 Ae。 Keylight 在制作专业品质的抠像效果方面表现出色…

第11章_数据处理之增删改

第11章_数据处理之增删改 1. 插入数据 1.1 实际问题 解决方式&#xff1a;使用 INSERT 语句向表中插入数据。 1.2 方式1&#xff1a;VALUES的方式添加 使用这种语法一次只能向表中插入一条数据。 情况1&#xff1a;为表的所有字段按默认顺序插入数据 INSERT INTO 表名 VAL…

Python 学习 2022.08.28 周日

文章目录 一、 概述1.1&#xff09; 之前写的文章&#xff1a;1.2) 基础点1.3) 配置1.4) Python2 和 Python3 的区别1.5&#xff09; 相关问题跟踪解决1.6) 其他 一、 概述 1.1&#xff09; 之前写的文章&#xff1a; 【Python大系】Python快速教程《Python 数据库 GUI CGI编…

clion开发stm32之flash驱动(f4系列)

前言 使用的开发工具(clionmsys2openocd)使用的开发版芯片stm32f407vet6参考手册为stm32f4中文参考文档 查看中文手册 ## 驱动代码 头文件(bsp_flash.h) #ifndef STM32F103VET6_PROJECT_BSP_FLASH_H #define STM32F103VET6_PROJECT_BSP_FLASH_H #include "sys.h"…

华硕幻X 2023 Windows11原厂预装系统 工厂恢复安装带ASUSRecevory一键还原

华硕幻X 2023 Windows11原厂预装系统 工厂模式恢复安装带ASUSRecevory一键还原 文件地址&#xff1a;https://pan.baidu.com/s/1snKOsH3OMl3GZLqeAf-GLA?pwd8888 华硕工厂恢复系统 &#xff0c;安装结束后带隐藏分区以及机器所有驱动软件 需准备一个16G左右空u盘进行恢复 …

chatgpt赋能Python-python3怎么用

Python3入门指南&#xff1a;从基础到进阶 Python是一款简单而强大的编程语言&#xff0c;具有易读性、易学性和高生产性的特点。它广泛应用于数据分析、机器学习、Web开发、自动化测试等领域。Python的第三个版本&#xff08;Python3&#xff09;相对于第二个版本&#xff08…

网络互连与互联网 - 路由信息协议(RIP)

文章目录 1 概述2 RIP 要点2.1 根据 距离矢量&#xff08;或 跳数&#xff09;寻找最佳路由2.2 RIP 三大要点2.3 基本工作过程2.4 路由条目的更新规则2.5 RIPv1 和 RIPv2 区别2.6 "坏消息传播慢" 的问题 3 网工软考真题 1 概述 #mermaid-svg-DFp89TU9n8BiJLTr {font-…

K8s scheduler 调度:NodeName、NodeSelector与Taint

1 前言 上篇介绍了k8s调度的预选和优选策略&#xff0c;K8s scheduler 调度&#xff1a;预选和优选策略。 本篇介绍三个影响pod调度的配置&#xff1a;NodeName、NodeSelector与Taint。 2 NodeName NodeName是根据node的名称调度pod。可用于强制约束Pod跳过默认的Kubernetes调度…

Hive on Spark调优(大数据技术6)

第6章 Join优化 6.1 Hive Join算法概述 Hive拥有多种join算法&#xff0c;包括common join&#xff0c;map join&#xff0c;sort Merge Bucket Map Join等。下面对每种join算法做简要说明&#xff1a; 1&#xff09;common join Map端负责读取参与join的表的数据&#xff…

Hive on Spark调优(大数据技术8)

第8章 任务并行度优化 8.1 优化说明 对于一个分布式的计算任务而言&#xff0c;设置一个合适的并行度十分重要。在Hive中&#xff0c;无论其计算引擎是什么&#xff0c;所有的计算任务都可分为Map阶段和Reduce阶段。所以并行度的调整&#xff0c;也可从上述两个方面进行调整。 …

Maven基础学习---3、Maven的使用(命令行)

1、根据坐标创建Maven工程 1、Maven核心概念&#xff1a;坐标 &#xff08;1&#xff09;数学中的坐标 使用x、y、z三个[向量]作为空间的坐标系&#xff0c;可以在[空间]中唯一定位到一个[点]。 &#xff08;2&#xff09;Maven中的坐标 1、向量说明 使用三个[向量]在[Maven…

MapReduce排序

MapTask和ReduceTask均会对数据按照key进行排序。该操作属于Hadoop的默认行为。任何应用程序中的数据均会被排序&#xff0c;而不管逻辑上是否需要。 默认排序是按照字典顺序排序&#xff0c;且实现该排序的方法是快速排序。 对于MapTask&#xff0c;它会将处理的结果暂时放到…

25 SQL ——标量子查询

create table dept(id int primary key auto_increment,name varchar(15))comment 部门;insert into dept(id, name) values (1,研发部),(2,市场部),(3,财务部),(4,销售部),(5,总经办),(6,人事部);create table staff (id int primary key auto_increment commentID,name …

Java课设部署教程

这里我只演示使用IDEA软件或Eclipse两种常用的Java编译器的导入项目的教程&#xff01; IDEA部署教程 把下载的压缩包解压&#xff0c;解压后就是源码&#xff0c;打开IDEA&#xff0c;导入项目【源码】 选择源码所在的位置&#xff0c;点击ok即可导入 下面就是把源码导入到I…