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

Android jetpack : Navigation 导航 路由 、 单个Activity嵌套多个Fragment的UI架构方式

Android Navigation 如何动态的更换StartDestination &&保存Fragment状态

Navigation(一)基础入门

google 官网 : Navigation 导航 路由

讨论了两年的 Navigation 保存 Fragment 状态问题居然被关闭了

Navigation是一种导航的概念,即把Activityfragment当成一个个的目的地Destination,各目的地形成一张导航图NavGraph,由导航控制器NavController来统一调度跳转

单个Activity嵌套多个Fragment的UI架构方式,已被大多数Android工程师所接受和采用。但是,对Fragment的管理一直是一个比较麻烦的事情,工程师需要通过FragmentManager和FragmentTransaction来管理Fragment之间的切换。这其中还包括了对应用程序的App bar的管理,Fragment间的切换动画,Fragment间的参数传递,总之,使用起来不是特别友好。

为此,Android Jetpack提供的一个名为Navigation的UI架构组件。旨在方便我们管理Fragment页面。它具体有以下优势:

  • 可视化的页面导航图,类似xcode中的StoryBoard,便于我们看清页面之间的关系
  • 通过destination和action来完成页面间的导航
  • 方便的页面切换动画
  • 页面间类型安全的参数传递
  • 通过NavigationUI类,对菜单,底部导航,抽屉菜单导航进行方便统一的管理
  • 深层链接

注意:在Android Studio3.2及以上版本才能支持Navigation特性。
本文所说的“页面”包括了Fragment和Activity,但主要是Fragment,因为Navigation组件的主要目地就是方便我们在一个Activity中对多个Fragment进行管理。
首先,我们需要先对Navigation有一个大致的了解。

Navigation Graph

这是一种新型的XML资源文件,里面包含了应用程序所有的页面及页面之间的关系

NavHostFragment

这是一个特殊的布局文件,Navigation Graph中的页面通过该Fragment展示

NavController

这是一个Java/Kotlin对象,用于在代码中完成Navigation Graph中具体的页面切换

当你想要切换页面的时候,使用NavController对象,告诉它你想要去Navigation Graph中的哪个页面,NavController会将相关的页面展示在NavHostFragment中。

创建工程,引入依赖,

android {compileSdkVersion 30buildToolsVersion "30.0.3"defaultConfig {applicationId "com.xq.mybottomnavigation"minSdkVersion 29targetSdkVersion 30versionCode 1versionName "1.0"}buildFeatures {viewBinding true}
}dependencies {implementation 'androidx.appcompat:appcompat:1.2.0'implementation 'com.google.android.material:material:1.2.1'implementation 'androidx.constraintlayout:constraintlayout:2.0.1'testImplementation 'junit:junit:4.+'androidTestImplementation 'androidx.test.ext:junit:1.1.2'androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'implementation 'androidx.navigation:navigation-fragment:2.0.0'implementation 'androidx.navigation:navigation-ui:2.0.0'
}

MainActivity和布局 :

import android.os.Bundle;import com.google.android.material.bottomnavigation.BottomNavigationView;import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;import com.xq.mybottomnavigation.databinding.ActivityMainBinding;public class MainActivity extends AppCompatActivity {private ActivityMainBinding binding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());BottomNavigationView navView = findViewById(R.id.nav_view);// Passing each menu ID as a set of Ids because each// menu should be considered as top level destinations.//获取App bar配置:AppBarConfigurationAppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications).build();NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);//将NavController和AppBarConfiguration进行绑定NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);//将需要交互的App barUI与NavController和AppBarConfiguration进行绑定NavigationUI.setupWithNavController(binding.navView, navController);}}
  • navigation pop 和 push的时候 对Fragment的 操作是 replace,所以会导致生命周期重新走一遍

  • 其实Navigation使用很简单,navigation和activity(确切的说是Fragment)绑定之后,使用两个方法就行,一个是navigate,就是跳转,一个是navigateUp,就是返回。

如果想要跳转到新页面时,在Fragment中使用:

NavHostFragment.findNavController(this).navigate(destinationID, bundle);
NavHostFragment.findNavController(this).navigateUp();

布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/container"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingTop="?attr/actionBarSize"><com.google.android.material.bottomnavigation.BottomNavigationViewandroid:id="@+id/nav_view"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginStart="0dp"android:layout_marginEnd="0dp"android:background="?android:attr/windowBackground"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:menu="@menu/bottom_nav_menu" /><fragmentandroid:id="@+id/nav_host_fragment_activity_main"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"app:defaultNavHost="true"app:layout_constraintBottom_toTopOf="@id/nav_view"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"app:navGraph="@navigation/mobile_navigation" /></androidx.constraintlayout.widget.ConstraintLayout>

NavHostFragment:

android:name="androidx.navigation.fragment.NavHostFragment"

这句话是在告诉系统,这是一个特殊的Fragment 。

app:defaultNavHost=“true”:

app:defaultNavHost="true"

将defaultNavHost属性设置为true,则该Fragment会自动处理系统返回键,即,当用户按下手机的返回按钮时,系统能自动将当前的Fragment推出。

app:navGraph=“@navigation/nav_graph”:

app:navGraph="@navigation/nav_graph"

设置该Fragment对应的导航图 。

导航图文件 :@navigation/mobile_navigation

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/mobile_navigation"app:startDestination="@+id/navigation_home"><fragmentandroid:id="@+id/navigation_home"android:name="com.xq.mybottomnavigation.ui.home.HomeFragment"android:label="@string/title_home"tools:layout="@layout/fragment_home" /><fragmentandroid:id="@+id/navigation_dashboard"android:name="com.xq.mybottomnavigation.ui.dashboard.DashboardFragment"android:label="@string/title_dashboard"tools:layout="@layout/fragment_dashboard" /><fragmentandroid:id="@+id/navigation_notifications"android:name="com.xq.mybottomnavigation.ui.notifications.NotificationsFragment"android:label="@string/title_notifications"tools:layout="@layout/fragment_notifications" />
</navigation>

fragment

三个fragment类似

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;import com.xq.mybottomnavigation.R;
import com.xq.mybottomnavigation.databinding.FragmentHomeBinding;public class HomeFragment extends Fragment {private HomeViewModel homeViewModel;private FragmentHomeBinding binding;public View onCreateView(@NonNull LayoutInflater inflater,ViewGroup container, Bundle savedInstanceState) {homeViewModel =new ViewModelProvider(this).get(HomeViewModel.class);binding = FragmentHomeBinding.inflate(inflater, container, false);View root = binding.getRoot();final TextView textView = binding.textHome;homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {@Overridepublic void onChanged(@Nullable String s) {textView.setText(s);}});return root;}@Overridepublic void onDestroyView() {super.onDestroyView();binding = null;}}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".ui.home.HomeFragment"><TextViewandroid:id="@+id/text_home"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:textAlignment="center"android:textSize="20sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

其他

Activity中导航到指定fragment

//方式一 、 通过NavController
NavController controller = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
binding.btn1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {controller.navigate(R.id.navigation_notifications);}
});//方式二 、 通过NavHostFragment
NavHostFragment fragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_activity_main);
binding.btn2.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (fragment != null) {NavHostFragment.findNavController(fragment).navigate(R.id.navigation_notifications);}}
});

NotificationsFragment 返回上一级

textView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {NavHostFragment.findNavController(NotificationsFragment.this).navigateUp();}
});

动态设置 navGraph,传参数

MainActivity : 发送数据

NavController controller = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
NavInflater navInflater = controller.getNavInflater();
NavGraph navGraph = navInflater.inflate(R.navigation.mobile_navigation);
navGraph.setStartDestination(R.id.navigation_notifications);//初始界面Bundle args = new Bundle();
args.putBoolean("6no6", true);//传参navController.setGraph(navGraph, args);

或者

//动态加载setGraph
FragmentManager manager = getSupportFragmentManager();
NavHostFragment hostFragment = (NavHostFragment) manager.findFragmentById(R.id.nav_host_fragment_activity_main);
NavController controller = null;
if (hostFragment != null) {controller = hostFragment.getNavController();
}
navController.setGraph(R.navigation.mobile_navigation);

NotificationsFragment : 接收数据

Bundle arguments = getArguments();
if (arguments != null) {boolean aBoolean = arguments.getBoolean("6no6");textView.setTextColor(aBoolean ? Color.RED : Color.BLUE);
}

导航监听:

navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {@Overridepublic void onDestinationChanged(@NonNull @NotNull NavController controller,@NonNull @NotNull NavDestination destination,@Nullable Bundle arguments) {CharSequence label = destination.getLabel();Log.e(TAG, "onDestinationChanged: ===="+label);}
});

切换时使Fragment保存状态

在进行跳转时 直接使用了replace,所以导致当前页面会调用 onDestroyView,即fragment变为 inactive,当进行pop操作时,fragment重新进入 active状态时,会重新调用 onViewCreated 等方法,导致页面重新绘制,
其实在这种情况下,我们可以直接用ViewModelLiveData对数据进行保存,但是这次想尝试一下新的解决办法。
在知道原因后就好办了,直接继承FragmentNavigator把方法重写

public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {//      ft.replace(mContainerId, frag);//      change to  if(mFragmentManager.getFragments().size()>0){ft.hide(mFragmentManager.getFragments().get(mFragmentManager.getFragments().size()-1));ft.add(mContainerId, frag);}else {ft.replace(mContainerId, frag);}}

KeepStateFragmentNavigator 使用如下:

NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
Fragment navHostFragment = getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
navController.getNavigatorProvider().addNavigator(new KeepStateFragmentNavigator(this, navHostFragment.getChildFragmentManager(), R.id.nav_host_fragment));
NavHostFragment.findNavController(this).navigate(destinationID, bundle);

相关文章:

Android jetpack : Navigation 导航 路由 、 单个Activity嵌套多个Fragment的UI架构方式

Android Navigation 如何动态的更换StartDestination &&保存Fragment状态 Navigation(一)基础入门 google 官网 &#xff1a; Navigation 导航 路由 讨论了两年的 Navigation 保存 Fragment 状态问题居然被关闭了 Navigation是一种导航的概念&#xff0c;即把Activ…...

【react】在react中祖父、父亲、孙子组件层层解构其余属性props时报错children.forEach is not function

起因 报错children.forEacht is not function 分析原因 由于地址组件本身存在options&#xff0c;此时父组件又传递…otherProps&#xff0c;且解构了父级组件的otherProps&#xff0c;其中others解构后的属性就有options&#xff0c;因此产生了属性冲突&#xff0c;导致属性…...

P9831 [ICPC2020 Shanghai R] Gitignore

P9831 [ICPC2020 Shanghai R] Gitignore - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 只看题意翻译这道题是做不出来的&#xff0c;还要去看英文里面的规定&#xff08;这里就不放英文了&#xff09;&#xff0c;主要问题是不要公用子文件夹。 例如: 1 / a / 2 2 / a / 3…...

LinkList集合方法(自写)

在使用以下方法时需要定义一个LinkNode类来定义变量&#xff0c;new一个新对象进行调用&#xff0c;输出时需要定义输出方法 public class ListNode {int value;ListNode next;//public ListNode(int value) {this.value value;}public String toString(){return "ListN…...

Ansible playbook自动化运维工具详解

Ansible playbook自动化运维工具详解 一、playbook的相关知识1.1、playbook 的简介1.2、playbook的 各部分组成 二、基础的playbook剧本编写实例三、 playbook的定义、引用变量3.1、基础变量的定义与引用3.2、引用fact信息中的变量 四、playbook中的when条件判断和变量循环使用…...

图像切分:将一张长图片切分为指定长宽的多张图片

1.需求 比如有一张很长的图片其大小为宽度779&#xff0c;高度为122552&#xff0c;那我想把图片切分为779乘以1280的格式。 步骤如下&#xff1a; 使用图像处理库&#xff08;如PIL或OpenCV&#xff09;加载原始图片。确定子图片的宽度和高度。计算原始图片的宽度和高度&am…...

ROS学习笔记(5):ros_control

1.ros_control简介 ros_control - ROS Wiki ros_control是为ROS提供的机器人控制包&#xff0c;包含一系列控制器接口、传动装置接口、控制器工具箱等,有效帮助机器人应用功能包快速落地&#xff0c;提高开发效率。 2.ros_control框架 ros_control总体框架&#xff1a; 针对…...

《008.Springboot+vue之自习室选座系统》

[火]《008.Springbootvue之自习室选座系统》 项目简介 [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMybatisredis; 前台&#xff1a;vueElementUI; [2]功能模块展示&#xff1a; 前端…...

道可云元宇宙每日资讯|5G数智新时代元宇宙发展论坛在厦门举办

道可云元宇宙每日简报&#xff08;2023年11月6日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 5G数智新时代元宇宙发展论坛在厦门举办 3日&#xff0c;由2023年中国金鸡百花电影节执委会主办、厦门电影节有限公司协办的“5G数智新时代元宇宙发展论坛暨‘中国白德…...

使用 Go 写入文件

在本教程中&#xff0c;我们将学习如何使用 Go 将数据写入文件。我们还将学习如何同时写入文件。 本教程有以下部分 将字符串写入文件将字节写入文件逐行将数据写入文件附加到文件同时写入文件 由于 Playground 不支持文件操作&#xff0c;请在本地系统中运行本教程的所有程…...

调用DeleteLocalRef的正确姿势

做安卓jni相关开发的总会在涉及到jni变量释放时怀疑人生&#xff0c;what? where? when? who? why? how? how much? 最近碰到一个比较奇怪的问题&#xff0c;有一个jni方法的耗时在随着调用次数的增加而上涨&#xff0c;但是没有明显的内存泄漏&#xff0c;经过我缜密分…...

抖音小店从0到1起店流程,实操经验分享!

我是电商珠珠 很多人在开店之后&#xff0c;并不知道怎么做。往往会有人跑来问我说&#xff0c;开店之后怎么做啊&#xff0c;流程方面我还不是很熟悉啊等等。 这份起店流程备好了&#xff0c;将来对你有用。 第一步&#xff0c;店铺基础设置 在店铺开好之后&#xff0c;不…...

MySQL权限

权限 MySQL 允许客户端用户连接到服务器并访问服务器管理数据&#xff0c;MySQL 用户权限系统的主要功能是对给定主机连接的用户进行身份验证&#xff0c;并将该用户与数据库的权限相关联。 在 MySQL8 之前&#xff0c;授权表使用 MyISAM 并且是非事务性的&#xff0c;在 MyS…...

Nginx服务器安装证书并启用SSL(acme.sh)

前提 您已购置vps服务器&#xff0c;例如阿里云全球站ecs、AWS EC2、Azure VM、GCP Compute等安全组已开启80、443端口&#xff0c;且访问源设置为0.0.0.0/0域名已设置A记录指向当前操作服务器&#xff0c;若您使用aws ec2&#xff0c;有公有 IPv4 DNS&#xff0c;可供使用 安…...

c++实现观察者模式

前言 我觉得这是最有意思的模式&#xff0c;其中一个动&#xff0c;另外的自动跟着动。发布-订阅&#xff0c;我觉得很巧妙。 代码 头文件 #pragma once #include<vector> #include<string> #include<iostream>// 抽象观察者 class Aobserver { public:v…...

C 语言左移位操作在kernel驱动子系统中的特殊用途

文章目录 前言一、C语言左移位操作介绍1. 左移位二、左移位操作在kernel 驱动子系统中的应用1. 左移位操作在 V4L2, Media 子系统中的应用实例2.左移位操作在 DRM 子系统中的应用实例2.1 左移位操作在struct drm_crtc 中的应用2.2 左移位操作在struct drm_encoder 中的应用总结…...

kafka3.6.0集群部署

环境准备 机器环境 系统主机名IP地址centos7.9kafka01192.168.200.51centos7.9kafka02192.168.200.52centos7.9kafka03192.168.200.53 所需软件 jdk-8u171-linux-x64.tar.gzapache-zookeeper-3.8.3-bin.tar.gz https://dlcdn.apache.org/zookeeper/zookeeper-3.8.3/apache-zook…...

JAVA客户端使用账号密码调用influxdb2报错:{“code“:“unauthorized“,“message“:“Unauthorized“}

问题&#xff1a;JAVA客户端访问influxdb2报错 说明&#xff1a;当前influxdb版本&#xff1a;2.6.1 使用依赖&#xff1a; <dependency><groupId>org.influxdb</groupId><artifactId>influxdb-java</artifactId><version>2.10</vers…...

Mysql查询今天到期、n天即将到期、还有n天过期相关sql

超级治愈的一段话 其实你已经很幸福了,吃饱穿暖,没病没灾,隔三岔五还能吃顿好的,偶尔还能睡到自然醒,肥嘟嘟的一身福气。人这一辈子,要是能够逃过天灾,躲过战乱,不遇歹人,不生大病,就已经是非常幸运了,要是还能家庭和谐,收人稳定,三五知己,那更是天大的福泽。 -…...

【漏洞复现】Apache Log4j Server 反序列化命令执行漏洞(CVE-2017-5645)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.5、深度利用1、反弹Shell 说明内容漏洞编号CVE-2017-5645漏洞名称Log4j Server …...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...