Skip to content

Commit

Permalink
develop navigator.pushTo() ing...
Browse files Browse the repository at this point in the history
see: #45
see: #47
  • Loading branch information
seiko committed Jul 20, 2021
1 parent 84bf0c5 commit f7c980e
Show file tree
Hide file tree
Showing 13 changed files with 161 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ class LaunchModeFragment : AbsBaseFragment() {
binding.btnPoptohome.setOnClickListener {
navigator.popTo(HomeFragment::class)
}

binding.btnPushtohome.setOnClickListener {
navigator.pushTo(HomeFragment::class)
}
}

override val titleName: String
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/layout/fragment_launch_mode.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@
android:layout_height="wrap_content"
android:text="PopBack To Home"/>

<Button
android:id="@+id/btn_pushtohome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Push To Home"/>

</LinearLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,14 @@ class FragivityFragmentNavigator(
val fragment = createFragment(destination, args)
ft.add(containerId, fragment, generateBackStackName(backStack.size, destId))

val prevFragment = fragmentManager.primaryNavigationFragment
val isPushTo = args?.getBoolean(KEY_PUSH_TO, false) == true
val prevFragment = if (isPushTo) {
fragmentManager.fragments.forEach { ft.remove(it) }
null
} else {
fragmentManager.primaryNavigationFragment
}

ft.setPrimaryNavigationFragment(fragment)

val isSingleTopReplacement = !initialNavigation
Expand Down Expand Up @@ -273,6 +280,8 @@ class FragivityFragmentNavigator(
companion object {
private const val TAG = "FragivityNavigator"
private const val KEY_BACK_STACK_IDS = "myFragmentNavigator:backStackIds"

internal const val KEY_POP_SELF = "Fragivity:PopSelf"
internal const val KEY_PUSH_TO = "Fragivity:PushTo"
}
}
5 changes: 5 additions & 0 deletions library/src/main/kotlin/androidx/navigation/Ext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ internal fun NavController.removeLastBackStackEntry(): NavBackStackEntry? {
return mBackStack.removeLast()
}

@JvmSynthetic
internal fun NavController.clearBackStackEntry() {
mBackStack.clear()
}

// ≈ mBackStack.size <= 1
@JvmSynthetic
internal fun NavController.isNullRootNode(): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ private fun NavHostFragment.composableInternal(
}

// save destination for rebuild
navigator.saveDestination(node)
navigator.addNode(node)
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ private fun NavHostFragment.loadRootInternal(
})
}

private fun NavHostFragment.setupGraph(startNode: NavDestination) {
@JvmSynthetic
internal fun NavHostFragment.setupGraph(startNode: NavDestination) {
val nodeViewModel = getViewModel<FragivityNodeViewModel>()
with(navController) {
val block: NavGraphBuilder.() -> Unit = {
Expand Down
45 changes: 4 additions & 41 deletions library/src/main/kotlin/com/github/fragivity/ActionPush.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ package com.github.fragivity

import android.os.Bundle
import androidx.core.net.toUri
import androidx.fragment.app.FragivityFragmentDestination
import androidx.fragment.app.FragivityFragmentNavigator
import androidx.fragment.app.Fragment
import androidx.navigation.*
import androidx.navigation.fragment.FragmentNavigator
import kotlin.reflect.KClass

@JvmSynthetic
Expand Down Expand Up @@ -40,7 +38,7 @@ fun FragivityNavHost.push(
clazz: KClass<out Fragment>,
navOptions: NavOptions?
) {
pushInternal(putNode(clazz), navOptions)
pushInternal(getOrCreateNode(clazz), navOptions)
}

/**
Expand All @@ -60,46 +58,11 @@ fun FragivityNavHost.push(
navOptions: NavOptions?,
factory: (Bundle) -> Fragment
) {
pushInternal(putNode(clazz, factory), navOptions)
pushInternal(getOrCreateNode(clazz, factory), navOptions)
}

@JvmSynthetic
internal fun FragivityNavHost.putNode(
clazz: KClass<out Fragment>,
factory: ((Bundle) -> Fragment)? = null
): FragmentNavigator.Destination {
val nodeId = clazz.positiveHashCode
val graph = navController.graph
var node = graph.findNode(nodeId) as? FragmentNavigator.Destination
if (node == null) {
node = if (factory != null) {
navController.createNode(nodeId, factory)
} else {
navController.createNode(nodeId, clazz)
}
graph += node
saveDestination(node)
return node
}
// check destination is valid
if (factory != null) {
if (node is FragivityFragmentDestination) {
node.factory = factory
return node
}
} else {
if (node !is FragivityFragmentDestination) {
return node
}
}
// rebuild destination
graph -= node
removeDestination(node)
return putNode(clazz, factory)
}

@JvmSynthetic
private fun FragivityNavHost.pushInternal(
internal fun FragivityNavHost.pushInternal(
node: NavDestination,
navOptions: NavOptions?,
matchingArgs: Bundle? = null
Expand All @@ -117,7 +80,7 @@ private fun FragivityNavHost.pushInternal(
val oldNode = removeLastBackStackEntry()?.destination
if (oldNode != null) {
graph.remove(oldNode)
removeDestination(oldNode)
removeNode(oldNode)
}
// 如果rootNode被删除了,认为新push的node为rootNode
if (isNullRootNode()) {
Expand Down
46 changes: 46 additions & 0 deletions library/src/main/kotlin/com/github/fragivity/ActionPushTo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.github.fragivity

import androidx.core.os.bundleOf
import androidx.fragment.app.FragivityFragmentNavigator.Companion.KEY_PUSH_TO
import androidx.fragment.app.Fragment
import androidx.navigation.clearBackStackEntry
import kotlin.reflect.KClass

/**
* 跳转到目标页面,并清空所有旧页面
*/
@JvmSynthetic
fun FragivityNavHost.pushTo(clazz: KClass<out Fragment>) {

val node = getOrCreateNode(clazz)

// 删除原有parent(NavGraph)
node.parent?.remove(node)

// 清空所有node
viewModel.clearNodes()

// 重新保存
addNode(node)

// 主动清空旧NavHost防止内存泄漏
val viewModel = viewModel
val navController = navController
onCleared()

// 修改开始id用于重建
viewModel.startNodeId = node.id

with(navController) {
// 清空返回栈
clearBackStackEntry()

// 重建graph
setGraph(createGraph(node), bundleOf(
KEY_PUSH_TO to true
))

// 重新绑定nav范围的viewModel
fragivityHostViewModel.setUpNavHost(viewModel, this)
}
}
52 changes: 50 additions & 2 deletions library/src/main/kotlin/com/github/fragivity/CreateNode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import android.os.Bundle
import androidx.fragment.app.FragivityFragmentDestination
import androidx.fragment.app.FragivityFragmentNavigator
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.*
import androidx.navigation.fragment.FragmentNavigator
import androidx.navigation.get
import com.github.fragivity.deeplink.getRouteUri
import kotlin.reflect.KClass

Expand All @@ -33,3 +32,52 @@ internal fun NavController.createNode(
id = nodeId
this.factory = block
}

private fun NavController.createNode(
nodeId: Int,
clazz: KClass<out Fragment>,
block: ((Bundle) -> Fragment)? = null
) = if (block != null) {
createNode(nodeId, block)
} else {
createNode(nodeId, clazz)
}

@JvmSynthetic
internal fun FragivityNavHost.getOrCreateNode(
clazz: KClass<out Fragment>,
block: ((Bundle) -> Fragment)? = null
): FragmentNavigator.Destination {
val nodeId = clazz.positiveHashCode

val graph = navController.graph

var node = graph.findNode(nodeId) as? FragmentNavigator.Destination
if (node == null) {
node = navController.createNode(nodeId, clazz, block)
graph += node
addNode(node)
return node
}

// check destination is valid
if (block != null) {
if (node is FragivityFragmentDestination) {
node.factory = block
return node
}
} else {
if (node !is FragivityFragmentDestination) {
return node
}
}

// rebuild destination
graph -= node
removeNode(node)

node = navController.createNode(nodeId, clazz, block)
graph += node
addNode(node)
return node
}
24 changes: 17 additions & 7 deletions library/src/main/kotlin/com/github/fragivity/FragivityNavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,31 @@ import androidx.navigation.NavHost
* NavHost with viewModel to store NavDestination
*/
class FragivityNavHost(
private val viewModel: FragivityNodeViewModel,
private val navController: NavController
@JvmSynthetic
internal var _viewModel: FragivityNodeViewModel?,
private var _navController: NavController?
) : NavHost {

val viewModel: FragivityNodeViewModel
get() = _viewModel!!

override fun getNavController(): NavController {
return navController
return _navController!!
}

@JvmSynthetic
internal fun addNode(node: NavDestination) {
viewModel.addNode(node)
}

@JvmSynthetic
internal fun saveDestination(node: NavDestination) {
viewModel.saveDestination(node)
internal fun removeNode(node: NavDestination) {
viewModel.removeNode(node)
}

@JvmSynthetic
internal fun removeDestination(node: NavDestination) {
viewModel.removeDestination(node)
internal fun onCleared() {
_viewModel = null
_navController = null
}
}
17 changes: 13 additions & 4 deletions library/src/main/kotlin/com/github/fragivity/Lifecycle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,24 @@ val View.navigator: FragivityNavHost
get() = findNavController().navigator

val NavController.navigator: FragivityNavHost
get() = fragivityHostViewModel.navHost
get() = fragivityHostViewModel.navHost!!

internal val NavController.fragivityHostViewModel: FragivityHostViewModel
get() = ViewModelProvider(getViewModelStoreOwner(graph.id))
.get(FragivityHostViewModel::class.java)

class FragivityHostViewModel : ViewModel() {

lateinit var navHost: FragivityNavHost
var navHost: FragivityNavHost? = null

internal fun setUpNavHost(viewModel: FragivityNodeViewModel, navController: NavController) {
navHost = FragivityNavHost(viewModel, navController)
}

override fun onCleared() {
super.onCleared()
navHost?.onCleared()
}
}

class FragivityNodeViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
Expand All @@ -53,12 +58,12 @@ class FragivityNodeViewModel(private val savedStateHandle: SavedStateHandle) : V
.forEach { graphBuilder.addDestination(it) }
}

fun saveDestination(node: NavDestination) {
fun addNode(node: NavDestination) {
val key = NAV_DEST_PREFIX + node.id
savedStateHandle.set(key, node.toBundle())
}

fun removeDestination(node: NavDestination) {
fun removeNode(node: NavDestination) {
val key = NAV_DEST_PREFIX + node.id
if (savedStateHandle.contains(key)) {
savedStateHandle.remove<NavDestinationBundle>(key)
Expand All @@ -67,6 +72,10 @@ class FragivityNodeViewModel(private val savedStateHandle: SavedStateHandle) : V

override fun onCleared() {
super.onCleared()
clearNodes()
}

fun clearNodes() {
navDestSequence().forEach {
savedStateHandle.remove<NavDestinationBundle>(it)
}
Expand Down
4 changes: 2 additions & 2 deletions library/src/main/kotlin/com/github/fragivity/deeplink/Ext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import android.content.Intent.ACTION_VIEW
import android.os.Handler
import androidx.annotation.MainThread
import androidx.navigation.fragment.NavHostFragment
import com.github.fragivity.getOrCreateNode
import com.github.fragivity.navigator
import com.github.fragivity.putNode

private val _isRouteInit by lazy(LazyThreadSafetyMode.NONE) {
val c = Class.forName("com.github.fragivity._RouteLoaderKt")
Expand All @@ -18,7 +18,7 @@ private val _isRouteInit by lazy(LazyThreadSafetyMode.NONE) {
fun NavHostFragment.handleDeepLink(intent: Intent) = with(intent) {
if (ACTION_VIEW == action && _isRouteInit) {
getFragmentInfo(data.toString())?.let {
navigator.putNode(it)
navigator.getOrCreateNode(it)
//post用来确保HomeFragment已进入fragmentManager,才能获取并对其进行hide
Handler().post { navController.navigate(data!!) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ private fun FragivityNavHost.putDialog(
label = clazz.qualifiedName
}.build()
graph.plusAssign(destination)
saveDestination(destination)
addNode(destination)
}
return destination
}
Expand Down

0 comments on commit f7c980e

Please sign in to comment.