Android 通过自定义注解实现Activity间跳转时登录路由的自动拦截
应用场景
在Android 中部分软件需要登录才能使用,但是有的页面又不需要登录,Android不同于Web可以直接拦截重定向路由,因此如果在Android中如果需要检测是否登录,如果没登录跳转登录的话就需要再每个页面中判断,当然也可以写成公共方法,但是这样的方式还是比较麻烦。这里讲一个自定义注解实现这个需求的方法
编写注解
先直接编写一个注解
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME) //运行时有效
public @interface NeedLogin {/*** 开关,可以不需要,但是我觉得还有有比较好,看个人需求,默认为不开启检测是否登录*/boolean enable() default false;
}
编写公共代码
我们可以再onStart
生命周期中进行检测:是否启用注解
和是否登录
,记得写在BaseActivity中,这样后面继承BaseActivity时方法自动生效,在需要登录拦截的Activity中只需要添加一个注解就可以实现自动拦截、登录、回显!
是否启动注解
:这里需要一点自定义注解的理论知识,请自行学习
private boolean isNeedLogin() {// 通过反射或注解处理器获取当前 Activity 是否需要登录boolean isAnnotation = getClass().isAnnotationPresent(NeedLogin.class);if (!isAnnotation) {return false;}NeedLogin needLogin = getClass().getAnnotation(NeedLogin.class);if (needLogin == null) {return false;}return needLogin.enable();}
是否登录
:这个没任何讲解的,你是使用SharedPreferences
还是MMKV
还是别的存储登录信息都可以无所谓,简单写个示例:
private boolean checkLogin() {// 检查登录状态的逻辑,true代表已登录,false代表未登录return !errorService.isLogin();}
然后在onStart
生命周期方法中进行检测
@Overrideprotected void onStart() {super.onStart();if (errorService == null) {return;}//不包含注解或者登录注解未开启if (!isNeedLogin()) {return;}//已登录,则跳转登录if (!checkLogin()) {return;}//TODO 这里可以跳转登录了}
提出疑问
- 如果想登录成功后再回调这个页面然后刷新页面怎么实现?
- 跳转页面的时候是否可以保持原参数的传递
- 登录页怎么写
问题解决
思考问题
如果想跳转回来肯定需要告知登录页我当前页面的路径,那么我们跳转登录的时候就必须要传递过去,那么我们定义一个参数存储这个当前页面路径TARGET_ACTIVITY
/*** 跳转目标Activity页面,目前用于自动检测登录的作用*/public static final String TARGET_ACTIVITY = "targetActivity";
那么我稍微修改下跳转登录,修改完善一下上面的onStart
@Overrideprotected void onStart() {super.onStart();if (errorService == null) {return;}//不包含注解或者登录注解未开启if (!isNeedLogin()) {return;}//已登录,则跳转登录if (!checkLogin()) {return;}//如果未登录跳转登录并且把当前页的信息传递过去,以便于登录后回传Bundle bundle = getIntent().getExtras();if (bundle == null) {bundle = new Bundle();}bundle.putString(ConstantsHelper.TARGET_ACTIVITY, getClass().getName());errorService.toLogin(this, bundle);//就是一个简单的Intent跳转finish();}
完善登录页面代码
简单思考一下我们再登录页需要跳转到哪几个目标页:首页
、指定目标页
、返回上一页
那么我们编写几个接口方法
public interface UserView extends BaseView {/*** 直接返回上个页面*/void toLast();/*** 是否有需要跳转的目标页面* @return true有目标页面*/boolean hasTarget();/*** 跳转到目标页面,结合hasTarget使用*/void toTarget();/*** 跳转到主页*/void toMain();/*** 关闭键盘*/void hideKeyboard();
}
我们在登录页实现接口,然后模拟下登录操作
点击登录
public MutableLiveData<UserInfo> getLiveData() {return liveData;}//点击按钮触发的方法,仅用于模拟public void loginClick(View v, RequestLoginBean requestLoginBean, String password) {int id = v.getId();if (id == R.id.login_submit) {if (StringUtil.isEmpty(requestLoginBean.getUsername())) {baseView.showToast( "请填写用户名");return;}if (StringUtil.isEmpty(password)) {baseView.showToast( "请填写密码");return;}try {requestLoginBean.setPassword(MD5Util.md5Encode(password));} catch (Exception e) {e.printStackTrace();baseView.showToast("密码加密异常");}
// iRepository.login(requestLoginBean, liveData);//模拟登录情况baseView.showLoading("正在登录,请稍后...");UserAccountHelper.setToken("this is token !!!");UserAccountHelper.setRefreshToken("this is refresh_token !!!");UserInfo userInfo = new UserInfo() {{setId("1");setAvatar("https://img2.baidu.com/it/u=2948556484,2204941832&fm=253&fmt=auto&app=120&f=JPEG?w=655&h=436");setEmail("fzkf3318@163.com");setName("张三");setPhone("15210230000");setRealName("张韶涵");setRoleName("演员");setSex(1);}};new Handler(Looper.getMainLooper()).postDelayed(() -> {baseView.hideLoading();liveData.setValue(userInfo);}, 3000);}}
LoginActivity
中监听liveData
mViewModel.getLiveData().observe(this, userInfo -> mViewModel.loginCallback(userInfo, binding.userEdit.getText().toString()));//mViewModel中public void loginCallback(UserInfo userInfo, String userName) {//存储登录信息和登录状态UserAccountHelper.saveLoginState(userInfo, true);//这里只是判断本地账号和上次账号是否为同一个,如果不是同一个则不能继续之前操作,则需要返回App首页刷新,并且同事判断下当前app是不是只有当前登录页一个页面if (TextUtils.isEmpty(userName) || !userName.equals(UserAccountHelper.getAccount()) ||AppManager.getAppManager().getActivityStack().size() == 1) {UserAccountHelper.saveAccount(userName);//打开MainActivitybaseView.toMain();return;}//存储本地登录的账号UserAccountHelper.saveAccount(userName);if (baseView.hasTarget()) {baseView.toTarget();return;}baseView.toLast();}
现在完善一下LoginActivity
@SuppressLint("UnsafeIntentLaunch")@Overridepublic void toLast() {showToast("登录成功!");setResult(RESULT_OK, getIntent().putExtras(bundle));finish();}@Overridepublic boolean hasTarget() {String targetActivity = bundle.getString(ConstantsHelper.TARGET_ACTIVITY);if (TextUtils.isEmpty(targetActivity)) {return false;}try {//是否报错,不报错说明目标页面存在Class.forName(targetActivity);return true;} catch (ClassNotFoundException e) {return false;}}@Overridepublic void toTarget() {String targetActivity = bundle.getString(ConstantsHelper.TARGET_ACTIVITY);if (TextUtils.isEmpty(targetActivity)) {toLast();return;}try {//是否报错,不报错说明目标页面存在Intent intent = new Intent(this, Class.forName(targetActivity));intent.putExtras(bundle);startActivity(intent);finish();} catch (ClassNotFoundException e) {toLast();}}@Overridepublic void toMain() {showToast("登录成功!");AppManager.getAppManager().finishAllActivity();startActivity(errorService.getMainActivity());}
编写案例测试效果
编写一个页面
@NeedLogin(enable = true)
@AndroidEntryPoint
public class TargetActivity extends BaseActivity<EmptyViewModel, ActivityTargetBinding> {public final static String ARGS = "ARGS";@Overrideprotected int getLayoutId() {return R.layout.activity_target;}@Overridepublic String setTitleBar() {return "测试登录拦截";}@Overridepublic void initView(Bundle savedInstanceState) {binding.buttonLogin.setOnClickListener(v-> errorService.toLogin(this));}@Overridepublic void initData(Bundle bundle) {String args = bundle.getString(ARGS);binding.tvArgs.setText(TextUtils.isEmpty(args) ? "暂无参数" : args);}
}
<?xml version="1.0" encoding="utf-8"?>
<layout 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"><data></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".activity.TargetActivity"><TextViewandroid:id="@+id/tv_args"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="@color/auto_color"android:textSize="@dimen/font_18"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/button_login"android:text="前往登录"android:textColor="@color/auto_color"android:textSize="@dimen/font_18"app:layout_constraintStart_toStartOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toBottomOf="@+id/tv_args"android:layout_width="wrap_content"android:layout_height="wrap_content"/></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
效果图
完结
代码地址
https://github.com/fzkf9225/mvvm-componnent-master/blob/master/app/src/main/java/com/casic/titan/demo/activity/TargetActivity.java
相关文章:

Android 通过自定义注解实现Activity间跳转时登录路由的自动拦截
应用场景 在Android 中部分软件需要登录才能使用,但是有的页面又不需要登录,Android不同于Web可以直接拦截重定向路由,因此如果在Android中如果需要检测是否登录,如果没登录跳转登录的话就需要再每个页面中判断,当然也…...
安全开发指南
1. 准备工作与培训 安全文化与意识:建立并强化组织的安全文化,对所有成员进行安全意识培训。安全策略与标准:制定明确的安全开发策略、标准和流程,包括代码审查、安全测试、事件响应等。工具与技术选择:选择合适的开发…...

【word脚注】双栏设置word脚注,脚注仅位于左栏,右栏不留白
【word脚注】双栏设置word脚注,脚注仅位于左栏,右栏不留白 调整前效果解决方法调整后效果参考文献 调整前效果 调整前:脚注位于左下角,但右栏与左栏内容对其,未填充右下角的空白区域 解决方法 备份源文件复制脚注内…...

ROS学习笔记(三):VSCode集成开发环境快速安装,以及常用扩展插件配置
文章目录 前言VSCode集成开发环境1 安装VSCode2 VSCode扩展插件2.1 VSCode扩展插件模块介绍2.1 常用扩展插件配置一、语言支持类插件二、智能辅助类插件三、科学计算与数据分析类插件四、ROS开发相关插件 3 总结相关链接 前言 关于Ubuntu与ROS的常规安装,可以看这几…...

论文精读--Two-Stream Convolutional Networks for Action Recognition in Videos
对于单张图片,丢进卷积和全连接层直接得出分类结果就行 但对于视频,早期的一些工作把视频中的一些关键帧抽取出来,把一个个帧通过网络,最后把结果合并,或者把帧叠起来,一起丢进网络。在网络中进行early fu…...

JAVA姓氏头像情侣头像家庭头像签名头像谐音顽埂头像设计小程序头像大全系统小程序源码
姓氏头像到谐音梗,打造你的专属头像大全系统 🎨✨ 👨👩👧👦 家庭头像:记录温馨瞬间 在这个充满爱的时代,用一张家庭头像来记录你和家人的美好瞬间吧!我们的“姓氏…...

UE5.4.3 Replay 重播回放系统
工程的配置文件DefaultEngine.ini中需要加入 +NetDriverDefinitions=(DefName=“DemoNetDriver”,DriverClassName=“/Script/Engine.DemoNetDriver”,DriverClassNameFallback=“/Script/Engine.DemoNetDriver”) 此步骤将启用并加载DemoNetDriver .ini添加示例 [/Script/En…...

深入掌握 Protobuf 与 RPC 的高效结合:实现C++工程中的高效通信
目录 一、Protobuf与RPC框架的通信流程概述二、Protobuf与RPC在C中的实际应用2.1 定义 .proto 文件2.2 编译 .proto 文件生成C代码2.3 实现服务器端逻辑2.4 实现客户端逻辑2.5 使用CMake构建工程2.6 编译与运行2.7 关键组件解析2.8 序列化与反序列化的实现 三、关键实现与解析四…...

录屏软件大比拼:四款必备工具助你轻松录制精彩瞬间!
哎呀,说到电脑录屏这事儿,我这个办公室小文员可是深有体会啊!平时工作里,经常需要录个会议啊、做个教程啊,或者分享个操作技巧给同事们看。市面上的录屏软件多得数不清,但我最常用的几款工具。今天就来跟大…...

计算机毕业设计宠物领养网站我的发布领养领养用户信息/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序
目录 1.课题背景 2.课题意义 3.技术介绍 4.技术性需求 4.1后端服务: 4.2 前端展示 5.数据库设计: 6.系统性能: 7.安全性: 8. 功能介绍: 9. 部分代码 1.课题背景 近年来,随着宠物饲养数量…...

用示波器测动态滞回线
大学物理(下)实验-中南民族大学通信工程2022级 手动逐个处理数据较为麻烦且还要绘图,故想到用pythonmatplotlib来计算结果并数据可视化。 代码实现 import matplotlib.pyplot as plt# 样品一磁化曲线 X [0, 0.2, 0.4, 0.6, 0.8, 1, 1.5, 2.…...

【JDK动态代理】JDK动态代理:为何只能代理接口和接口实现类
在Java开发中,JDK动态代理是一种非常有用的技术,它允许开发者在不修改目标类代码的情况下,为目标类添加额外的功能。然而,JDK动态代理的使用有一些限制,特别是它只能代理接口和接口实现类。本文将深入探讨这一限制的原…...

MFC工控项目实例二十一型号选择界面删除参数按钮禁用切换
承接专栏《MFC工控项目实例二十手动测试界面模拟量输入实时显示》 对于禁止使用的删除、参数按钮,在选中列表控件选项时切换为能够使用。 1、在TypDlg.h文件中添加代码 #include "ShadeButtonST.h" #include "BtnST.h" class CTypDlg : publi…...

前端框架对比和选择指南
前端框架对比和选择指南 随着 Web 开发技术的快速发展,前端框架已经成为了现代 Web 开发的核心工具之一。它们为开发人员提供了快速构建高效、交互性强的应用的基础。当前流行的前端框架主要包括 React.js、Vue.js 和 Angular.js。在这篇技术博客中,我们…...

人工智能价格战——如何降低成本让人工智能更易于普及
十年前,开发人工智能 (AI) 是只有大公司和资金充足的研究机构才能负担得起的事情。必要的硬件、软件和数据存储成本非常高。但从那时起,情况发生了很大变化。一切始于 2012 年的 AlexNet,这是一种深度学习模型,展示了神经网络的真…...

企业间图文档发放:如何在保障安全的同时提升效率?
不管是大型企业,还是小型创业公司,不论企业规模大小,每天都会有大量的图文档发放,对内传输协作和对外发送使用,数据的生产也是企业业务生产力的体现之一。 伴随着业务范围的不断扩大,企业与客户、合作伙伴之…...

深入解析 ConcurrentHashMap:从 JDK 1.7 到 JDK 1.8
✨探索Java基础 ConcurrentHashMap✨ 引言 ConcurrentHashMap 是 Java 中一个线程安全的高效 Map 集合。它在多线程环境下提供了高性能的数据访问和修改能力。本文将详细探讨 ConcurrentHashMap 在 JDK 1.7 和 JDK 1.8 中的不同实现方式,以及它们各自的优缺点。 …...

VS code user setting 与 workspace setting 的区别
VS code user setting 与 workspace setting 的区别 引言正文引言 相信有不少开始接触 VS code 的小伙伴会有疑问,user setting 与 workspace setting 有什么区别呢?这里我们来说明一下 正文 首先,当我们使用 Ctrl + Shift + P 打开搜索输入 setting 后,可以弹出 4 个se…...
XPath基础知识点讲解——用于在XML中查找信息的语言
1. 什么是XPath? XPath(XML Path Language)是用于在XML(Extensible Markup Language)文档中查找信息的语言。它可以通过路径表达式来选择XML文档中的节点,类似于如何在文件系统中使用路径查找文件。XPath是…...
Visual Studio 2022
VS(Visual Studio)是一款由微软开发的集成开发环境(IDE),用于开发应用程序、网站以及移动应用等。VS的历史可以追溯到1997年,当时发布了第一个版本的VS。以下是VS的一些重要历史里程碑: Visual …...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...

Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...