简体   繁体   中英

Fragment's custom listener is null when activity is destroyed on rotation

I have 3 fragments and one activity. These fragments define an interface for communicating a result to the activity.

This interface is initialized in onAttach() and set to null in onDetach(). However, when activity is destroyed and recreated on rotation, the interfaces are set to null and when they are invoked, it results in a NullPointerException.

Here is my code for the fragments and the activity.

MainActivity.java

package com.gianfranco.rsa.view;

import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import com.gianfranco.rsa.R;
import com.gianfranco.rsa.view.fragment.InputKeySizeFragment;
import com.gianfranco.rsa.view.fragment.InputPlainTextFragment;
import com.gianfranco.rsa.view.fragment.RSADetailsFragment;

public class MainActivity extends AppCompatActivity implements InputKeySizeFragment.OnKeySizeListener,
        InputPlainTextFragment.OnMessageListener, RSADetailsFragment.OnClickHomeListener {

    private InputKeySizeFragment inputKeySizeFragment;
    private InputPlainTextFragment inputPlainTextFragment;
    private RSADetailsFragment rsaDetailsFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            inputKeySizeFragment = new InputKeySizeFragment();
            inputPlainTextFragment = new InputPlainTextFragment();
            rsaDetailsFragment = new RSADetailsFragment();

            getSupportFragmentManager().beginTransaction()
                    .add(R.id.main_container, inputKeySizeFragment)
                    .addToBackStack(null)
                    .commit();
        }
    }

    @Override
    public void onKeySizeSelected(int keySize) {
        getSupportFragmentManager().beginTransaction()
                .add(R.id.main_container, inputPlainTextFragment)
                .addToBackStack(null)
                .commit();
    }

    @Override
    public void onMessageEntered(String message) {
        getSupportFragmentManager().beginTransaction()
                .add(R.id.main_container, rsaDetailsFragment)
                .addToBackStack(null)
                .commit();
    }

    @Override
    public void onClickHome() {
        getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

        getSupportFragmentManager().beginTransaction()
                .add(R.id.main_container, inputKeySizeFragment)
                .addToBackStack(null)
                .commit();
    }
}

InputKeySizeFragment.java

package com.gianfranco.rsa.view.fragment;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;

import com.gianfranco.rsa.R;

public class InputKeySizeFragment extends Fragment {

    private Spinner keySizeSpinner;
    private Button buttonNext;
    private ArrayAdapter<Integer> adapter;

    private OnKeySizeListener listener;

    @FunctionalInterface
    public interface OnKeySizeListener {
        void onKeySizeSelected(int keySize);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        if (context instanceof OnKeySizeListener) {
            listener = (OnKeySizeListener) context;
        }
    }

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

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        keySizeSpinner = view.findViewById(R.id.spinner_key_size);
        buttonNext = view.findViewById(R.id.btn_next);

        Integer[] bits = new Integer[]{64, 128, 256, 512, 1024, 2048};
        adapter = new ArrayAdapter<>(getActivity(), R.layout.support_simple_spinner_dropdown_item, bits);
        adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);
        keySizeSpinner.setAdapter(adapter);

        buttonNext.setOnClickListener(v -> {
            int keySize = (Integer) keySizeSpinner.getSelectedItem();
            listener.onKeySizeSelected(keySize);
        });
    }

    @Override
    public void onDestroyView() {
        keySizeSpinner = null;
        buttonNext = null;
        adapter = null;
        super.onDestroyView();
    }

    @Override
    public void onDetach() {
        listener = null;
        super.onDetach();
    }
}

InputPlainTextFragment.java

package com.gianfranco.rsa.view.fragment;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;

import com.gianfranco.rsa.R;

public class InputPlainTextFragment extends Fragment {

    private EditText editTextMessage;
    private Snackbar snackbar;
    private Button buttonNext;

    private OnMessageListener listener;

    @FunctionalInterface
    public interface OnMessageListener {
        void onMessageEntered(String message);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        if (context instanceof OnMessageListener) {
            listener = (OnMessageListener) context;
        }
    }

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

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        editTextMessage = view.findViewById(R.id.et_message);
        buttonNext = view.findViewById(R.id.btn_details);
        buttonNext.setOnClickListener(v -> listener.onMessageEntered(editTextMessage.getText().toString()));
    }

    @Override
    public void onDestroyView() {
        editTextMessage = null;
        buttonNext = null;
        super.onDestroyView();
    }

    @Override
    public void onDetach() {
        listener = null;
        super.onDetach();
    }
}

RSADetailsFragment.java

package com.gianfranco.rsa.view.fragment;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import com.gianfranco.rsa.R;

public class RSADetailsFragment extends Fragment {

    private TextView pValue;
    private TextView qValue;
    private TextView nValue;
    private TextView zValue;
    private TextView dValue;
    private TextView eValue;
    private TextView encryptedValue;
    private TextView decryptedValue;
    private Button buttonHome;

    private OnClickHomeListener listener;

    @FunctionalInterface
    public interface OnClickHomeListener {
        void onClickHome();
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        if (context instanceof OnClickHomeListener) {
            listener = (OnClickHomeListener) context;
        }
    }

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

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        pValue = view.findViewById(R.id.tv_pValue);
        qValue = view.findViewById(R.id.tv_qValue);
        nValue = view.findViewById(R.id.tv_nValue);
        zValue = view.findViewById(R.id.tv_zValue);
        dValue = view.findViewById(R.id.tv_dValue);
        eValue = view.findViewById(R.id.tv_eValue);
        encryptedValue = view.findViewById(R.id.tv_encrypted_value);
        decryptedValue = view.findViewById(R.id.tv_decrypted_value);
        buttonHome = view.findViewById(R.id.btn_home);

        buttonHome.setOnClickListener(v -> listener.onClickHome());
    }

    @Override
    public void onDestroyView() {
        pValue = null;
        qValue = null;
        nValue = null;
        zValue = null;
        dValue = null;
        eValue = null;
        encryptedValue = null;
        decryptedValue = null;
        buttonHome = null;
        super.onDestroyView();
    }

    @Override
    public void onDetach() {
        listener = null;
        super.onDetach();
    }
}

And this is the error message

03-10 00:03:16.243 31676-31676/com.gianfranco.rsa E/AndroidRuntime: FATAL EXCEPTION: main
                                                                    Process: com.gianfranco.rsa, PID: 31676
                                                                    java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
                                                                        at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:392)
                                                                        at android.support.v4.app.BackStackRecord.add(BackStackRecord.java:381)
                                                                        at com.gianfranco.rsa.view.MainActivity.onKeySizeSelected(MainActivity.java:39)
                                                                        at com.gianfranco.rsa.view.fragment.InputKeySizeFragment.lambda$onViewCreated$0$InputKeySizeFragment(InputKeySizeFragment.java:58)
                                                                        at com.gianfranco.rsa.view.fragment.InputKeySizeFragment$$Lambda$0.onClick(Unknown Source)
                                                                        at android.view.View.performClick(View.java:4819)
                                                                        at android.view.View$PerformClick.run(View.java:20152)
                                                                        at android.os.Handler.handleCallback(Handler.java:815)
                                                                        at android.os.Handler.dispatchMessage(Handler.java:104)
                                                                        at android.os.Looper.loop(Looper.java:194)
                                                                        at android.app.ActivityThread.main(ActivityThread.java:5562)
                                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                                        at java.lang.reflect.Method.invoke(Method.java:372)
                                                                        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:968)
                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:763)

Is there anything I'm missing or doing wrong? Please, let me know.

Add this line in your activity manifest

android:configChanges="keyboardHidden|orientation|screenSize"

 <activity android:name=".MainActivity"
           android:configChanges="keyboardHidden|orientation|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM