0

Before explaining, I will show you my (shorted) code:

public class MainActivity extends AppCompatActivity 
{
    FrameLayout simpleFrameLayout;
    TabLayout tabLayout;
    SecondFragment s = new SecondFragment();
    
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        simpleFrameLayout = (FrameLayout) findViewById(R.id.simpleFrameLayout);
        tabLayout = (TabLayout) findViewById(R.id.simpleTabLayout);
// Create a new Tab named "First"
        TabLayout.Tab firstTab = tabLayout.newTab();
        firstTab.setText("First"); // set the Text for the first Tab
    
// first tab
        tabLayout.addTab(firstTab); // add  the tab at in the TabLayout
// Create a new Tab named "Second"
        TabLayout.Tab secondTab = tabLayout.newTab();
        secondTab.setText("Second"); // set the Text for the second Tab
        
        tabLayout.addTab(secondTab); // add  the tab  in the TabLayout

        Fragment fragment = null;
        fragment = new FirstFragment();
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.replace(R.id.simpleFrameLayout, fragment);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        ft.commit();
        
        tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
// get the current selected tab's position and replace the fragment accordingly
                    Fragment fragment = null;
                    switch (tab.getPosition()) {
                        case 0:
                            fragment = new FirstFragment();
                            myMenu.findItem(R.id.telefono).setVisible(false);
                            break;
                        case 1:
                            fragment = s;
                            myMenu.findItem(R.id.telefono).setVisible(true);
                            break;
                    }
                    FragmentManager fm = getSupportFragmentManager();
                    FragmentTransaction ft = fm.beginTransaction();
                    ft.replace(R.id.simpleFrameLayout, fragment);
                    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                    ft.commit();
                }
                @Override
                public void onTabUnselected(TabLayout.Tab tab) {

                }

                @Override
                public void onTabReselected(TabLayout.Tab tab) {

                }
            });
    }
    
    public void conectar(String nombre, String ip, int puerto) {
        //Code...
        Executor executor = Executors.newSingleThreadExecutor();
        executor.execute(new Runnable() {
                @Override
                public void run() {
                    Looper.prepare();
                    runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                try{
                                    //Code...
                                    try {
                                        //Code
                                        TabLayout.Tab tab = tabLayout.getTabAt(1);
                                        tab.select();
                                        s.roomText(param1);
                                        
                                    }
                                    catch(SocketTimeoutException ex)
                                    {

                                    }
                                }
                                catch(Exception e){
                                    StringWriter sw = new StringWriter();
                                    e.printStackTrace(new PrintWriter(sw));
                                    String stacktrace = sw.toString();
                                    // create an email intent to send to yourself
                                    final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
                                    emailIntent.setType("plain/text");
                                    emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { "lets.think.android@gmail.com" });
                                    emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "App Error Report");
                                    emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, stacktrace);
                                    // start the email activity - note you need to start it with a chooser
                                    startActivity(Intent.createChooser(emailIntent, "Send error report..."));
                                }
                            }
                        });
                    
                }
            });

    }
}

SecondFragment:

package com.mycompany.myapp;
    
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.*;
    
    public class SecondFragment extends Fragment
    {
        EditText chat, room;
        
        public SecondFragment() {
    // Required empty public constructor
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
    // Inflate the layout for this fragment
            View view = inflater.inflate(R.layout.fragment_second, container, false);
            room = (EditText) view.findViewById(R.id.mainEditText1);
            chat = (EditText) view.findViewById(R.id.chatEditText1);
            return view;
        }
        
        public void roomText(String t){
            room.setText(t);
        }
    }

I am trying to set the text of an editText of a fragment from the MainActivity. For that I wrote a method in SecondFragment that should change the text of that editText. And this method is called in the MainActivity.

Why is the "room" object null if the tab was selected beforehand and the elements of SecondFragment should actually already be created? I do not understand that.

I already road this: What is a NullPointerException, and how do I fix it?

But it doesn't help me.

I've seen that many users are voting minus points without reason. So I hope this question is acceptable.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
Aaron
  • 55
  • 5

1 Answers1

0

The room variable is assigned a value when onCreateView() is called; This should happen after calling ft.commit() inside your onTabSelected method.

But the default behaviour of commit is asynchronous, so:

FragmentTransaction isn't applied instantly after calling commit()

The main thread won't wait, it will just continue executing the next code lines regardless commit() was finished or not (or not even started).

As a result, onCreateView() from SecondFragment was not executed before your main thread call: room was still not initialized when you tried to call setText() on it.

In order to avoid this, force the update by calling executePendingTransactions:

//....
ft.replace(R.id.simpleFrameLayout, fragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
ft.executePendingTransactions();

Some info from the docs:

executePendingTransactions --- After a FragmentTransaction is committed with FragmentTransaction#commit, it is scheduled to be executed asynchronously on the process's main thread. If you want to immediately execute any pending operations, you can call this function.


In resume

executePendingTransactions offers commit something like a "synchronous" nature. It will force the process to immediately execute all tasks scheduled on the FragmentTransaction.

This way, you'll guarantee that onCreateView() has been properly executed (and room initialized) by the time you call s.roomText(), avoiding the NullPointerException that is confusing you.

aran
  • 10,978
  • 5
  • 39
  • 69
  • Wouldn't it be better to use a callback? – chrylis -cautiouslyoptimistic- Dec 15 '20 at 00:46
  • I don't get your point. Callbacks are just pieces of code that can be executed both synchronously/asynchronously. The issue here is within synchronization. Also, I don't really get the point of implementing a synchronous callback when you just have to call a method in order to achieve the same behaviour. – aran Dec 15 '20 at 01:24
  • The entire point of a callback is that it happens _after something else has happened_, in this case the completion of the commit. – chrylis -cautiouslyoptimistic- Dec 15 '20 at 01:27
  • The callback would in fact be called after the commit. Immediately after: the point is that commit is asynchronous, so it may not be finished or even started by the time the callback is invoked. which is instantly after the commit() line. You'd have the same issue as the OP. – aran Dec 15 '20 at 01:30
  • Something else happened doesn't mean something else was executed and/or finished. The roomText was in fact called after the commit happened. But the commit was never executed/finished. – aran Dec 15 '20 at 01:36
  • 1
    @aran Thank you for your explanation. I thinked something like that was going in that direction... But I think I would never have found it out. – Aaron Dec 15 '20 at 10:10
  • @Aaron details are the key regarding code. Glad to help :) – aran Dec 15 '20 at 10:17