Почему моя переменная равна нулю, когда я ее установил?

Прежде чем объяснять, я покажу вам свой (укороченный) код:

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[] { "[email protected]" });
                                    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..."));
                                }
                            }
                        });
                    
                }
            });

    }
}

Второй фрагмент:

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);
        }
    }

Я пытаюсь установить текст editText фрагмента из MainActivity. Для этого я написал в SecondFragment метод, который должен изменить текст этого editText. И этот метод вызывается в MainActivity.

Почему объект «комната» нулевой, если вкладка была выбрана заранее, а элементы SecondFragment фактически уже должны быть созданы? Я не понимаю это.

Я уже разобрался с этим: Что такое NullPointerException и как его исправить?

Но мне это не помогает.

Я видел, что многие пользователи голосуют минус баллы без причины. Поэтому я надеюсь, что этот вопрос приемлем.

@aran Ну, название слабое, не конкретное. Не уверен, что он заслуживает отрицательного голосования, но это, безусловно, плохой вопрос.

Basil Bourque 15.12.2020 01:18

Переименовано, так что это не молоток.

chrylis -cautiouslyoptimistic- 15.12.2020 01:34

Пожалуйста, опишите проблему, прежде чем создавать стену кода.

NomadMaker 15.12.2020 01:57
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
3
124
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Переменной room присваивается значение при вызове onCreateView(); Это должно произойти после вызова ft.commit() внутри вашего onTabSelected метода.

Но поведение commit по умолчанию асинхронно, поэтому:

FragmentTransaction не применяется сразу после вызова commit()

Основной поток не будет ждать, он просто продолжит выполнение следующих строк кода, независимо от того, было ли commit() завершено или нет (или даже не запущено).

В результате onCreateView() from SecondFragment не был выполнен до вашего вызова основного потока: room все еще не был инициализирован, когда вы попытались вызвать setText() на нем.

Чтобы этого избежать, принудительно выполните обновление, вызвав executePendingTransactions:

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

Немного информации из документации:

executePendingTransactions --- После фиксации FragmentTransaction с помощью FragmentTransaction#commit она должна быть выполнена асинхронно в основном потоке процесса. Если вы хотите немедленно выполнить любые ожидающие операции, вы можете вызвать это функция.


В резюме

executePendingTransactions предлагает commit что-то вроде «синхронного» характера. Это заставит процесс немедленно выполнить все задачи, запланированные на FragmentTransaction.

Таким образом, вы гарантируете, что onCreateView() будет правильно выполнен (и room инициализирован) к тому времени, когда вы вызовете s.roomText(), избегая сбивающего вас с толку NullPointerException.

Не лучше ли использовать обратный вызов?

chrylis -cautiouslyoptimistic- 15.12.2020 01:46

Я не понимаю вашей точки зрения. Обратные вызовы — это просто фрагменты кода, которые могут выполняться как синхронно, так и асинхронно. Проблема здесь в синхронизации. Кроме того, я действительно не понимаю смысла реализации синхронного обратного вызова, когда вам просто нужно вызвать метод, чтобы добиться того же поведения.

aran 15.12.2020 02:24

Весь смысл обратного вызова в том, что он происходит после того, как произошло что-то еще, в данном случае завершение коммита.

chrylis -cautiouslyoptimistic- 15.12.2020 02:27

На самом деле обратный вызов будет вызываться после коммита. Сразу после: дело в том, что коммит является асинхронным, поэтому он может быть не завершен или даже не запущен к моменту вызова обратного вызова. который находится сразу после строки commit(). У вас будет та же проблема, что и у ОП.

aran 15.12.2020 02:30

Произошло что-то еще, не означает, что что-то еще было выполнено и/или завершено. На самом деле roomText был вызван после того, как произошла фиксация. Но коммит так и не был выполнен/завершен.

aran 15.12.2020 02:36

@aran Спасибо за объяснение. Я думал, что что-то подобное происходит в этом направлении... Но я думаю, что никогда бы не узнал об этом.

Aaron 15.12.2020 11:10

Детали @Aaron являются ключом к коду. Рад помочь :)

aran 15.12.2020 11:17

Другие вопросы по теме