1

I'm trying to pass data between a fragment to another fragment trough the main_activity. While doing so, I can see that it works (thanks to Log.d(...)) but right after my value becomes null and I get an null pointer exception

my code MAIN_ACTIVITY:

class MainActivity : AppCompatActivity(), Communicator {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
        // Keyboard hidden when app start
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
    }

    override fun passDataComCategoryId(input: String) {
        val bundle = Bundle()
        bundle.putString("selectedCategoryId", input)

        val transaction = this.supportFragmentManager.beginTransaction()
        val frag2 = GameFragment()
        frag2.arguments = bundle
        transaction.replace(R.id.myNavHostFragment,frag2)
        transaction.commit()
    }

TITLEFRAGMENT (fragment 1):

class TitleFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?): View? {

        //variables
        lateinit var comm: Communicator

        // Inflate the layout for this fragment
        val binding: FragmentTitleBinding = DataBindingUtil.inflate( inflater, R.layout.fragment_title,
            container, false)

        binding.buttonStart.setOnClickListener {
            comm = activity as Communicator
            // +1 because position begins with 0
            comm.passDataCom((binding.spinnerQuizCategory.selectedItemPosition + 1).toString())
            view?.findNavController()?.navigate(R.id.action_titleFragment_to_gameFragment)
        }
        return binding.root
    }
}

GAMEFRAGMENT (fragment 2):

class GameFragment : Fragment() {

    /**
     * Lazily initialize our [GameViewModel].
     */
    private val viewModel: GameViewModel by lazy {
        ViewModelProvider(this).get(GameViewModel::class.java)
    }

    /**
     * Inflates the layout with Data Binding, sets its lifecycle owner to the GameViewModel
     * to enable Data Binding to observe LiveData, and sets up the RecyclerView with an adapter.
     */
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding = FragmentGameBinding.inflate(inflater)

        // Allows Data Binding to Observe LiveData with the lifecycle of this Fragment
        binding.lifecycleOwner = this

        // Giving the binding access to the GameViewModel
        binding.viewModel = viewModel

        //variables
        val red = Color.parseColor("#FF0000")
        val green = Color.parseColor("#008000")
        val white = Color.parseColor("#FFFFFF")
        var score = 0
        var lives = 3
        lateinit var comm: Communicator

        val selectedCategoryId: String? = arguments?.getString("selectedCategoryId")

        Log.d("idkk", selectedCategoryId.toString())

        viewModel.getRandomQuizQuestion(selectedCategoryId.toString())
            binding.submitButton.isEnabled = false

..........
}
}

When I log the value after the data is passed I see this (with null exception if I don't catch it):

2021-01-16 00:37:37.166 6565-6565/be.adembacaj.kuizuv2 D/idkk: 1
2021-01-16 00:37:37.313 6565-6565/be.adembacaj.kuizuv2 D/idkk: null

EDIT: Null pointer exception looks like this:

2021-01-16 01:13:36.921 7517-7517/be.adembacaj.kuizuv2 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: be.adembacaj.kuizuv2, PID: 7517
    java.lang.NullPointerException
        at be.adembacaj.kuizuv2.GameFragment.onCreateView(GameFragment.kt:51)
        at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
        at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
        at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
        at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
        at android.os.Handler.handleCallback(Handler.java:808)
        at android.os.Handler.dispatchMessage(Handler.java:101)
        at android.os.Looper.loop(Looper.java:166)
        at android.app.ActivityThread.main(ActivityThread.java:7529)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)

Can somebody enlighten me ?

demsdems
  • 27
  • 5
  • Please post the stacktrace of the NullPointerException – Zain Jan 16 '21 at 00:08
  • @Zain NullpointerException added – demsdems Jan 16 '21 at 00:17
  • The stack trace shows you that the error is happening on line 51 of GameFragment.kt, which you seem to have cut off above. But probably you are trying to use a synthetic view reference for a view that isn't in the current layout. – Tenfour04 Jan 16 '21 at 00:31
  • @Tenfour04 line 51 is: val selectedCategoryId: String? = arguments?.getString("selectedCategoryId") – demsdems Jan 16 '21 at 00:36
  • 1
    Seems that you use Nav Architecture components .. why you did the transaction manually then – Zain Jan 16 '21 at 00:45
  • I'm voting to reopen because the supposed duplicate only tangentially helps here. A Java NPE in pure Kotlin code is often caused by something happening under the hood of an API. – Tenfour04 Jan 16 '21 at 03:59

1 Answers1

2

If you are using a NavHostFragment, then you should never be doing a manual FragmentTransaction - delete your entire passDataComCategoryId method.

Instead, pass your Bundle through to navigate():

binding.buttonStart.setOnClickListener {
    val category = (binding.spinnerQuizCategory.selectedItemPosition + 1).toString())
    val bundle = Bundle()
    bundle.putString("selectedCategoryId", input)
    view?.findNavController()?.navigate(
        R.id.action_titleFragment_to_gameFragment,
        bundle)
}
ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • Note that Navigation has a feature called [Safe Args](https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args) which make this type safe so that you cannot navigate to your `GameFragment` without passing in required arguments. – ianhanniballake Jan 16 '21 at 01:27