Android: диспетчер фрагментов становится нулевым

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

VerifyOTPDialog verifyOTPDialog = VerifyOTPDialog.newInstance("Kindly Enter the OTP sent to your Registered Mobile No. :", false, "Verify & Proceed",
                    "home_screen");
verifyOTPDialog.show(getFragmentManager(), VerifyOTPDialog.class.getSimpleName());

Приложение вылетает на 2 строчке. От crashlytics я получил следующую трассировку стека:

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.FragmentTransaction android.app.FragmentManager.beginTransaction()' on a null object reference
   at android.app.DialogFragment.show(DialogFragment.java:228)
   at in.droom.fragments.HomeScreenFragment.updateUI(HomeScreenFragment.java:553)
   at in.droom.fragments.HomeScreenFragment.onResponse(HomeScreenFragment.java:626)
   at in.droom.fragments.HomeScreenFragment.onResponse(HomeScreenFragment.java:100)
   at in.droom.util.DroomApi$1.onPostExecute(DroomApi.java:1136)
   at in.droom.util.DroomApi$1.onPostExecute(DroomApi.java:1094)
   at android.os.AsyncTask.finish(AsyncTask.java:636)
   at android.os.AsyncTask.access$500(AsyncTask.java:177)
   at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:653)
   at android.os.Handler.dispatchMessage(Handler.java:111)
   at android.os.Looper.loop(Looper.java:194)
   at android.app.ActivityThread.main(ActivityThread.java:5637)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:959)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754)

0. Сбой: основной: 0 0 0x00000000000000000

   at android.app.DialogFragment.show(DialogFragment.java:228)
   at in.droom.fragments.HomeScreenFragment.updateUI(HomeScreenFragment.java:553)
   at in.droom.fragments.HomeScreenFragment.onResponse(HomeScreenFragment.java:626)
   at in.droom.fragments.HomeScreenFragment.onResponse(HomeScreenFragment.java:100)
   at in.droom.util.DroomApi$1.onPostExecute(DroomApi.java:1136)
   at in.droom.util.DroomApi$1.onPostExecute(DroomApi.java:1094)
   at android.os.AsyncTask.finish(AsyncTask.java:636)
   at android.os.AsyncTask.access$500(AsyncTask.java:177)
   at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:653)
   at android.os.Handler.dispatchMessage(Handler.java:111)
   at android.os.Looper.loop(Looper.java:194)
   at android.app.ActivityThread.main(ActivityThread.java:5637)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:959)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754)

Насколько я понял, менеджер фрагментов в какой-то момент стал нулевым. Но я не могу воспроизвести сценарий. Сбои происходят в нет. пользователей почти каждый день.

Обновление: (VerifyOtpDialog)

public class VerifyOTPDialog extends DialogFragment implements OnClickListener {
private static final String TAG_NAME = VerifyOTPDialog.class.getSimpleName();
private Context ctx;
private Dialog dialog;
private boolean isLaterVisible;
private String strTitle, strBtnTitle, strFragment = "";

private BroadcastReceiver broadcastReceiver;
private ProfileAddressContactInfoModel userModel;
private VerifyOTPDialogDismissed verifyOTPDialogDismissed;
public static final String RECEIVE_OTP = "com.myapp.ACTION_RECEIVED";

private ImageView imgViewForClose;
private RobotoRegularEditTextView editTextForOTP;
private RobotoBoldTextView btnLater, btnVerifyAndProceed;
private RobotoLightTextView txtViewForTitle, txtViewForResendOTP;
public popFragmentListener mPopFragmentListener;

public VerifyOTPDialog() {

}

public static VerifyOTPDialog newInstance(String strTitle, boolean isLaterVisible, String strBtnTitle, String strFragment) {
    VerifyOTPDialog dialog = new VerifyOTPDialog();
    Bundle b = new Bundle();
    b.putString("title", strTitle);
    b.putBoolean("isLaterVisible", isLaterVisible);
    b.putString("btnTitle", strBtnTitle);
    b.putString("strFrom", strFragment);
    dialog.setArguments(b);
    return dialog;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ctx = getActivity();
    registerReceiver();
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    dialog = super.onCreateDialog(savedInstanceState);
    dialog.setOnKeyListener(new OnKeyListener() {
        @Override
        public boolean onKey(android.content.DialogInterface dialog, int keyCode, android.view.KeyEvent event) {
            if ((keyCode == android.view.KeyEvent.KEYCODE_BACK)) {
                AppUtil.hideKeyboard();
                return true;
            } else
                return false;
        }
    });

    return dialog;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.verify_otp_popup, container, false);
    Bundle b = getArguments();
    strTitle = b.getString("title");
    isLaterVisible = b.getBoolean("isLaterVisible");
    strBtnTitle = b.getString("btnTitle");
    strFragment = b.getString("strFrom");
    getDialog().setCanceledOnTouchOutside(false);
    getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
    getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));

    userModel = AppUtil.getUserProfile();

    imgViewForClose = (ImageView) view.findViewById(R.id.imgViewForClose);
    editTextForOTP = (RobotoRegularEditTextView) view.findViewById(R.id.editTextForOTP);
    btnLater = (RobotoBoldTextView) view.findViewById(R.id.btnLater);
    btnVerifyAndProceed = (RobotoBoldTextView) view.findViewById(R.id.btnVerifyAndProceed);
    txtViewForTitle = (RobotoLightTextView) view.findViewById(R.id.txtViewForTitle);
    txtViewForResendOTP = (RobotoLightTextView) view.findViewById(R.id.txtViewForResendOTP);

    if (userModel != null)
        txtViewForTitle.setText(strTitle + " +91 " + userModel.getContactInfo().getMobilePhone());

    txtViewForResendOTP.setText(getUnderlinedContent());

    if (!isLaterVisible)
        btnLater.setVisibility(View.GONE);

    btnVerifyAndProceed.setText(strBtnTitle);

    imgViewForClose.setOnClickListener(this);
    txtViewForResendOTP.setOnClickListener(this);
    btnLater.setOnClickListener(this);
    btnVerifyAndProceed.setOnClickListener(this);

    return view;
}

public interface VerifyOTPDialogDismissed {
    void dialogDismissing(String... s);
}

@Override
public void onDismiss(DialogInterface dialog) {
    super.onDismiss(dialog);
}

public VerifyOTPDialogDismissed getDialogDismissListener() {
    return verifyOTPDialogDismissed;
}

public void setDialogDismissListener(VerifyOTPDialogDismissed VerifyOTPDialogDismissed) {
    this.verifyOTPDialogDismissed = VerifyOTPDialogDismissed;
}

private void registerReceiver() {
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(RECEIVE_OTP);

    broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String strOTP = intent.getExtras().getString("otp");

            if (strOTP != null && editTextForOTP != null)
                editTextForOTP.setText(strOTP);
        }
    };

    ctx.registerReceiver(broadcastReceiver, intentFilter);
}

private SpannableString getUnderlinedContent() {
    SpannableString content = new SpannableString(getResources().getString(R.string.resend_otp));
    content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
    content.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.blue_button)), 0, content.length(), 0);
    return content;
}

private void sendOTP(HashMap<String, String> params) {
    Response.Listener<JSONObject> responseListener = new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            try {
                Logger.debugMessage("Response Object", response.toString());
                String responseCode = response.getString("code");

                if (responseCode.equalsIgnoreCase("success")) {
                    String message = response.getString("message");
                    Toast.makeText(ctx, message, Toast.LENGTH_SHORT).show();
                } else if (responseCode.equalsIgnoreCase("failed")) {
                    if (response.has("error")) {
                        if (response.has("error_code")) {
                            String error_code = response.optString("error_code");
                            displayMessageAlert(response.optString("error"), "", error_code);
                        } else {
                            displayMessageAlert(response.optString("error"), "", "");
                        }
                    } else if (response.has("errors")) {
                        JSONArray errorsArray;
                        try {
                            errorsArray = response.getJSONArray("errors");
                            Toast.makeText(ctx, errorsArray.getString(0), Toast.LENGTH_SHORT).show();
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    };

    Response.ErrorListener errorListener = new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {

        }
    };

    Api.sendOTP(params, responseListener, errorListener);
}

private void verifyOTP(HashMap<String, String> params, final String strFragment) {
    Response.Listener<JSONObject> responseListener = new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            try {
                Logger.debugMessage("Response Object", response.toString());
                String responseCode = response.getString("code");

                if (responseCode.equalsIgnoreCase("success")) {
                    if (userModel != null) {
                        userModel.setPhoneVerified(true);
                        userModel.setOTPVerified(true);
                        AppUtil.saveUserProfile(userModel);
                    }

                    VerifyOTPDialog.this.dismissAllowingStateLoss();
                    String event_name = "";
                    if (strFragment.equalsIgnoreCase("SellFragment")) {
                        event_name = "otp_verified_on_sell";
                        MainActivity.getInstance().pushFragment(SellFragment.newInstance(), SellFragment.class.getSimpleName(), true);
                    } else if (strFragment.equalsIgnoreCase("QuickSellFragment")) {
                        event_name = "otp_verified_on_quick_sell";
                        MainActivity.getInstance().pushFragment(new QuickSellFragment(), QuickSellFragment.class.getSimpleName(), true);
                    } else if (strFragment.equalsIgnoreCase("gotoPaymentFlow")) {
                        event_name = "otp_verified_on_payment";
                        DraftSummaryFragment.getInstance().gotoPaymentFlow();
                    } else if (strFragment.equalsIgnoreCase("gotoPlaceBidPage")) {
                        event_name = "otp_verified_on_place_bid";
                        verifyOTPDialogDismissed.dialogDismissing(strFragment);
                    } else if (strFragment.equalsIgnoreCase("make_best_offer")) {
                        event_name = "otp_verified_on_make_best_offer";
                        verifyOTPDialogDismissed.dialogDismissing(strFragment);
                    } else if (strFragment.equalsIgnoreCase("checkAvailableFees")) {
                        event_name = "otp_verified_on_quick_sell";
                        QuickSellDraftSummaryFragment.getInstance().checkAvailableFees();
                    } else if (strFragment.equalsIgnoreCase("home_screen")) {
                        event_name = "otp_verified_for_casual_seller";
                    } else if (strFragment.equalsIgnoreCase("my_profile")) {
                        event_name = "otp_verified_from_my_profile";
                    } else if (strFragment.equalsIgnoreCase("my_profile")) {
                        event_name = "otp_verified_from_my_profile";
                    } else if (strFragment.equalsIgnoreCase("pro_seller_profile_settings")) {
                        event_name = "otp_verified_from_pro_seller_profile_settings";
                    } else if (strFragment.equalsIgnoreCase("pro_seller_welcome")) {
                        event_name = "otp_verified_from_pro_seller_welcome";
                    } else if (strFragment.equalsIgnoreCase("seller_badges")) {
                        event_name = "otp_verified_from_seller_badges";
                    } else if (strFragment.equalsIgnoreCase("trust_factor")) {
                        event_name = "otp_verified_from_trust_factor";
                    } else if (strFragment.equalsIgnoreCase("ProSellerDashboard"))
                        MainActivity.getInstance().pushFragment(ProSellerDashboardFragment.newInstance(false), ProSellerDashboardFragment.class.getSimpleName(), true);
                    else
                        event_name = "otp_verified_from_my_account";

                    JSONObject post = BetaOutAPIs.getPostDataForUserAdd(userModel, "update");
                    JSONArray eventArray = new JSONArray();
                    JSONObject event = new JSONObject();
                    event.put("name", event_name);
                    event.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
                    eventArray.put(event);
                    post.put("events", eventArray);
                    BetaOutAPIs.sendEventToBetaOut(post, TAG_NAME);
                    BaseApplication.getInstance().trackMoEngageEvents(event_name, new PayloadBuilder().build());
                } else if (responseCode.equalsIgnoreCase("failed")) {
                    btnVerifyAndProceed.setEnabled(true);
                    if (response.has("error")) {
                        if (response.has("error_code")) {
                            String error_code = response.optString("error_code");
                            displayMessageAlert(response.optString("error"), "", error_code);
                        } else {
                            displayMessageAlert(response.optString("error"), "", "");
                        }
                    } else if (response.has("errors")) {
                        Toast.makeText(ctx, response.optString("errors"), Toast.LENGTH_SHORT).show();
                    }
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    };

    Response.ErrorListener errorListener = new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            error.printStackTrace();
        }
    };

    Api.verifyOTP(params, responseListener, errorListener);
}

private boolean validateOTP() {
    String validationMessage = "";
    boolean isValid = true;
    String email = editTextForOTP.getText().toString();

    if (email.length() == 0) {
        isValid = false;
        validationMessage = "Please Enter OTP";
        Toast.makeText(getActivity(), validationMessage, Toast.LENGTH_SHORT).show();
    }

    return isValid;
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.imgViewForClose:
            VerifyOTPDialog.this.dismissAllowingStateLoss();
            break;

        case R.id.txtViewForResendOTP:
            if (userModel != null) {
                HashMap<String, String> params = new HashMap<String, String>();
                params.put("user_id", AppSharedPref.getUserId());
                params.put("phone", userModel.getContactInfo().getMobilePhone());
                sendOTP(params);
            }
            break;

        case R.id.btnLater:
            VerifyOTPDialog.this.dismissAllowingStateLoss();

            if (mPopFragmentListener != null)
                mPopFragmentListener.gotoRootFragment();
            if (strFragment.equalsIgnoreCase("SellFragment")) {
                       MainActivity.getInstance().pushFragment(SellFragment.newInstance(), SellFragment.class.getSimpleName(), true);
            } else if (strFragment.equalsIgnoreCase("QuickSellFragment")) {
                MainActivity.getInstance().pushFragment(new QuickSellFragment(), QuickSellFragment.class.getSimpleName(), true);
            } else if (strFragment.equalsIgnoreCase("gotoPlaceBidPage")) {
                //verifyOTPDialogDismissed.dialogDismissing();
            } else if (strFragment.equalsIgnoreCase("ProSellerDashboard"))
                         MainActivity.getInstance().pushFragment(ProSellerDashboardFragment.newInstance(false), ProSellerDashboardFragment.class.getSimpleName(), true);
            break;

        case R.id.btnVerifyAndProceed:
            if (validateOTP()) {
                btnVerifyAndProceed.setEnabled(false);
                HashMap<String, String> map = new HashMap<String, String>();
                map.put("user_id", SharedPref.getUserId());
                map.put("code", editTextForOTP.getText().toString());
                verifyOTP(map, strFragment);
            }
            break;

        default:
            break;
    }
}

protected void displayMessageAlert(String message, String title, final String error_code) {
    String s = "Alert";
    if (title != null && title.length() > 0) {
        s = title;
    }

    if (message != null) {
        new AlertDialog.Builder(ctx).setTitle(s).setMessage(message)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        if (error_code.toLowerCase().equals("logout")) {
                            AppUtil.logoutUser((MainActivity) ctx);
                            MainActivity.getInstance().pushFragment(LoginFragment.newInstance(true, true), LoginFragment.class.getSimpleName(), true);
                        }
                        dialog.dismiss();
                    }
                }).show();
    }
}

@Override
public void onDestroy() {
    super.onDestroy();

    if (broadcastReceiver != null) {
        ctx.unregisterReceiver(broadcastReceiver);
    }
}

public void setPopFragmentListener(popFragmentListener mPopFragmentListener) {
    this.mPopFragmentListener = mPopFragmentListener;
}

public interface popFragmentListener {
    void gotoRootFragment();
}
}

person Nitish    schedule 25.07.2016    source источник
comment
не могли бы вы разместить свой VerifyOTPDialog здесь   -  person Vishal Patoliya ツ    schedule 25.07.2016
comment
Вы звоните show() во фрагменте?   -  person Akshay Bhat 'AB'    schedule 25.07.2016
comment
Вы вызываете show внутри асинхронной задачи?   -  person Kirill    schedule 25.07.2016
comment
@VishalPatoliya Опубликовано VerifyOTPDialog. Пожалуйста, посмотрите   -  person Nitish    schedule 26.07.2016
comment
@ g4s8 g4s8 Я не вызываю шоу в асинхронной задаче   -  person Nitish    schedule 26.07.2016
comment
@AkshayBhat Да, я вызываю показ диалогового окна из фрагмента   -  person Nitish    schedule 26.07.2016
comment
Вы используете v4 или appFragment??   -  person Vishal Patoliya ツ    schedule 26.07.2016
comment
@VishalPatoliya Я использую android.app.Fragment   -  person Nitish    schedule 26.07.2016
comment
Можете ли вы попытаться удалить этот общедоступный диалог @Override onCreateDialog (Bundle saveInstanceState)   -  person Vishal Patoliya ツ    schedule 01.08.2016
comment
Хорошо, я попробую и сообщу вам о результате. Самая большая проблема в том, что мы не можем понять, когда это происходит.   -  person Nitish    schedule 01.08.2016


Ответы (4)


Вы должны получить диспетчер фрагментов до:

FragmentManager fragMan = getSupportFragmentManager();

затем проверьте его недействительность

if(fragMan != null){
    // Do stuff
}

И если вы используете if в DoInBackground асинхронной задачи, вы должны получить Uithread :

runOnUiThread(new Runnable() {
    public void run() {
        // do stuff on the ui like display fragments
    }
});

РЕДАКТИРОВАТЬ :

Вы должны использовать библиотеку поддержки v4 для поддержки фрагмента

person An-droid    schedule 25.07.2016

Если вы показываете из фрагмента, используйте getChildFragmentManager() :

Нравиться :

verifyOTPDialog.show(getChildFragmentManager(),VerifyOTPDialog.class.getSimpleName());
person Akshay Bhat 'AB'    schedule 26.07.2016
comment
Но это доступно в API 17. Также я использую android.app.fragment. - person Nitish; 26.07.2016
comment
Затем вы должны использовать android.support.v4.app.dialogFragment для DialogFragment, а для фрагмента использовать android.support.v4.app.Fragment. Вы должны расширить FragmentActivity или AppCompatActivity в своей деятельности. Таким образом, вы можете получить getchildFragmentManager() во фрагменте ниже API 17. - person Akshay Bhat 'AB'; 26.07.2016

Вы сказали, что используете android.app.fragment для размещения своего фрагмента. Поскольку вы создаете диалог с помощью Android DialogFragment, сам диалог Fragment является фрагментом, что делает это случаем вложенных фрагментов в android. Когда мы вкладываем фрагменты, мы используем getChildFragmentManager().

Поддержка вложенных фрагментов в Android была недоступна в API 11. Поддержка вложенных фрагментов началась с API 17 и выше. Если вы хотите использовать вложенные фрагменты в API 11–16, вам необходимо использовать фрагменты библиотеки поддержки.

Итак, вы должны сделать свой DialogFragment типа android.support.v4.app.dialogFragment и ваш фрагмент типа android.support.v4.app.fragment

person Nick Asher    schedule 03.08.2016

Я бы рекомендовал вызывать DialogFragment из родительского действия, чтобы не возникало проблем с жизненным циклом фрагмента. Поскольку вы, вероятно, пытаетесь заставить фрагменты общаться друг с другом, это следует делать через Activity.

Вот дополнительная информация — https://developer.android.com/training/basics/fragments/communicating.html

Следуя шагам:

  1. Определить интерфейс
  2. Реализовать интерфейс
  3. Доставить сообщение во фрагмент

На последнем шаге вы можете вызвать DialogFragment, а затем заставить Dialog вернуть данные обратно в действие, а затем передать их вызывающему фрагменту.

person Ray Hunter    schedule 05.08.2016