Navigationskomponente Kotlin – vom aktuellen Ziel aus nicht auffindbar

Lesezeit: 8 Minuten

Ich habe ein Fragment A, B, C. Es ist in Ordnung, wenn von A -> B navigiert wird, aber von B -> C stürzt es ab.

Hier ist mein Navi

Geben Sie hier die Bildbeschreibung ein

Hier ist mein Navigationscode

 categoryProductItemListAdapter.setOnItemClickListener {
        val action = CategoryProductItemsDirections.actionCategoryProductItems2ToProductItem(null, it)
        navController = Navigation.findNavController(requireView())
        navController?.navigateUp()
        navController?.navigate(action)
    }

Hier ist der XML-Code für das Ziel zu productItem

<fragment
    android:id="@+id/categoryProductItems2"
    android:name="com.sample.store.main.dashboard.ui.ui.home.categoryitems.CategoryProductItems"
    android:label="CategoryProductItems"
    tools:layout="@layout/fragment_category_product_items">
    <argument
        android:name="category_global"
        app:argType="com.sample.store.data.globalmodels.response.categories.Category" />
    <action
        android:id="@+id/action_categoryProductItems2_to_productItem"
        app:destination="@id/productItem"
        app:enterAnim="@anim/enter_from_right"
        app:exitAnim="@anim/exit_to_right"
        app:popEnterAnim="@anim/fragment_open_enter"
        app:popExitAnim="@anim/fragment_fade_exit" />
</fragment>

Und hier ist der Fehler:

java.lang.IllegalArgumentException: Navigation action/destination com.sample.store.full:id/action_categoryProductItems2_to_productItem cannot be found from the current destination Destination(id/navigation_home) label=Home class=com.sample.store.main.dashboard.ui.ui.home.mainui.HomeFragment

Ich weiß nicht, was passiert ist, aber es scheint, dass der navController nach “navigation_home” sucht

  • können Sie die vollständige nav_host.xml-Datei freigeben

    – TRK P

    9. September 2020 um 6:38 Uhr

  • @Cyd entfernen Sie diese Zeile ` navController?.navigateUp()` kann Ihr Problem lösen

    – Asad Mahmud

    9. September 2020 um 11:23 Uhr

Dies ist eher ein Hinweis als eine Antwort. Aber ich hoffe es hilft.

Zusammenfassung: (Wie andere bereits gesagt haben:) Aufeinanderfolgende Aufrufe von Navigationsfunktionen sind der Grund für die meisten dieser Ausnahmen.

Angesichts der Struktur der Android-Komponenten und insbesondere der Funktionsweise von MediatorLiveData möchten Benutzer möglicherweise manchmal Datenknoten in einem einzigen beobachtbaren Datenbehälter (LiveData) zusammenführen.

Wenn eine Beobachtung dieses Mediators mit dynamischen Navigationsfunktionen verknüpft ist, werden zweifellos Fehler auftreten.

Der Grund dafür ist, dass Quellen einen LiveData-Wert so oft ändern können, wie es der Anzahl der Quellen entspricht, die mit dem Mediator verbunden sind.

Das ist eine absolut gute Idee, ABER. Wiederholte Änderungen am NavController führen definitiv zu unerwünschten Ergebnissen.

Dies kann beinhalten:

  • Den backStack zweimal knallen lassen.

  • Gehen Sie zweimal hintereinander von A -> B und geben Sie eine Ausnahme von A nicht gefunden
    das zweite Mal.

Dies ist ein großes Testproblem, insbesondere da die Ausgabe eines Fragments zu den darunter liegenden Stapeln kaskadieren kann, und wenn daher in einem Fragment eine Ausnahme von „Direction not found“ auftreten kann, kann der wahre Schuldige auf dem Fragment zuoberst des Gebenden gefunden werden die Ausnahme.

In Wirklichkeit wäre dies leicht zu lösen, indem ein selbstabbrechender Thread-Executor erstellt wird scheduled.cancel(true); mit einer Verzögerungstoleranz für die mediatorLiveData selbst (genauer gesagt die onChange, nicht die setValue(), da eifrige innere Zustandsaktualisierungen der gesamte und einzige Zweck/Witz des Mediators IMHO sind (sorry, kein postValue() erlaubt!)).

Ganz zu schweigen davon, dass der Mediator selbst eine unvollständige Komponente ist …

Ein weiterer einfacherer Ansatz besteht darin, sicherzustellen, dass onChange-Aufrufe von MutableLiveData nur dann ausgeführt werden, wenn !Object::Equals, und wiederholte Aufrufe von onChange() zu verhindern, was immer noch ein Beweis für die Unvollständigkeit von MediatorLiveData/LiveData ist. (Seien Sie nur äußerst vorsichtig mit Listen)

Vermeiden Sie um jeden Preis aufeinanderfolgende Aufrufe an einen NavController, und wenn Sie es irgendwie MÜSSEN, dann ist ein verzögertes Runnable möglicherweise Ihre einzige Möglichkeit, dies zu erreichen.

  • danke für den Hinweis. Ich habe einen Flow für Navigationsänderungen angehört und ihn mehrmals abonniert, als ich anfing, in Fragments onViewCreated (das mehrfach aufgerufen wird) zu lauschen.

    – cwiesner

    3. März um 10:48 Uhr

Erstens sollten Sie nicht bestehen requireView() wenn Sie versuchen, Ihren Nav-Controller abzurufen – navController = Navigation.findNavController(requireView()). Sie sollten die eigentliche Instanz des Navigationshostfragments übergeben.

Zweitens wird das Problem dadurch verursacht, dass Sie versuchen, einen Navigationspfad von B -> C aufzurufen, wenn Sie sich auf Fragment A befinden.

Ihr Richtungspfad ist von B -> C

val action = CategoryProductItemsDirections.actionCategoryProductItems2ToProductItem(null, it)

Aber Sie navigieren zuerst nach oben, sodass Sie sich jetzt tatsächlich auf Fragment A befinden, wenn Sie versuchen, die Navigation auszuführen:

navController?.navigateUp()
navController?.navigate(action)

  • Beim Umgang mit verschachtelten Fragmenten wie einem ViewPager können Sie leicht den Fehler machen, von einer verschachtelten Seite zu einem anderen Fragment zu navigieren, obwohl die Navigation tatsächlich vom ViewPager-Fragment zum Zielfragment erfolgen muss.

    – Jeffrey

    23. Juli 2021 um 0:47 Uhr

  • Interessanter Kommentar, kaufen Sie nicht sicher, welche Relevanz ein Ansichtspager für diesen Beitrag / diese Frage hat? Es wird nicht erwähnt, dass ein View-Pager verwendet wird

    – Indiana

    24. Juli 2021 um 5:53 Uhr

  • Ihre Antwort hat mir geholfen, das Problem zu lösen, das ich im Kommentar erwähnt habe. Das heißt, Sie versuchen, von einem Ziel aus zu navigieren, an dem Sie sich gerade nicht befinden.

    – Jeffrey

    25. Juli 2021 um 3:04 Uhr


Benutzeravatar von vishnu benny
vishnu benny

Ich habe eine Erweiterungsfunktion erstellt, um die Möglichkeit zu prüfen, eine Aktion vom aktuellen Ziel aus zu starten.

fun NavController.navigateSafe(@IdRes resId: Int, args: Bundle? = null) {
val destinationId = currentDestination?.getAction(resId)?.destinationId.orEmpty()
currentDestination?.let { node ->
    val currentNode = when (node) {
        is NavGraph -> node
        else -> node.parent
    }
    if (destinationId != 0) {
        currentNode?.findNode(destinationId)?.let { navigate(resId, args) }
    }
}}

Und die orEmpty() Teil ist Verlängerung vorbei Int? folgendermaßen:

fun Int?.orEmpty(default: Int = 0): Int {
    return this ?: default
}

  • val destinationId = currentDestination?.getAction(resId)?.destinationId.orEmpty() In dieser Zeile ist das letzte .orEmpty() rot. Ich weiß nicht warum. if (destinationId != EMPTY_INT) { currentNode?.findNode(destinationId)?.let { navigation(resId, args) } } und in dieser Bedingung ist EMPTY_INT rot, welcher Wert dort geprüft wird. führe mich

    – Syed Rafaqat Hussain

    13. August 2021 um 6:10 Uhr


  • @SyedRafaqatHussain sieht so aus, als wäre .orEmpty() eine Erweiterung, die den int-Wert auf destinationId anwendet. Sie können destinationId als nullfähige Eigenschaft ohne diesen Erweiterungsaufruf verwenden und einfach die Prüfung in if (destinationId != null) ändern.

    – Sviatoslav Zaitsev

    17. Februar um 22:09 Uhr

  • sehr gute Lösung.

    – Luis Alegria

    27. April um 19:49 Uhr

  • Aber wenn Sie eine ZielresId anstelle einer AktionsresId übergeben, funktioniert diese Logik nicht, weil currentDestination?.getAction(resId) gibt null zurück.

    – Shefchenko

    17. Mai um 15:02 Uhr

Benutzeravatar von Homayoon Ahmadi
Homayoon Ahmadi

Hier ist die Java-Version der NavigationUtils-Klasse für eine sichere Navigation:

public abstract class NavigationUtils {

    /**
     * This function will check navigation safety before starting navigation using direction
     *
     * @param navController NavController instance
     * @param direction     navigation operation
     */
    public static void navigateSafe(NavController navController, NavDirections direction) {
        NavDestination currentDestination = navController.getCurrentDestination();

        if (currentDestination != null) {
            NavAction navAction = currentDestination.getAction(direction.getActionId());

            if (navAction != null) {
                int destinationId = orEmpty(navAction.getDestinationId());

                NavGraph currentNode;
                if (currentDestination instanceof NavGraph)
                    currentNode = (NavGraph) currentDestination;
                else
                    currentNode = currentDestination.getParent();

                if (destinationId != 0 && currentNode != null && currentNode.findNode(destinationId) != null) {
                    navController.navigate(direction);
                }
            }
        }
    }


    /**
     * This function will check navigation safety before starting navigation using resId and args bundle
     *
     * @param navController NavController instance
     * @param resId         destination resource id
     * @param args          bundle args
     */
    public static void navigateSafe(NavController navController, @IdRes int resId, Bundle args) {
        NavDestination currentDestination = navController.getCurrentDestination();

        if (currentDestination != null) {
            NavAction navAction = currentDestination.getAction(resId);

            if (navAction != null) {
                int destinationId = orEmpty(navAction.getDestinationId());

                NavGraph currentNode;
                if (currentDestination instanceof NavGraph)
                    currentNode = (NavGraph) currentDestination;
                else
                    currentNode = currentDestination.getParent();

                if (destinationId != 0 && currentNode != null && currentNode.findNode(destinationId) != null) {
                    navController.navigate(resId, args);
                }
            }
        }
    }

    private static int orEmpty(Integer value) {
        return value == null ? 0 : value;
    }
}

Sie können diese Klasse wie folgt verwenden:

NavController navController = Navigation.findNavController(view);
NavigationUtils.navigateSafe(navController, R.id.action_firstFragment_to_secondFragment, null);

oder:

NavController navController = Navigation.findNavController(view);
NavDirections direction = FirstFragmentDirections.actionFirstFragmentToSecondFragment(yourModel, bundleArgs);
NavigationUtils.navigateSafe(navController, direction);

Diese Art von Fehler tritt meistens in einer Liste von Elementen auf und das Klicken auf ein Element löst die Navigation aus.

Ich habe mit diesem Code gelöst, Beim Elementklick vor dem Aufruf der Navigationsfunktion überprüfe ich, ob das aktuelle Ziel das beabsichtigte ist wie,

val currentDestinationIsHome = this.findNavController().currentDestination == this.findNavController().findDestination(R.id.nav_home)
val currentDestinationIsDetail = this.findNavController().currentDestination == this.findNavController().findDestination(R.id.nav_detail)

if(currentDestinationIsHome && !currentDestinationIsDetail){
    .... 
    // perform navigation
}

Dadurch wird sichergestellt, dass die Navigation nur durchgeführt wird, wenn sich die Ziele in einem legalen Zustand befinden. [No IllegalStateException … :)) ]

Benutzeravatar von Gobinath Nataraj
Gobinath Nataraj

//Überprüfen Sie, ob das aktuelle Fragment ein ereignisgesteuertes Fragment ist, indem Sie die ID übergeben

 fun Fragment.findNavControllerSafely(id: Int): NavController? {
        return if (findNavController().currentDestination?.id == id) {
            findNavController()
        } else {
            null
        }
    }

//In Fragment implementieren, wo Sie die Navigation aufrufen

findNavControllerSafely(R.id.fragment1)?.navigate(
                    R.id.action_fragment1_to_fragment2, bundle
                )

Benutzeravatar von Gk Mohammad Emon
Gk Mohammad Emon

In meinem Fall löse ich das Problem, indem ich ersetze –

 <action
    android:id="@+id/action_categoryProductItems2_to_productItem"
    app:destination="@id/productItem"
    app:enterAnim="@anim/enter_from_right"
    app:exitAnim="@anim/exit_to_right"
    app:popEnterAnim="@anim/fragment_open_enter"
    app:popExitAnim="@anim/fragment_fade_exit"/>

mit

 <action
    android:id="@+id/action_categoryProductItems2_to_productItem"
    app:destination="@id/productItem"
    app:enterAnim="@anim/enter_from_right"
    app:exitAnim="@anim/exit_to_right"
    app:popEnterAnim="@anim/fragment_open_enter"
    app:popExitAnim="@anim/fragment_fade_exit"
    app:popUpToInclusive="true" /* If true then also remove the destination from stack while popup */
    app:popUpTo="@id/navigation_home"/>  /*The fragment where to land again from destination*/
             

1436210cookie-checkNavigationskomponente Kotlin – vom aktuellen Ziel aus nicht auffindbar

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy