关于业务栈的管理方式,我在去年刚接触当前项目的时候就想记录一下,但是一直晃晃悠悠拖到了现在,索性在春节前以其收尾也是不错。其实这篇内容在项目中肯定经常用得到,但是关于标题命名我却不知道如何描述…
在实际业务中为了形成业务闭环,经常需要对一条完整的业务线进行管理,而承载业务的组件一般都是 Activity
,所以也可以说是对 Activity
的管理
关于Activity管理的篇章,我早期曾写过类似的一篇 Android进阶之路 - 强制下线、退出登录,内部方法可能有所改变,但是部分思想是可以借鉴的
如需 gif 效果,年后补入
在该篇的 Activity
管理类中用到了弱引用(Weak Reference
),特此给大家说一下Java中的四种引用类型(经常被人问到),如下:
Strong Reference
):这是最常见的引用类型,它指向一个对象,只要强引用存在,对象就不会被垃圾回收器回收。如果对象没有任何强引用指向它,垃圾回收器将在适当的时候回收该对象。1Soft Reference
):软引用指向的对象在内存不足时会被垃圾回收器回收,而软引用所指向的对象在垃圾回收时会被立即回收。这种引用类型通常用于对内存敏感的应用程序,例如缓存。Weak Reference
):弱引用指向的对象在垃圾回收时会被立即回收。这种引用类型通常用于需要在对象被回收之前完成某些操作的情况。Virtual Reference
):虚引用主要用于跟踪对象被垃圾回收的状态,但不能通过虚引用来获取对象的实例。虚引用只能在配合 ReferenceQueue
使用时才有意义。虚引用通常在设计模式中使用,例如单例模式。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();
// 清理掉已经释放的activity
if (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;
}
}
假设这样一个业务场景
回退场景
Demo类对照业务场景
Tip
:使用非常简单,直接按照代码示例来就行
MainActivity
package com.example.taskmanage
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class 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
<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">
<TextView
android: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.taskmanage
import android.annotation.SuppressLint
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class 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
<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">
<TextView
android: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.taskmanage
import android.annotation.SuppressLint
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class 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
<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">
<TextView
android: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.taskmanage
import android.annotation.SuppressLint
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class 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
<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">
<TextView
android:id="@+id/tv_end"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="结束业务流程"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black" />
<TextView
android: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>