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

Android入门第66天-使用AOP

开篇

        这篇恐怕又是一篇补足网上超9成关于这个领域实际都是错的、用不起来的一个知识点了。网上太多太多教程和案例用的是一个叫hujiang的AOP组件-com.hujiang.aspectjx:gradle-android-plugin-aspectjx

        首先这些错的文章我不知道是怎么来的,其次那些案例真的运行成功过吗?它们用的都是aspectjx功能,是为了兼容Kotlin。

        还是那句话,Java开发和Kotlin还有Flutter开发到底有什么区别?整一堆无用功、把框架搞得很复杂、一堆错误的无用的代码混在一起,这才是本质。开发了好用什么语言都可以开发出好东西来,开发了不好就只剩下了:我用的开发语言是最先进的。因此我们坚持使用Java。

        扯回来,我们继续来看AOP功能在Android中的使用,由于Android是Java语言,因此它也支持aspectj功能,用法和在spring系里使用一模一样的简单。只不过都是被了这个叫:com.hujiang.aspectjx:gradle-android-plugin-aspectjx搞了太复杂了,甚至有人几天都调不通一个AOP功能。

        因此本篇就是交给大家正确的、纯净的、纯真的AOP使用方法。

AOP的使用场景

        AOP使用场景太多了,如:拦截方法打印日志,特别是:APP里有很多activity,它要求用户必须登录才可以打开,如:用户积分、我的历史订单等。对于这些界面,我们要求如果用户不登录那么APP自动跳到登录页。

        这种操作就是用的AOP功能去实现的。

如何使用一个AOP来判断用户打开界面前是否已经登录

        一个activity从进入到展示界面在onCreate方法里一般会经历这么几个方法,这里的initMain()就是用来展示界面的。

我们为了做这个界面在展示前判断用户是否已经登录,我们会这么“切”一刀。

然后把这个判断用户是否已经登录做成一个和后端交互的API,整个是否登录的判断逻辑如下截图:

动手使用AOP实现

        我们假设后台的这个接口如下所示(这边用了上一篇讲到的retrofit2+okhttp3+rxjava)。

package com.mkyuan.aset.mall.android.login.api;import com.mkyuan.aset.mall.android.login.model.LoginResponseBean;import io.reactivex.Observable;
import okhttp3.RequestBody;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.POST;public interface LoginAPI {@POST("/api/account/checkLoginUT")Observable <LoginResponseBean> checkLoginUT(@Header("ut") String ut);
}

        所以我们现在开始动手制作我们的AOP了。

先引入AOP包

        我们这边不会使用网上的已经不维护的、一堆问题的“com.hujiang.aspectjx:gradle-android-plugin-aspectjx”。

        请直接使用“org.aspectj:aspectjrt:1.9.6”。为此

第一步:编程全局build.gradle

加入以下语句:

dependencies {classpath 'org.aspectj:aspectjtools:1.9.6'
}

第二步:编辑我们的模块级别的build.gradle文件

先引入aspectjrt包

    implementation 'org.aspectj:aspectjrt:1.9.6' //引入 aspectj

然后再要在同一个build.gradle中加入如下语句

//使得aop生效
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main// 获取log打印工具和构建配置
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->if (!variant.buildType.isDebuggable()) {// 判断是否debug,如果打release把return去掉就可以log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")// return;}// 使aspectj配置生效JavaCompile javaCompile = variant.javaCompilejavaCompile.doLast {String[] args = ["-showWeaveInfo","-1.8","-inpath", javaCompile.destinationDir.toString(),"-aspectpath", javaCompile.classpath.asPath,"-d", javaCompile.destinationDir.toString(),"-classpath", javaCompile.classpath.asPath,"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]log.debug "ajc args: " + Arrays.toString(args)MessageHandler handler = new MessageHandler(true);new Main().run(args, handler);//在编译时打印信息如警告、error等等for (IMessage message : handler.getMessages(null, true)) {switch (message.getKind()) {case IMessage.ABORT:case IMessage.ERROR:case IMessage.FAIL:log.error message.message, message.thrownbreak;case IMessage.WARNING:log.warn message.message, message.thrownbreak;case IMessage.INFO:log.info message.message, message.thrownbreak;case IMessage.DEBUG:log.debug message.message, message.thrownbreak;}}}
}

加完后的build.gradle长这个样

/*** following plugins is the original version*/
/*
plugins {id 'com.android.application'
}
*/
/*** by using aspectj must ad following lines tart*/
apply plugin: 'com.android.application'
//apply plugin: 'android-aspectjx' //暂时注了
/*** by using aspectj must ad following lines tart*/
android {namespace 'com.mkyuan.aset.mall.android'compileSdk 32defaultConfig {applicationId "com.mkyuan.aset.mall.android"minSdk 27targetSdk 32versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}dataBinding {enabled = true}//aspectjx {//    exclude "**/module-info.class"//    exclude "META-INF.versions.9.module-info"//    exclude "META-INF/versions/9/*.class"//    exclude 'com.google', 'com.squareup', 'org.apache','com.taobao','com.ut'//    exclude 'androidx', 'com.squareup', 'com.alipay', 'org.apache', 'org.jetbrains.kotlin',//    "module-info", 'versions.9'//}}dependencies {implementation 'org.aspectj:aspectjrt:1.9.6' //引入 aspectjimplementation 'com.github.bumptech.glide:glide:4.11.0'annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'implementation 'org.aspectj:aspectjrt:1.9.6'implementation 'io.github.youth5201314:banner:2.2.2'//com.google.android.material.theme//implementation 'com.google.android.material:material:<version>'//retrofit2implementation 'com.squareup.retrofit2:retrofit:2.9.0'implementation 'com.squareup.retrofit2:converter-gson:2.9.0'//日志拦截器implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'//rxjavaimplementation 'io.reactivex.rxjava2:rxandroid:2.1.1'implementation 'io.reactivex.rxjava2:rxjava:2.2.12'//gsonimplementation 'com.google.code.gson:gson:2.8.7'implementation 'androidx.appcompat:appcompat:1.4.1'implementation 'com.google.android.material:material:1.5.0'implementation 'androidx.constraintlayout:constraintlayout:2.1.3'testImplementation 'junit:junit:4.13.2'androidTestImplementation 'androidx.test.ext:junit:1.1.3'androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}//使得aop生效
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main// 获取log打印工具和构建配置
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->if (!variant.buildType.isDebuggable()) {// 判断是否debug,如果打release把return去掉就可以log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")// return;}// 使aspectj配置生效JavaCompile javaCompile = variant.javaCompilejavaCompile.doLast {String[] args = ["-showWeaveInfo","-1.8","-inpath", javaCompile.destinationDir.toString(),"-aspectpath", javaCompile.classpath.asPath,"-d", javaCompile.destinationDir.toString(),"-classpath", javaCompile.classpath.asPath,"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]log.debug "ajc args: " + Arrays.toString(args)MessageHandler handler = new MessageHandler(true);new Main().run(args, handler);//在编译时打印信息如警告、error等等for (IMessage message : handler.getMessages(null, true)) {switch (message.getKind()) {case IMessage.ABORT:case IMessage.ERROR:case IMessage.FAIL:log.error message.message, message.thrownbreak;case IMessage.WARNING:log.warn message.message, message.thrownbreak;case IMessage.INFO:log.info message.message, message.thrownbreak;case IMessage.DEBUG:log.debug message.message, message.thrownbreak;}}}
}

制作AOP相关的代码

Login.java-定义一个基于方法切面的annotation

package com.mkyuan.aset.mall.android.util.aop.login;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;//不需要回调的处理
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
}

LoginAspect.java -使用Around模式对含有@Login的方法进行切面

package com.mkyuan.aset.mall.android.util.aop.login;import android.util.Log;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;@Aspect
public class LoginAspect {private static final String TAG = "LoginAspect";@Pointcut("execution(@com.mkyuan.aset.mall.android.util.aop.login.Login  * *(..))")public void executionCheckLogin() {}//不带回调的注解处理@Around("executionCheckLogin()")public void loginJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {Log.i(TAG, ">>>>>>走进AOP方法");Signature signature = joinPoint.getSignature();if (!(signature instanceof MethodSignature)) {throw new RuntimeException("该注解只能用于方法上");}Login login = ((MethodSignature) signature).getMethod().getAnnotation(Login.class);if (login == null)return;//判断当前是否已经登录LoginManager.isLogin(new LoginCheckCallBack() {@Overridepublic void changeValue(boolean loginResult) {if(loginResult) {Log.i(TAG, ">>>>>>用户己登录走入下一步");try {joinPoint.proceed();} catch (Throwable e) {Log.e(TAG,e.getMessage(),e);}}else{//如果未登录,去登录页面Log.i(TAG, ">>>>>>用户未登录去登录页面");LoginManager.gotoLoginPage();}}});}
}

LoginManager.java

        内含有和后台是否登录接口交互以及判断用户如果已经登录那么继续“走下去-打开界面”,否则跳到Login登录界面的逻辑

package com.mkyuan.aset.mall.android.util.aop.login;import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;import com.google.gson.Gson;
import com.mkyuan.aset.mall.android.home.MainActivity;
import com.mkyuan.aset.mall.android.login.LoginActivity;
import com.mkyuan.aset.mall.android.login.SmsLoginActivity;
import com.mkyuan.aset.mall.android.login.api.LoginAPI;
import com.mkyuan.aset.mall.android.login.model.LoginResponseBean;
import com.mkyuan.aset.mall.android.network.BaseObserver;
import com.mkyuan.aset.mall.android.network.NetworkApi;
import com.mkyuan.aset.mall.android.util.ContextUtils;
import com.mkyuan.aset.mall.android.util.SharedPreferenceHelper;
import com.mkyuan.aset.mall.android.util.activity.ActivityCollector;import java.util.Map;import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.RequestBody;public class LoginManager {private static final String TAG = "LoginAspect";public static void isLogin(LoginCheckCallBack loginCheckCallBack) {Context ctx = null;try {ctx = ContextUtils.getCurApplicationContext();SharedPreferenceHelper spHelper = new SharedPreferenceHelper(ctx);Map<String, String> data = spHelper.read();if (data.get("ut") != null && data.get("ut").trim().length() > 0) {String utValue = data.get("ut");//开始调用okhttp3, retrofit, rxjava框架LoginAPI loginAPI = NetworkApi.createService(LoginAPI.class);//loginAPI.checkLoginUT().loginAPI.checkLoginUT(utValue).compose(NetworkApi.applySchedulers(new BaseObserver<LoginResponseBean>() {@Overridepublic void onSuccess(LoginResponseBean loginResponseBean) {Log.i(TAG, ">>>>>>" + new Gson().toJson(loginResponseBean));int returnCode = loginResponseBean.getCode();String returnMsg = loginResponseBean.getMessage();if (returnCode == 0) {//result = true;loginCheckCallBack.changeValue(true);Log.i(TAG,">>>>>>get verifiedCode->" + loginResponseBean.getData());//startActivity(new Intent(SmsLoginActivity.this, MainActivity// .class));} else {loginCheckCallBack.changeValue(false);}}@Overridepublic void onFailure(Throwable e) {Log.e(TAG, ">>>>>>Network Error: " + e.toString(), e);loginCheckCallBack.changeValue(false);}}));} else {loginCheckCallBack.changeValue(false);}} catch (Exception e) {Log.e(TAG, ">>>>>>isLogin error: " + e.getMessage());loginCheckCallBack.changeValue(false);}}public static void gotoLoginPage() {Context ctx = null;try {ctx = ContextUtils.getCurApplicationContext();Intent intent = new Intent();intent.setClass(ctx, LoginActivity.class);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);ctx.startActivity(intent);ActivityCollector.getInstance().quitCurrentProcess("com.mkyuan.aset.mall.android.home.MainActivity");} catch (Exception e) {Log.e(TAG, ">>>>>>gotoLoginPage error: " + e.getMessage(), e);}}
}

使用这个AOP

        在MainActivity代码里有一个initMain方法,渲染界面的逻辑全部在这个initMain方法里。

接着我们来看这个initMain()方法。在方法前我们加入了自定义的“切入点”。

效果演示

第一次打开APP,用户未登录,因此直接被跳到了Login界面

然后输入手机,点获取验证码

按提交,登录成功。

然后关闭APP,再次打开APP

由于之前用户已经登录过了,因此AOP直接会把用户带入到主页

结合着我上一篇:retrofit2+okhttp3+rxjava自己不妨动动手试试看吧

相关文章:

Android入门第66天-使用AOP

开篇这篇恐怕又是一篇补足网上超9成关于这个领域实际都是错的、用不起来的一个知识点了。网上太多太多教程和案例用的是一个叫hujiang的AOP组件-com.hujiang.aspectjx:gradle-android-plugin-aspectjx。首先这些错的文章我不知道是怎么来的&#xff0c;其次那些案例真的运行成功…...

pl/sql篇之触发器

简述本文將具体简述触发器的语法&#xff0c;触发条件及其适用场景&#xff0c;希望对读者理解&#xff0c;使用触发器能起到作用。触发器的定位触发器是数据库独立编译&#xff0c;存储的对象&#xff0c;是数据库重要的技术。和函数不同&#xff0c;触发器的执行是主动的&…...

黑马《数据结构与算法2023版》正式发布

有人的地方就有江湖。 在“程序开发”的江湖之中&#xff0c;各种技术流派风起云涌&#xff0c;变幻莫测&#xff0c;每一位IT侠客&#xff0c;对“技术秘籍”的追求和探索也从未停止过。 要论开发技术哪家强&#xff0c;可谓众说纷纭。但长久以来&#xff0c;确有一技&#…...

Spring的创建和使用

目录 创建Spring项目 步骤 1)使用Maven的方式创建Spring项目 2)添加Spring依赖 3)创建启动类 存Bean对象 1.创建Bean对象 2.将Bean注册到Spring中 取Bean对象并使用 步骤 1.先得到Spring上下文对象 2.从Spring中获取Bean对象 3.使用Bean ApplicationContext VS Bea…...

如何实现外网跨网远程控制内网计算机?快解析来解决

远程控制&#xff0c;是指管理人员在异地通过计算机网络异地拨号或双方都接入Internet等手段&#xff0c;连通需被控制的计算机&#xff0c;将被控计算机的桌面环境显示到自己的计算机上&#xff0c;通过本地计算机对远方计算机进行配置、软件安装程序、修改等工作。通俗来讲&a…...

【跟着ChatGPT学深度学习】ChatGPT教我文本分类

【跟着ChatGPT学深度学习】ChatGPT教我文本分类 ChatGPT既然无所不能&#xff0c;我为啥不干脆拜他为师&#xff0c;直接向他学习&#xff0c;岂不是妙哉。说干就干&#xff0c;我马上就让ChatGPT给我生成了一段文本分类的代码&#xff0c;不看不知道&#xff0c;一看吓一跳&am…...

IM即时通讯架构技术:可靠性、有序性、弱网优化等

消息的可靠性是IM系统的典型技术指标&#xff0c;对于用户来说&#xff0c;消息能不能被可靠送达&#xff08;不丢消息&#xff09;&#xff0c;是使用这套IM的信任前提。 换句话说&#xff0c;如果这套IM系统不能保证不丢消息&#xff0c;那相当于发送的每一条消息都有被丢失的…...

【算法】三道算法题两道难度中等一道困难

算法目录只出现一次的数字&#xff08;中等难度&#xff09;java解答参考二叉树的层序遍历&#xff08;难度中等&#xff09;java 解答参考给表达式添加运算符&#xff08;比较困难&#xff09;java解答参考大家好&#xff0c;我是小冷。 上一篇是算法题目 接下来继续看下算法题…...

正交实验与极差分析

正交试验极差分析流程如下图&#xff1a; 正交试验说明 正交试验是研究多因素试验的设计方法。对于多因素、多水平的实验要求&#xff0c;如果每个因素的每个水平都要进行试验&#xff0c;这样就会耗费大量的人力和时间&#xff0c;正交试验可以选择出具有代表性的少数试验进行…...

DEXTUpload .NET增强的上传速度和可靠性

DEXTUpload .NET增强的上传速度和可靠性 DEXTUpload.NET Pro托管在Windows操作系统上的Internet Information Server(IIS)上&#xff0c;服务器端组件基于HTTP协议&#xff0c;支持从web浏览器到web服务器的文件上载。它也可以在ASP.NET服务器应用程序平台开发的任何网站上使用…...

SkyWalking 将方法加入追踪链路(@Trace)

SkyWalking8 自定义链路追踪@Trace 自定义链路,需要依赖skywalking官方提供的apm-toolkit-trace包.在pom.xml的dependencies中添加如下依赖: <dependency><groupId>org.apache.skywalking</groupId><artifactId>apm-toolkit-trace</artifactId>&…...

MySQL Administrator定时备份MySQL数据库

1、下载并安装软件mysql-gui-tools-5.0-r17-win32.exe 2、将汉化包zh_CN文件夹拷贝到软件安装目录 3、菜单中打开MySql Adminstrator&#xff0c;见下图&#xff0c;初次打开无服务实例。 点击已存储连接右侧按钮①&#xff0c;打开下图对话框。点击“新连接”按钮&#xff…...

Kubernetes入门教程 --- 使用二进制安装

Kubernetes入门教程 --- 使用二进制安装1. Introduction1.1 架构图1.2 关键字介绍1.3 简述2. 使用Kubeadm Install2.1 申请三个虚拟环境2.2 准备安装环境2.3 配置yum源2.4 安装Docker2.4.1 配置docker加速器并修改成k8s驱动2.5 时间同步2.6 安装组件3. 基础知识3.1 Pod3.2 控制…...

深度学习模型压缩方法概述

一,模型压缩技术概述 1.1,模型压缩问题定义 因为嵌入式设备的算力和内存有限,因此深度学习模型需要经过模型压缩后,方才能部署到嵌入式设备上。 模型压缩问题的定义可以从 3 角度出发: 模型压缩的收益: 计算: 减少浮点运算量(FLOPs),降低延迟(Latency)存储: 减少内…...

《NFL橄榄球》:坦帕湾海盗·橄榄1号位

坦帕湾海盗&#xff08;英语&#xff1a;Tampa Bay Buccaneers&#xff09;是一支位于佛罗里达州的坦帕湾职业美式橄榄球球队。他们是全国橄榄球联盟的南区其中一支球队。在1976年&#xff0c;与西雅图海鹰成为NFL的球队。球队在最初的两个球季连败26场&#xff0c;在二十世纪七…...

Xmake v2.7.7 发布,支持 Haiku 平台,改进 API 检测和 C++ Modules 支持

layout: post.cn title: “Xmake v2.7.7 发布&#xff0c;支持 Haiku 平台&#xff0c;改进 API 检测和 C Modules 支持” tags: xmake lua C/C package modules haiku cmodules categories: xmake Xmake 是一个基于 Lua 的轻量级跨平台构建工具。 它非常的轻量&#xff0c;没…...

苹果ios签名证书的生成方法

在使用hbuilderx打包uniapp或html5应用的时候&#xff0c;假如是打包ios应用&#xff0c;是需要ios签名证书&#xff0c;和证书profile文件的&#xff0c;这个证书要求是p12格式的证书&#xff0c;profile文件又叫描述文件。 这两个文件&#xff0c;需要在苹果开发者中心生成&…...

c++开发配置常用网站记录

1.ubuntu 镜像源&#xff1a; (1) 清华源&#xff1a;https://mirror.tuna.tsinghua.edu.cn/help/ubuntu/ (2) 阿里源&#xff1a;https://developer.aliyun.com/mirror/ubuntu?spma2c6h.13651102.0.0.3e221b11VuM27s 包含了ubuntu各个版本的source源 2.ubuntu iso镜像下载…...

DC-1 靶场学习

以前写过了&#xff0c;有一些忘了&#xff0c;快速的重温一遍。 DC一共九个靶场&#xff0c;目标一天一个。 文章目录环境配置&#xff1a;信息搜集&#xff1a;漏洞复现&#xff1a;FLAG获取环境配置&#xff1a; 最简单的办法莫过于将kali和DC-1同属为一个nat的网络下。 信…...

oracle 不使用索引深入解析

首先&#xff0c;我们要确定数据库运行在何种优化模式下&#xff0c;相应的参数是&#xff1a;optimizer_mode。缺省的设置应是"choose"&#xff0c;即如果对已分析的表查询的话选择CBO&#xff0c;否则选择RBO。如果该参数设为“rule”&#xff0c;则不论表是否分析…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...