20

I would like to use permission for android 6 in my app but i saw a strange event. Perhaps you can help me about that.

If you start your app with "Dangerous" permissions, these permissions appears in the "App permissions" of your android device. PERFECT!

BUT if you keep your app in background, go to the "App permissions" menu, disable (you can enable and then disable it) a permission and go back to your app, android never go to onStart (Fragment or activity) ?! And never go there again.

If you don't touch to the permission or if you enable a permission => it go to onStart and others.

That's problematic, for exemple, if your app use listeners, you can't restart it and you can have a crash...

Do you know the method where Android go when you disable a permission ?

I tried onCreate onStart onResume onRestart onRestoreInstanceState onActionModeStarted onRestoreInstanceState onPostResume onContentChanged

... but no way... :/

acs-team
  • 602
  • 6
  • 14
  • 4
    "android never go to onStart (Fragment or activity) ?! And never go there again." -- Android terminates your process if the user revokes a permission from Settings. You handle this the same way if Android terminates your process for any other reason while you are in the background. – CommonsWare Oct 07 '15 at 12:24
  • Thanks for your answer ! I continue the investigation. When i create my activity, i show 1 fragment. If i change the permission and go back the app, there are 2 fragments (the fragment before the permission change and after). Do you have any idea why the old fragment is kept ? – acs-team Oct 07 '15 at 13:04
  • "Do you have any idea why the old fragment is kept ?" -- because your task is still outstanding, and so Android is re-creating the activity and restoring its state as best it can, which includes re-creating any existing fragments. You will get the same results if you undergo a configuration change (e.g., you rotate the screen), as your activity will be destroyed and re-created by default. Check to see if the fragment exists before adding it. – CommonsWare Oct 07 '15 at 13:07
  • Thank you very much !!! That's better now ! :p – acs-team Oct 07 '15 at 13:29
  • 1
    We are also getting same issue. Is it fixed? – Suresh Mar 03 '16 at 11:54
  • My solution below !!! – acs-team Mar 04 '16 at 13:32

2 Answers2

8

Here is my solution:

I use some fragments in my mainActivity.

As you know, the activity is recreated when user disable a permission => go through onCreate, ...

What i do in the onCreate(...) of my mainActivity, i remove all loaded fragments and put my app in the same state as a first run.

Like this:

    // Clean fragments (only if the app is recreated (When user disable permission))
    FragmentManager fragmentManager = getSupportFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {
        fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

    // Remove previous fragments (case of the app was restarted after changed permission on android 6 and higher)
    List<Fragment> fragmentList = fragmentManager.getFragments();
    if (fragmentList != null) {
        for (Fragment fragment : fragmentList) {
            if (fragment != null) {
                fragmentManager.beginTransaction().remove(fragment).commit();
            }
        }
    }

Be careful !!! : I had a crash with the version 23.2.0 of appcompat and design libraries. This crash appear when the app is recreated !

Check this link for more info.

Community
  • 1
  • 1
acs-team
  • 602
  • 6
  • 14
  • 1
    when we know the activity is recreated? Getting Crash "Fragment no longer exists". – Santosh Mar 22 '16 at 09:02
  • @Santosh you can either check if the fragment manager has any fragments like written above or you use my solution and hold the initialize state in the application class. Your app usually starts over a startup activity splash screen in case of a restart the startup activity is skipped and directly the last activity is shown. – eugstman Sep 25 '18 at 12:58
8

As already pointed out correct android is relaunching your app. This is the same behavior as when your app is in background and the system kills your application as more memory is used. What happens when you go back to your application is that the last activity inclusiv fragment(s) is recreated.

Usually a start up screen (splash screen) is used to initialize the application. Once the application is initialize (for example the services, viewmodel are ready) the startup-activity is switched to the main activity.

A common crash occurs in many applications when the app is recreated(for example revoke permissions) as the app is not initialized and the services or viewmodels are used which are null. I don't think there is a way to avoid a recreation of the last activity after a app restart.

What you could do is to check if the application is initialized and otherwise switch to the startup activity and initialize the app. Be aware that you have to handle unitialized applications in the activity and also the fragments.

Xamarin example code:

if (!((MyApplication)ApplicationContext).IsInitialized)
{
    Intent intent = new Intent(Application.Context,typeof(StartupActivity));
    intent.SetFlags(ActivityFlags.NewTask);
    StartActivity(intent);
    Finish();
}

As soon as base.onCreate is called the fragments are created so even the provided "workaround" from acs-team does not avoid the recreation of the last fragment.

With the above provided sample code the lifecycle will be in case the activity had a fragment:

  • Revoke permission app gets killed
  • Restart app
  • Application OnCreate
  • LastActivity.OnCreate
  • LastFragment.OnAttach
  • LastFragment.OnCreate
  • LastFragment.OnCreateView
  • LastFragment.OnViewCreated
  • LastFragment.OnDestroy
  • LastFragment.OnDettach
  • LastActivity.OnDestroy
  • StartupActivity.OnCreate

By the way you can also test an app restart over the adb shell:

Open your app then go to the android home screen in order that your app is in the background

adb shell "ps | grep <com.yourpackage>" // get the app process id
adb shell run-as <com.yourpackage> kill <app-process-id>    // kill the app

start the your app again over the icon or the recent tasks

eugstman
  • 978
  • 2
  • 15
  • 18
  • Can you post Java example code as well? The method IsInitialized doesn't seem to exist in Java. – Cody Apr 17 '18 at 15:38
  • 1
    @Cody this is a flag you set in your application once you have initialized all the required services. It is not available in Xamarin and Java you have to do it yourself. – eugstman Apr 26 '18 at 07:56
  • @eugstman I am having problems when user revokes any permissions. In this case app crashes immediately and no lifecycle event is called in the app. Is there any way how to handle this scenario? – Jan Nepraš Aug 13 '18 at 13:01
  • @JanNepraš your app gets killed and started again with the last activity which was open. Check the logcat immediately after you revoke the permissions you should find there the exception. You should also not be debugging as otherwise the restart could fail. – eugstman Aug 14 '18 at 11:51
  • How would you detect the event that permission is removed from settings @eugstman – Mansuu.... Sep 20 '18 at 11:12
  • @Mansuu.... you do net get any event, but your app gets restarted. You should check and request the required permissions when you require them e.g.: if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) == (int)Permission.Granted) { } – eugstman Sep 21 '18 at 14:58