Фрагмент не отображается после запроса разрешения

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

Все называется (onCreate(), onStart(), onResume(),...) в моем SecondFragment, но не отображается.

При втором запуске приложения, когда нет необходимости снова запрашивать разрешения, все работает нормально, и отображается SecondFragment.

Один и тот же код используется в обоих случаях:

/**
 * This method starts the second fragment.
 */
private void startSecondView(){

    final SecondFragment frag = new SecondFragment();
    final FragmentTransaction ft =  getFragmentManager().beginTransaction();
    ft.replace(R.id.container, frag);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.addToBackStack(null);
    ft.commit();
}

Мои фрагменты помещаются в FrameLayout.

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

Вот некоторый код.

activity_main.xml

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

<RelativeLayout
        android:layout_height="match_parent"
        android:layout_width="match_parent">

    <!-- The main content view -->
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:id="@+id/container"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 tools:ignore="MergeRootFrame"/>
</RelativeLayout>

<!-- The navigation drawer -->
<android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:layout_gravity="start"
        app:headerLayout="@layout/navigation_header"
        app:menu="@menu/navigation_menu">

    <android.support.design.widget.NavigationView
            android:id="@+id/navigation_drawer_bottom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            app:elevation="0dp"
            app:menu="@menu/menu_navigation_dawer_bottom"/>

</android.support.design.widget.NavigationView>

MainActivity

   public class MainActivity extends AppCompatActivity {

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

        @Override
        protected void onResume(){
            super.onResume();

            final FirstFragment frag = new FirstFragment();
            frag.setRetainInstance(true);
            getSupportFragmentManager().beginTransaction()
                        .add(R.id.container, frag)
                        .commit();

        }
    }

Первый фрагмент

public class FirstFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    private static final int PERMISSIONS_REQUEST = 12;

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    public FirstFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment FirstFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static FirstFragment newInstance(String param1, String param2) {
        FirstFragment fragment = new FirstFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.fragment_first, container, false);

        Button button = (Button) rootView.findViewById(R.id.btnStartTour);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                checkRequirementsAndStart();
            }
        });

        // Inflate the layout for this fragment
        return rootView;
    }

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

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

    private void checkRequirementsAndStart(){
        final String[] checkPermissions = new String[]{
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.SEND_SMS,
                Manifest.permission.CALL_PHONE
        };


        if(hasPermissions(getActivity(), checkPermissions)) {
            startSecondView();
        } else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
            requestPermissions(checkPermissions, PERMISSIONS_REQUEST);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == PERMISSIONS_REQUEST) {
            for(int x = 0; x < permissions.length; x++){
                if(grantResults[x] != PackageManager.PERMISSION_GRANTED){
                    return; //Abort if permission is missing
                }
            }
            startSecondView();
        }
    }

    private void startSecondView(){
        final SecondFragment frag = new SecondFragment();
        final FragmentTransaction ft =  getFragmentManager().beginTransaction();
        ft.replace(R.id.container, frag);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.addToBackStack(null);
        ft.commit();
    }

    public boolean hasPermissions(Context context, String... permissions) {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
            for (String permission : permissions) {
                if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
        }
        return true;
    }
}

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

public class SecondFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    public SecondFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment SecondFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static SecondFragment newInstance(String param1, String param2) {
        SecondFragment fragment = new SecondFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_second, container, false);
    }

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

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

Обновить

Ошибка фиктивного приложения:

Пожалуйста, поделитесь всем классом фрагментов.


person Thomas Meinhart    schedule 26.07.2017    source источник
comment
Спасибо за быстрый ответ. Пожалуйста, смотрите мой обновленный вопрос.   -  person Ahmad Aghazadeh    schedule 26.07.2017
comment
Предоставьте аварийный дамп из logcat при использовании вашего исправленного приложения, это может помочь дать некоторое представление о том, что происходит.   -  person Thomas Meinhart    schedule 26.07.2017
comment
добавлен аварийный дамп.   -  person Larry Schiefer    schedule 26.07.2017
comment
Использование второй кнопки для запуска SecondFragment после запроса разрешения также работает. Похоже, есть проблема при запуске SecondFragment непосредственно в onRequestPermissionResult.   -  person Thomas Meinhart    schedule 26.07.2017
comment
Верно, похоже, это проблема с состоянием при фиксации транзакции фрагмента при обработке результата. Скорее всего, это связано с изменениями жизненного цикла при отправке запроса на разрешение и получении результата. Некоторые вещи, которые вы можете рассмотреть: 1. Используйте _1_ для указания используемого _2_ на основе обратных вызовов, а не одного _3_, заменяющего себя другим, 2. Используйте MVP/MVC или MVVM для более четкого разделения проблем (аналогично моему # 1), 3. Если возможно, уточните свой запрос на разрешение до точной функциональности, а не для общего запроса.   -  person Thomas Meinhart    schedule 26.07.2017
comment
Благодарю вас! Вы указываете мне правильное направление. Теперь я понимаю, почему это происходит, и придумываю это решение. stackoverflow.com/a/34522782/7454336 На самом деле мне не нравится это решение, но пока оно решило мою проблему.   -  person Larry Schiefer    schedule 26.07.2017
comment
НЕИСПРАВНОЕ ИСКЛЮЧЕНИЕ: основной процесс: com.example.permissiontest, PID: 15228 java.lang.RuntimeException: Ошибка доставки результата ResultInfo{who=@android:requestPermissions:, request=12, result=-1, data=Intent {act=android .content.pm.action.REQUEST_PERMISSIONS (есть дополнения) }} к действию {com.example.permissiontest/com.example.permissiontest.MainActivity}: java.lang.IllegalStateException: невозможно выполнить это действие после onSaveInstanceState в android.app. ActivityThread.deliverResults(ActivityThread.java:3699) в android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) в android.app.ActivityThread.-wrap16(ActivityThread.java) в android.app.ActivityThread$H.handleMessage( ActivityThread.java:1393) в android.os.Handler.dispatchMessage(Handler.java:102) в android.os.Looper.loop(Looper .java:148) в android.app.ActivityThread.main(ActivityThread.java:5417) в java.lang.reflect.Method.invoke(собственный метод) в com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit .java:726) в com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Причина: java.lang.IllegalStateException: невозможно выполнить это действие после onSaveInstanceState в android.support.v4.app.FragmentManagerImpl .checkStateLoss(FragmentManager.java:1842) в android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1860) в android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:650) в android. support.v4.app.BackStackRecord.commit(BackStackRecord.java:609) в com.example.permissiontest.MainActivity.startSecondView(Main Activity.java:75) в com.example.permissiontest.MainActivity.onRequestPermissionsResult(MainActivity.java:40) в android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6582) в android.app.Activity.dispatchActivityResult(Activity.java :6460) в android.app.ActivityThread.deliverResults(ActivityThread.java:3695) в android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)  в android.app. ActivityThread.-wrap16(ActivityThread.java) на android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) на android.os.Handler.dispatchMessage(Handler.java:102) на android.os.Looper.loop( Looper.java:148) в android.app.ActivityThread.main(ActivityThread.java:5417) в java.lang.reflect.Method.invoke(собственный метод) в com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run( ZygoteInit.java:726) на com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)   -  person Thomas Meinhart    schedule 26.07.2017