android backstack 清空,【Fragivity框架介绍】如何处理BackStack的销毁重建

本文探讨了Fragivity在Navigation基础上实现的免配置路由跳转机制,分析了其在Activity重建时面临的BackStack恢复问题,并提出了使用ViewModel保存Destination信息的方法,以确保在不同情况下的BackStack一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2d05f605f5a11b49df2558f52b2638fa.png

Fragivity在Navigation基础上实现了免配置的路由跳转:无需事先配置NavGraph,在跳转时再动态更新Graph,这在方便了使用的同时带来一个问题:

FragmentManager#mBackStack

FragmentManger一样维护了一个mBackStack,当Activity恢复重建时,会对BackStack重建以恢复之前的栈状态

//FragmentManager.java

void restoreSaveState(@Nullable Parcelable state){

// If there is no saved state at all, then there's nothing else to do

if (state == null) return;

FragmentManagerState fms = (FragmentManagerState) state;

if (fms.mActive == null) return;

...

// Build the back stack.

if (fms.mBackStack != null) {

mBackStack = new ArrayList<>(fms.mBackStack.length);

for (int i = 0; i < fms.mBackStack.length; i++) {

BackStackRecord bse = fms.mBackStack[i].instantiate(this);

mBackStack.add(bse);

}

}

...

}

复制代码

NavController#mBackStack

Navigation在NavController中同样维护了一个mBackStack,管理栈内的Destination。为了保证与FragmentManager中的mBackStack一致性,NavController借助mBackStackToRestore销毁重建

//NavController.java

public void restoreState(@Nullable Bundle navState){

...

mBackStackToRestore = navState.getParcelableArray(KEY_BACK_STACK);

...

}

复制代码

//NavController.java

private void onGraphCreated(@Nullable Bundle startDestinationArgs){

...

if (mBackStackToRestore != null) {

for (Parcelable parcelable : mBackStackToRestore) {

NavBackStackEntryState state = (NavBackStackEntryState) parcelable;

NavDestination node = findDestination(state.getDestinationId());

if (node == null) {

...

}

Bundle args = state.getArgs();

if (args != null) {

args.setClassLoader(mContext.getClassLoader());

}

NavBackStackEntry entry = new NavBackStackEntry(mContext, node, args,

mLifecycleOwner, mViewModel,

state.getUUID(), state.getSavedState());

mBackStack.add(entry);

}

}

...

}

复制代码

如上,恢复重建时需要根据state信息重建Destination

NavDestination node = findDestination(state.getDestinationId());

复制代码

findDestination内部需要在NavGraph查询节点信息。对于Navigation来说,因为有实现配置,Activity重启后会根据Xml或者Dsl重建Graph,但是对于Fragivity来说,Graph是动态更新的,此时无法成功findDestination。

使用ViewModel保存Destination

Activity重启时要在NavGraph中找到所有BackStack中的Destination信息,所以我们需要将所有入栈的Fragment信息保存到重建后以用来重建Grap中,很自然地我们想到使用ViewModel跨越Framgent生命周期这一特性来实现。

internal class MyViewModel : ViewModel() {

val nodes = SparseArrayCompat()

}

class MyNavHost : NavHost {

...

internal fun saveToViewModel(destination: NavDestination) {

val vm = ViewModelProvider(requireActivity())[MyViewModel::class.java]

if (vm.nodes.keyIterator().asSequence().any {

it == destination.id

}) return

vm.nodes.put(destination.id, destination)

}

...

}

复制代码

Fragment压栈的同时记录到ViewModel

internal fun MyNavHost.putFragment(

clazz: KClass

): FragmentNavigator.Destination {

val destId = clazz.hashCode()

val graph = navController.graph

var destination = graph.findNode(destId) as? FragmentNavigator.Destination

if (destination == null) {

destination = navController.createNavDestination(destId, clazz)

graph.plusAssign(destination)

saveToViewModel(destination)

}

return destination

}

复制代码

当恢复重建时,从ViewModel恢复Destination信息到Graph

fun NavHostFragment.loadRoot(root: KClass) {

...

graph = createGraph(startDestination = startDestId) {

...

}.also { graph ->

//restore destination from vm for NavController#mBackStackToRestore

val activity = requireActivity()

val vm = ViewModelProvider(activity ).get(MyViewModel::class.java)

vm.nodes.valueIterator().forEach {

it.removeFromParent()

graph += it

}

}

}

复制代码

ViewModel的持久化

通过ViewModel虽然可以解决Configurations Change之后的销毁重建,但是对于进程被杀的销毁重建仍然无效。

跨进程保存需要借助ViewModel的持久化机制,改造如下:

internal class MyViewModel(private val _handle: SavedStateHandle) : ViewModel() {

lateinit var navController: NavController

val nodes by lazy {

val list: List =

_handle.get(NAV_DEST_NODES_KEY)?.getParcelableArrayList(null) ?: emptyList()

val nodes = SparseArrayCompat()

list.forEach {

val clazz = Class.forName(it.className) as Class

val destination = navController.createNavDestination(clazz.hashCode(), clazz.kotlin)

nodes.put(destination.id, destination)

}

nodes

}

init {

/**

* 当杀进程重启(例如不保留活动等)时,NavGraph中的nodes信息也需要重建,

* 所以需要借助SavedStateHandle对ViewModel数据持久化

*/

_handle.setSavedStateProvider(NAV_DEST_NODES_KEY) {

val list = ArrayList().apply {

nodes.valueIterator().forEach { add(NavDestinationBundle((it))) }

}

Bundle().apply { putParcelableArrayList(null, list) }

}

}

}

复制代码

然后借助SavedStateViewModelFactory从ViewModel恢复持久化信息

fun NavHostFragment.loadRoot(root: KClass) {

...

graph = createGraph(startDestination = startDestId) {

...

}.also { graph ->

//restore destination from vm for NavController#mBackStackToRestore

val activity = requireActivity()

val vm = ViewModelProvider(

activity,

SavedStateViewModelFactory(activity.application, activity)

).get(MyViewModel::class.java).also {

it.navController = this

}

vm.nodes.valueIterator().forEach {

it.removeFromParent()

graph += it

}

}

}

复制代码

### 如何清空栈(Stack)数据结构 #### C语言中的链式栈清空操作 在C语言中,如果使用的是链表形式的栈(即链式栈),可以通过遍历整个链表并逐一释放节点的方式来完成清空操作。以下是基于引用的内容实现的一个具体例子[^1]: ```c void linkstack_clear(linklist s) { linklist p; p = s->next; // 获取第一个节点 puts("clean: "); while (p) { // 遍历链表直到最后一个节点 s->next = p->next; // 修改当前头结点指向下一个节点 printf("%d ", p->data); // 打印即将被删除的节点数据 free(p); // 释放该节点内存 p = s->next; // 更新到新的节点 } } ``` 上述代码通过`while`循环逐步访问每一个节点,并调用`free()`函数将其从堆内存中释放。 --- #### Go语言中的数组栈清空操作 Go语言通常会采用切片作为底层存储方式来模拟栈的行为,在这种情况下,“清空”可以简单地重置栈的状态或将内部使用的变量设置为初始状态。以下是一个具体的实例[^2]: ```go func (s *stack) Clear() { if s != nil && !s.Empty() { s.mutex.Lock() s.top = 0 // 将顶部指针重置为零 s.data = []interface{}{} // 切片重新初始化为空 s.cap = 0 // 容量也设回默认值 s.mutex.Unlock() } } ``` 此方法不仅将栈顶索引归零,还彻底清理了底层数组以防止残留旧数据占用不必要的资源。 --- #### Python 中的列表栈清空操作 Python 的内置 `list` 类型非常适合用来构建简单的栈结构。要清除其中的所有元素,则可以直接赋给一个新的空列表或者调用其自带的方法 `.clear()` 来达到目的: ```python class Stack: def __init__(self): self.items = [] def clear(self): """Clear all items from the stack.""" self.items.clear() # Example usage: my_stack = Stack() for i in range(5): my_stack.items.append(i) print(f'Before clearing: {my_stack.items}') my_stack.clear() print(f'After clearing : {my_stack.items}') # Output should be an empty list [] ``` 这里展示了如何定义一个类封装基本功能以及提供专门用于清空的操作接口。 --- #### 总结不同编程环境下的通用策略 无论是哪种语言或实现方式,核心思路都是让表示栈的数据容器回到最初创建时的样子 —— 即没有任何有效项存在为止。这可能涉及调整计数器/指标位置、销毁动态分配的对象或者是单纯替换掉原有的集合对象等等措施之一或多者组合而成的实际做法取决于具体情况而定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值