0

On first app start my app is requesting some permissions when the user wants to start an action by hitting a button. After the user has granted the permissions the Fragment in my MainActivity should be replaced.

Everything is called (onCreate(), onStart(), onResume(),...) on my SecondFragment but it will not show up.

On the second app start, when there is no need to ask for the permissions again, everything works fine and the SecondFragment is displayed.

The same code is used in both ways:

/**
 * 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();
}

My Fragments are placed in a FrameLayout.

Update I quickly tried to build a dummy app to reproduce this error. Not exactly the same behavior but the dummy app crashes when replacing the Fragment after requesting permission. On second app start everthing works fine.

Here is some code.

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

        }
    }

FirstFragment

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

SecondFragment

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

Update

Error of dummy app:

FATAL EXCEPTION: main Process: com.example.permissiontest, PID: 15228 java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=12, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {com.example.permissiontest/com.example.permissiontest.MainActivity}: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.app.ActivityThread.deliverResults(ActivityThread.java:3699) at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) at android.app.ActivityThread.-wrap16(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1842) at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1860) at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:650) at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:609) at com.example.permissiontest.MainActivity.startSecondView(MainActivity.java:75) at com.example.permissiontest.MainActivity.onRequestPermissionsResult(MainActivity.java:40) at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6582) at android.app.Activity.dispatchActivityResult(Activity.java:6460) at android.app.ActivityThread.deliverResults(ActivityThread.java:3695) at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)  at android.app.ActivityThread.-wrap16(ActivityThread.java)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:148)  at android.app.ActivityThread.main(ActivityThread.java:5417)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Thomas Meinhart
  • 659
  • 2
  • 7
  • 28
  • Please share all fragment class. – Ahmad Aghazadeh Jul 26 '17 at 09:20
  • Thank you for your fast reply. Please see my updated question. – Thomas Meinhart Jul 26 '17 at 11:11
  • Provide the crash dump from logcat when using your revised app, it may help provide some insight as to what is going on. – Larry Schiefer Jul 26 '17 at 11:49
  • added the crash dump. – Thomas Meinhart Jul 26 '17 at 12:04
  • Using a second button to start SecondFragment after requesting permission also works. Looks like there is a problem when starting SecondFragment directly in onRequestPermissionResult – Thomas Meinhart Jul 26 '17 at 12:07
  • Right, it appears to be a state issue with committing a fragment transaction while processing the result. This is most likely because of lifecycle changes when the permission request is being sent and the result received. Some things you may consider: 1. Use the `Activity` to dictate the `Fragment` in use based on callbacks rather than one `Fragment` replacing itself with another, 2. Use MVP/MVC or MVVM to more cleanly separate "concerns" (similar to my #1), 3. If possible, refine your permission request to exact functionality rather than a blanket request – Larry Schiefer Jul 26 '17 at 12:13
  • Thank you! You show me the right direction. I understand now, why this happens and come up with this solution. https://stackoverflow.com/a/34522782/7454336 Acctually i don't like this solution, but for now it solved my problem. – Thomas Meinhart Jul 26 '17 at 14:24

0 Answers0