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

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 这里可以跳转登录了}

提出疑问

  1. 如果想登录成功后再回调这个页面然后刷新页面怎么实现?
  2. 跳转页面的时候是否可以保持原参数的传递
  3. 登录页怎么写

问题解决

思考问题

如果想跳转回来肯定需要告知登录页我当前页面的路径,那么我们跳转登录的时候就必须要传递过去,那么我们定义一个参数存储这个当前页面路径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

相关文章:

  • Qt_文件操作
  • 了解独享IP的概念及其独特优势
  • 微信小程序如何使用自定义的字体
  • 解决macOS MySQL安装后不能远程访问的问题
  • golang雪花算法实现64位的ID
  • 无人机侦测:频谱无线电侦测设备技术详解
  • OSPFv3协议几类LSA介绍
  • redis序列化数据时,如何包含clsss类型信息?
  • 多线程计算π
  • 力扣9.25
  • 51单片机如何判断浮点数nan
  • QT 如何判断电脑已安装某个软件
  • 知识点复习4
  • 漫步者头戴式耳机好用吗?漫步者、西圣、万魔顶级机型测评对比
  • (23)mysql中mysqldump备份数据库
  • 【知识碎片】第三方登录弹窗效果
  • 〔开发系列〕一次关于小程序开发的深度总结
  • css的样式优先级
  • Java多态
  • Java应用性能调优
  • js中的正则表达式入门
  • magento 货币换算
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • 分类模型——Logistics Regression
  • 七牛云假注销小指南
  • 以太坊客户端Geth命令参数详解
  • 《码出高效》学习笔记与书中错误记录
  • Spring第一个helloWorld
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • ### RabbitMQ五种工作模式:
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (2)nginx 安装、启停
  • (6)添加vue-cookie
  • (9)STL算法之逆转旋转
  • (C#)一个最简单的链表类
  • (二)换源+apt-get基础配置+搜狗拼音
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (附源码)ssm码农论坛 毕业设计 231126
  • (七)理解angular中的module和injector,即依赖注入
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)linux下的时间函数使用
  • .“空心村”成因分析及解决对策122344
  • .bat批处理(一):@echo off
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .NET 8 跨平台高性能边缘采集网关
  • .NET 8.0 中有哪些新的变化?
  • .NET CORE 3.1 集成JWT鉴权和授权2
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • .NET中的十进制浮点类型,徐汇区网站设计
  • .NET中两种OCR方式对比
  • /run/containerd/containerd.sock connect: connection refused