Action is unknown to this NavController

Aleksey Kabanov picture Aleksey Kabanov · Jul 13, 2018 · Viewed 20.5k times · Source

I'm providing data transfer between fragments in two different ways, first is working normal, while a safe data type cause run-time crash. I'm using that Android docs: https://developer.android.com/topic/libraries/architecture/navigation/navigation-implementing#Safe-args

Don't understand how could I resolve the problem. Thanks for your help

07-13 11:40:07.986 8119-8119/com.flexdecision.ak_lex.navigationsimple E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.flexdecision.ak_lex.navigationsimple, PID: 8119
java.lang.IllegalArgumentException: navigation destination com.flexdecision.ak_lex.navigationsimple:id/transferAction is unknown to this NavController
    at androidx.navigation.NavController.navigate(NavController.java:669)
    at androidx.navigation.NavController.navigate(NavController.java:628)
    at androidx.navigation.NavController.navigate(NavController.java:690)
    at com.flexdecision.ak_lex.navigationsimple.InitialFragment.makeTransfer(InitialFragment.java:58)
    at com.flexdecision.ak_lex.navigationsimple.InitialFragment.lambda$onViewCreated$0(InitialFragment.java:47)
    at com.flexdecision.ak_lex.navigationsimple.-$$Lambda$InitialFragment$shEoLbIe0sVhbTcJ2Al_FvBuU7g.onClick(lambda)
    at android.view.View.performClick(View.java:5198)
    at android.view.View$PerformClick.run(View.java:21147)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Navigation graph:

<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
    app:startDestination="@id/initialFragment">

    <fragment
        android:id="@+id/initialFragment"
        android:name="com.flexdecision.ak_lex.navigationsimple.InitialFragment"
        android:label="fragment_initial"
        tools:layout="@layout/fragment_initial">
        <action
            android:id="@+id/transferAction"
            app:destination="@+id/nextFragment" />
    </fragment>
    <fragment
        android:id="@+id/nextFragment"
        android:name="com.flexdecision.ak_lex.navigationsimple.NextFragment"
        android:label="fragment_next"
        tools:layout="@layout/fragment_next" >
        <argument
            android:name="firstName"
            android:defaultValue="none"
            app:type="string" />
        <argument
            android:name="lastName"
            android:defaultValue="none"
            app:type="string" />
    </fragment>
</navigation>

Activity layout:

<android.support.constraint.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">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />
</android.support.constraint.ConstraintLayout>

Sender:

import androidx.navigation.Navigation;

public class InitialFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_initial, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Button transactionBtn = view.findViewById(R.id.transaction);
        transactionBtn.setOnClickListener(v -> makeTransfer(v));
    }

    private void makeTransfer(View view) {
        Bundle bundle = new Bundle();
        bundle.putString("name", "Aleksey");
        Navigation.findNavController(view).navigate(R.id.transferAction, bundle);

        //Type safe passing data
        InitialFragmentDirections.TransferAction action = InitialFragmentDirections.transferAction();
        action.setLastName("Petrov");
        Navigation.findNavController(view).navigate(action);
    }
}

Receiver:

public class NextFragment extends Fragment {
    private static final String ARG_PARAM1 = "name";

    private String mParam1;
    private TextView firstName;

    public NextFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            Log.d("Next", "Param: " + mParam1);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_next, container, false);
    }


    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        firstName = view.findViewById(R.id.firstName);
        TextView lastName = view.findViewById(R.id.lastNameTV);
        firstName.setText(mParam1);

        String ln = NextFragmentArgs.fromBundle(getArguments()).getLastName();
        lastName.setText(ln);
    }
}

additional:

Answer

Alex picture Alex · Jul 13, 2018

You calling twice to 'Navigation.findNavController(view).navigate':

private void makeTransfer(View view) {
    Bundle bundle = new Bundle();
    bundle.putString("name", "Aleksey");
    Navigation.findNavController(view).navigate(R.id.transferAction, bundle);

    //Type safe passing data 
    InitialFragmentDirections.TransferAction action = InitialFragmentDirections.transferAction();
    action.setLastName("Petrov");
    Navigation.findNavController(view).navigate(action);
} 

First time with bundle and second time with safe args, but after the first call your destination already changed to 'nextFragment', and when you call second 'navigate' the 'NavController' looking for 'transferAction' action inside 'nextFragment' and throws exception.