Android进阶之路 - 通过业务(Activity)栈管理业务流程

关于业务栈的管理方式,我在去年刚接触当前项目的时候就想记录一下,但是一直晃晃悠悠拖到了现在,索性在春节前以其收尾也是不错。其实这篇内容在项目中肯定经常用得到,但是关于标题命名我却不知道如何描述…

在实际业务中为了形成业务闭环,经常需要对一条完整的业务线进行管理,而承载业务的组件一般都是 Activity,所以也可以说是对 Activity的管理

关于Activity管理的篇章,我早期曾写过类似的一篇 Android进阶之路 - 强制下线、退出登录,内部方法可能有所改变,但是部分思想是可以借鉴的

如需 gif 效果,年后补入

提前祝各位,新春快乐

    • 基础了解
    • Activity管理
    • 业务实践

基础了解

在该篇的 Activity 管理类中用到了弱引用Weak Reference),特此给大家说一下Java中的四种引用类型(经常被人问到),如下:

  • 强引用Strong Reference):这是最常见的引用类型,它指向一个对象,只要强引用存在,对象就不会被垃圾回收器回收。如果对象没有任何强引用指向它,垃圾回收器将在适当的时候回收该对象。1
  • 软引用Soft Reference):软引用指向的对象在内存不足时会被垃圾回收器回收,而软引用所指向的对象在垃圾回收时会被立即回收。这种引用类型通常用于对内存敏感的应用程序,例如缓存。
  • 弱引用Weak Reference):弱引用指向的对象在垃圾回收时会被立即回收。这种引用类型通常用于需要在对象被回收之前完成某些操作的情况。
  • 虚引用Virtual Reference):虚引用主要用于跟踪对象被垃圾回收的状态,但不能通过虚引用来获取对象的实例。虚引用只能在配合 ReferenceQueue 使用时才有意义。虚引用通常在设计模式中使用,例如单例模式。

Activity管理

package com.example.taskmanage;import android.app.Activity;import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;/*** @Description: 任务栈管理*/
public class ActivityTaskManager {private ActivityTaskManager() {}private static Map<String, ActivityTaskManager> taskMap;public static ActivityTaskManager getManager(String tag) {if (taskMap == null) {taskMap = new HashMap<>();}if (!taskMap.containsKey(tag)) {taskMap.put(tag, new ActivityTaskManager());}return taskMap.get(tag);}//    @Deprecated
//    public static ActivityTaskManager getManager() {
//        return getManager(TaskTag.TAG_DEFAULT);
//    }private Stack<WeakReference<Activity>> mActivityStack;/*** 添加Activity到栈*/public void addActivity(Activity activity) {if (mActivityStack == null) {mActivityStack = new Stack<>();}mActivityStack.add(new WeakReference<>(activity));}/*** 检查弱引用是否释放,若释放,则从栈中清理掉该元素*/public void checkWeakReference() {if (mActivityStack != null) {// 使用迭代器进行安全删除for (Iterator<WeakReference<Activity>> it = mActivityStack.iterator(); it.hasNext(); ) {WeakReference<Activity> activityReference = it.next();Activity temp = activityReference.get();if (temp == null || temp.isFinishing()) {it.remove();}}}}/*** 获取当前Activity(栈中最后一个压入的)** @return*/public Activity currentActivity() {checkWeakReference();if (mActivityStack != null && !mActivityStack.isEmpty()) {return mActivityStack.lastElement().get();}return null;}/*** 关闭当前Activity(栈中最后一个压入的)*/public void finishActivity() {Activity activity = currentActivity();if (activity != null) {finishActivity(activity);}}/*** 关闭指定的Activity*/public void finishActivity(Activity activity) {if (activity != null && mActivityStack != null) {// 使用迭代器进行安全删除for (Iterator<WeakReference<Activity>> it = mActivityStack.iterator(); it.hasNext(); ) {WeakReference<Activity> activityReference = it.next();Activity temp = activityReference.get();// 清理掉已经释放的activityif (temp == null) {it.remove();continue;}if (temp == activity) {it.remove();}}activity.finish();}if (mActivityStack != null && mActivityStack.empty()) {taskMap.remove(this);}}/*** 结束所有Activity*/public void finishAllActivity() {if (mActivityStack != null) {for (WeakReference<Activity> activityReference : mActivityStack) {Activity activity = activityReference.get();if (activity != null) {activity.finish();}}mActivityStack.clear();}if (mActivityStack != null && mActivityStack.empty()) {taskMap.remove(this);}}//    /**
//     * 退出应用程序
//     */
//    public void exitApp() {
//        try {
//            finishAllActivity();
//            // 退出JVM,释放所占内存资源,0表示正常退出
//            System.exit(0);
//            // 从系统中kill掉应用程序
//            android.os.Process.killProcess(android.os.Process.myPid());
//        } catch (Exception e) {
//           Timber.e(e);
//        }
//    }public static void clearAll() {if (taskMap == null || taskMap.isEmpty()) return;for (ActivityTaskManager taskManager : taskMap.values()) {taskManager.finishAllActivity();}taskMap.clear();}public static boolean hasActivity() {if (taskMap == null) {return false;}for (String key : taskMap.keySet()) {ActivityTaskManager activityTaskManager = taskMap.get(key);if (activityTaskManager != null && activityTaskManager.mActivityStack != null) {activityTaskManager.checkWeakReference();if (!activityTaskManager.mActivityStack.empty()) {return true;}}}return false;}private static boolean hasMainActivity = false;public static void setHasMainActivity(boolean has) {hasMainActivity = has;}public static boolean getHasMainActivity() {return hasMainActivity;}
}

业务实践

假设这样一个业务场景

  1. 用户购物时从商品列表页 进入商品详情页
  2. 接着点击购买 进入商品购买页
  3. 支付完成后 进入购买成功页,当前点击完成结束流程,退回到商品列表页

回退场景

  • 业务未完成之前回退时 返回上级页面
  • 业务完成后回退时 返回起始页面

Demo类对照业务场景

  • MainActivity = 商品列表页
  • AActivity = 商品详情页
  • BActivity = 商品购买页
  • CActivity = 购买成功页

Tip:使用非常简单,直接按照代码示例来就行

MainActivity

package com.example.taskmanageimport android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextViewclass MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val goView = findViewById<TextView>(R.id.tv_go)goView.setOnClickListener {startActivity(Intent(this,AActivity::class.java))}}
}

activity_main

<?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=".MainActivity"><TextViewandroid:id="@+id/tv_go"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="主页面!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

AActivity

package com.example.taskmanageimport android.annotation.SuppressLint
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextViewclass AActivity : AppCompatActivity() {@SuppressLint("MissingInflatedId")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_a)ActivityTaskManager.getManager("Buy").addActivity(this)val goView = findViewById<TextView>(R.id.tv_go)goView.setOnClickListener {startActivity(Intent(this,BActivity::class.java))}}
}

activity_a

<?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=".MainActivity"><TextViewandroid:id="@+id/tv_go"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="A-Activity"android:textStyle="bold"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

BActivity

package com.example.taskmanageimport android.annotation.SuppressLint
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextViewclass BActivity : AppCompatActivity() {@SuppressLint("MissingInflatedId")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_b)val goView = findViewById<TextView>(R.id.tv_go)goView.setOnClickListener {startActivity(Intent(this,CActivity::class.java))}}
}

activity_b

<?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=".MainActivity"><TextViewandroid:id="@+id/tv_go"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="B-Activity"android:textStyle="bold"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

CActivity

package com.example.taskmanageimport android.annotation.SuppressLint
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextViewclass CActivity : AppCompatActivity() {@SuppressLint("MissingInflatedId")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_c)ActivityTaskManager.getManager("Buy").addActivity(this)val endView = findViewById<TextView>(R.id.tv_end)val goView = findViewById<TextView>(R.id.tv_go)endView.setOnClickListener {ActivityTaskManager.getManager("Buy").finishActivity()}}override fun onBackPressed() {super.onBackPressed()ActivityTaskManager.getManager("Buy").finishActivity()}
}

activity_c

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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"android:orientation="vertical"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv_end"android:layout_width="match_parent"android:layout_height="50dp"android:gravity="center"android:text="结束业务流程"android:textStyle="bold" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="@color/black" /><TextViewandroid:id="@+id/tv_go"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:text="C-Activity"android:textStyle="bold" /></androidx.appcompat.widget.LinearLayoutCompat>

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

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

相关文章

leetcode-用栈实现队列

232. 用栈实现队列 前面有道题是用队列实现栈 &#xff0c;本题反过来了&#xff0c;是使用两个栈来模拟队列。 题解&#xff1a; 我们定义两个栈一个进栈in_stack&#xff0c;一个出栈out_stack&#xff0c;在push数据的时候将数据放入进栈就好&#xff0c;但在pop的时候&a…

【Java】MybatisPlus入门

学习目标 能够基于MyBatisPlus完成标准Dao开发 能够掌握MyBatisPlus的条件查询 能够掌握MyBatisPlus的字段映射与表名映射 能够掌握id生成策略控制 能够理解代码生成器的相关配置 一、MyBatisPlus简介 1. 入门案例 问题导入 MyBatisPlus环境搭建的步骤&#xff1f; 1.1 Sp…

部署实战--修改jar中的文件并重新打包成jar文件

一.jar文件 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&#xff0c;多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…

Go语言中...(三个点)的使用几个常见情况

①可变参数函数&#xff08;Variadic Functions&#xff09;&#xff1a; 在函数定义中&#xff0c;“…” 可以用于表示可变参数。这使得函数可以接受任意数量的参数。例如&#xff1a; func sum(nums ...int) int {total : 0for _, num : range nums {total num}return tot…

DPVS 多活部署架构部署

一、目标 利用DPVS部署一个基于OSPF/ECMP的提供HTTP服务的多活高可用的测试环境。 本次部署仅用于验证功能&#xff0c;不提供性能验证。 配置两台DPVS组成集群、两台REAL SERVER提供实际HTTP服务。 注&#xff1a;在虚拟环境里面&#xff0c;通过在一台虚拟服务器上面安装FR…

mybatis查询修改mysql的json字段

前言&#xff1a; mysql5.7版本之后支持json字段类型&#xff0c;推荐mysql8版本&#xff0c;适用于属性不确定的个性化字段&#xff0c;比如: 身份信息{“职业”,“学生”,“兴趣”:“打乒乓球”,“特长”:“跳高&#xff0c;书法”}; 图片信息{“日期”:“2023-12-12 22:12”…

flinkjar开发 自定义函数

编写自定义加密函数&#xff0c;继承ScalarFunction类&#xff0c;实现eval方法&#xff0c;参数个数类型和返回值根据业务来自定义。 import org.apache.flink.table.functions.ScalarFunction; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax…

ELK集群搭建(基础教程)

ELK集群搭建(基础教程) 目录&#xff1a; 机器准备 集群内各台机器安装Elasticsearch 安装部署Kafka&#xff08;注&#xff1a;每个节点都配置&#xff0c;注意ip不同&#xff09; 安装logstash工具 安装filebeat ELK收集Nginx的json日志 ELK收集Nginx正常日志和错误日…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之TimePicker组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之TimePicker组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 编辑 二、TimePicker组件 TextClock组件通过文本将当前系统时间显示在设备上。…

Java数组的静态初始化、动态初始化和默认初始化

以下是Java数组的静态初始化、动态初始化和默认初始化的示例&#xff1a; 静态初始化&#xff1a; 静态初始化是在声明数组时直接赋值&#xff0c;不需要使用new关键字。例如&#xff1a; int[] staticArray {10, 20, 30, 40};这里&#xff0c;staticArray是一个静态初始化…

华为配置使用SNMPv1与网管通信示例

配置使用SNMPv1与网管通信示例 组网图形 图1 配置使用SNMPv1与网管通信组网图 SNMP简介配置注意事项组网需求配置思路操作步骤配置文件 SNMP简介 简单网络管理协议SNMP&#xff08;Simple Network Management Protocol&#xff09;是广泛应用于TCP/IP网络的网络管理标准协议。S…

AJAX-接口文档

接口文档&#xff1a;由后端提供的描述接口的文章 接口&#xff1a;使用AJAX和服务器通讯时&#xff0c;使用的URL&#xff0c;请求方法&#xff0c;以及参数 1.请求参数的位置为query&#xff08;查询&#xff09;的时候&#xff0c;就说明要使用params写为查询参数 2.请求参…

神经网络 | 基于多种神经网络模型的轴承故障检测

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要源自《第二届全国技能大赛智能制造工程技术项目比赛试题&#xff08;样题&#xff09; 模块 E 工业大数据与人工智能应用》&#xff0c;基于给出的已知轴承状态的振动信号样本&#xff0c;对数据进行分析&#xff0c;建…

PDF中公式转word

效果&#xff1a;实现pdf中公式免编辑 step1: 截图CtrlAltA&#xff0c;复制 step2: SimpleTex - Snip & Get 网页或客户端均可&#xff0c;无次数限制&#xff0c;效果还不错。还支持手写、文字识别 单张图片&#xff1a;选 手写板 step3: 导出结果选择 注&#xff1a;…

PriorityBlockingQueue的tryGrow方法

前言: 最近看PriorityBlockingQueue这个类的过程中&#xff0c;对扩容方法产生了一些困惑&#xff0c;特此记录下自己思索的过程。 PriorityBlockingQueue&#xff1a; PriorityBlockingQueue 是带优先级的无界阻塞队列&#xff0c;每次出队都返回优先级最高或者最低的元素。…

pdmodel从动态模型转成静态onnx

1.下载项目 git clone https://github.com/jiangjiajun/PaddleUtils.git 2.新建两个新的文件夹 第一个文件夹放两个必要文件 第二个文件夹可以设置为空&#xff0c;用来存放转换后的模型 如图&#xff1a; 3.在终端运行 python paddle/paddle_infer_shape.py --model_dir …

GMSSL之ZUC256算法

GmSSL介绍 GmSSL是一个开源的密码工具箱&#xff0c;支持SM2/SM3/SM4/SM9/ZUC等国密(国家商用密码)算法。 从 GmSSL 官网处得到的下载链接为 GitHub - guanzhi/GmSSL: 支持国密SM2/SM3/SM4/SM9/SSL的密码工具箱 GmSSL的下载编译如下&#xff1a; # git clone https://g…

谷粒商城【成神路】-【4】——分类维护

目录 1.删除功能的实现 2.新增功能的实现 3.修改功能的实现 4.拖拽功能 1.删除功能的实现 1.1逻辑删除 逻辑删除&#xff1a;不删除数据库中真实的数据&#xff0c;用指定字段&#xff0c;显示的表示是否删除 1.在application.yml中加入配置 mybatis-plus:global-config:…

【PostgreSQL内核学习(二十五) —— (DBMS存储空间管理)】

DBMS存储空间管理 概述块&#xff08;或页面&#xff09;PageHeaderData 结构体HeapTupleHeaderData 结构 表空间表空间的作用&#xff1a;表空间和数据库关系表空间执行案例 补充 —— 模式&#xff08;Schema&#xff09; 声明&#xff1a;本文的部分内容参考了他人的文章。在…

【HarmonyOS】鸿蒙开发之自定义组件——第3.7章

自定义构建函数 (适合内部页面的封装&#xff0c;更加合适)(构建页面) 案例: 自定义组件文件 Index.ets //全局自定义构建函数写法 Builder function item1(){Row({space:10}){Text("我是自定义构建函数")} }Component export struct Index{build(){Column(){item…