Navigation(二) 一、使用navigation角度上分析源碼

Android知識總結

一、使用navigation角度上分析源碼

1、Fragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val btn = view.findViewById<Button>(R.id.btn)
    btn.setOnClickListener { view ->
       // 在這裏開始導航
        Navigation.findNavController(view).navigate(R.id.action_page2)
    }
}

2、 NavController.navigation

public void navigate(@IdRes int resId) {
    navigate(resId, null);
}

public void navigate(@IdRes int resId, @Nullable Bundle args) {
    navigate(resId, args, null);
}

public void navigate(@IdRes int resId, @Nullable Bundle args,
        @Nullable NavOptions navOptions) {
    navigate(resId, args, navOptions, null);
}

public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions,
        @Nullable Navigator.Extras navigatorExtras) {
    // 目的 是在 Graph裏面去尋找 當前節點的Fragment 爲 目的地
    NavDestination currentNode = mBackStack.isEmpty() // 先去看看棧有沒有
            ? mGraph
            : mBackStack.getLast().getDestination(); // 如果棧沒有,就取最後一個
    if (currentNode == null) {
        throw new IllegalStateException("no current navigation node");
    }
    @IdRes int destId = resId;
    // 獲取節點後,就要去獲取 action 導航動作,只有action才能通過resId 獲取下一個要導航的目標id
    final NavAction navAction = currentNode.getAction(resId); 
    Bundle combinedArgs = null;
    if (navAction != null) {
        if (navOptions == null) {
            navOptions = navAction.getNavOptions();
        }
         // 獲取下一個目的地的 id號信息等
        destId = navAction.getDestinationId();
        Bundle navActionArgs = navAction.getDefaultArguments();
        if (navActionArgs != null) {
            combinedArgs = new Bundle();
            combinedArgs.putAll(navActionArgs);
        }
    }
    
    if (args != null) {
        if (combinedArgs == null) {
            combinedArgs = new Bundle();
        }
        combinedArgs.putAll(args);
    }

    if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != -1) {
        popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
        return;
    }

    if (destId == 0) {
        throw new IllegalArgumentException("Destination id == 0 can only be used"
                + " in conjunction with a valid navOptions.popUpTo");
    }
     
    // 檢查健壯性
    NavDestination node = findDestination(destId);
    if (node == null) {
        final String dest = NavDestination.getDisplayName(mContext, destId);
        if (navAction != null) {
            throw new IllegalArgumentException("Navigation destination " + dest
                    + " referenced from action "
                    + NavDestination.getDisplayName(mContext, resId)
                    + " cannot be found from the current destination " + currentNode);
        } else {
            throw new IllegalArgumentException("Navigation action/destination " + dest
                    + " cannot be found from the current destination " + currentNode);
        }
    }
    // 這行代碼就是我們要關心的主線流程
    navigate(node, combinedArgs, navOptions, navigatorExtras);
}

3、 NavController.navigation

private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
        @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
    boolean popped = false;
    boolean launchSingleTop = false;
    if (navOptions != null) {
        if (navOptions.getPopUpTo() != -1) {
            popped = popBackStackInternal(navOptions.getPopUpTo(),
                    navOptions.isPopUpToInclusive());
        }
    }
    //這裏又回到了,最初開始的代碼
    Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
            node.getNavigatorName());
    Bundle finalArgs = node.addInDefaultArgs(args);
    NavDestination newDest = navigator.navigate(node, finalArgs,
            navOptions, navigatorExtras);

    if (newDest != null) {
        if (!(newDest instanceof FloatingWindow)) {
            // We've successfully navigating to the new destination, which means
            // we should pop any FloatingWindow destination off the back stack
            // before updating the back stack with our new destination
            //noinspection StatementWithEmptyBody
            while (!mBackStack.isEmpty()
                    && mBackStack.peekLast().getDestination() instanceof FloatingWindow
                    && popBackStackInternal(
                            mBackStack.peekLast().getDestination().getId(), true)) {
                // Keep popping
            }
        }

        // When you navigate() to a NavGraph, we need to ensure that a new instance
        // is always created vs reusing an existing copy of that destination
        ArrayDeque<NavBackStackEntry> hierarchy = new ArrayDeque<>();
        NavDestination destination = newDest;
        if (node instanceof NavGraph) {
            do {
                NavGraph parent = destination.getParent();
                if (parent != null) {
                    NavBackStackEntry entry = new NavBackStackEntry(mContext, parent,
                            finalArgs, mLifecycleOwner, mViewModel);
                    hierarchy.addFirst(entry);
                    // Pop any orphaned copy of that navigation graph off the back stack
                    if (!mBackStack.isEmpty()
                            && mBackStack.getLast().getDestination() == parent) {
                        popBackStackInternal(parent.getId(), true);
                    }
                }
                destination = parent;
            } while (destination != null && destination != node);
        }

        // Now collect the set of all intermediate NavGraphs that need to be put onto
        // the back stack
        destination = hierarchy.isEmpty()
                ? newDest
                : hierarchy.getFirst().getDestination();
        while (destination != null && findDestination(destination.getId()) == null) {
            NavGraph parent = destination.getParent();
            if (parent != null) {
                NavBackStackEntry entry = new NavBackStackEntry(mContext, parent, finalArgs,
                        mLifecycleOwner, mViewModel);
                hierarchy.addFirst(entry);
            }
            destination = parent;
        }
        NavDestination overlappingDestination = hierarchy.isEmpty()
                ? newDest
                : hierarchy.getLast().getDestination();
        // Pop any orphaned navigation graphs that don't connect to the new destinations
        //noinspection StatementWithEmptyBody
        while (!mBackStack.isEmpty()
                && mBackStack.getLast().getDestination() instanceof NavGraph
                && ((NavGraph) mBackStack.getLast().getDestination()).findNode(
                        overlappingDestination.getId(), false) == null
                && popBackStackInternal(mBackStack.getLast().getDestination().getId(), true)) {
            // Keep popping
        }
        mBackStack.addAll(hierarchy);
        // The mGraph should always be on the back stack after you navigate()
        if (mBackStack.isEmpty() || mBackStack.getFirst().getDestination() != mGraph) {
            NavBackStackEntry entry = new NavBackStackEntry(mContext, mGraph, finalArgs,
                    mLifecycleOwner, mViewModel);
            mBackStack.addFirst(entry); // 留意一下,每次都會管理 進棧
        }
        // And finally, add the new destination with its default args
        NavBackStackEntry newBackStackEntry = new NavBackStackEntry(mContext, newDest,
                newDest.addInDefaultArgs(finalArgs), mLifecycleOwner, mViewModel);
        mBackStack.add(newBackStackEntry);
    } else if (navOptions != null && navOptions.shouldLaunchSingleTop()) {
        launchSingleTop = true;
        NavBackStackEntry singleTopBackStackEntry = mBackStack.peekLast();
        if (singleTopBackStackEntry != null) {
            singleTopBackStackEntry.replaceArguments(finalArgs);
        }
    }
    updateOnBackPressedCallbackEnabled();
    if (popped || newDest != null || launchSingleTop) {
        dispatchOnDestinationChanged();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章