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();
                // 清理掉已经释放的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;
    }
}

业务实践

假设这样一个业务场景

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

回退场景

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

Demo类对照业务场景

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

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>

你可能感兴趣的:(Android进阶之路,#,项目开发知识点归纳,Android,业务栈管理,业务流程管理)