6

My app has got a single activity with a bottom navigation and multiple fragments and I'm using Navigation component.

In my settings fragment I have a ListPreference where the user can switch between light and dark mode.

if (themePreference != null) {
            String[] entries = {getResources().getString(R.string.dark), getResources().getString(R.string.light)};
            String[] entryValues = {Integer.toString(MODE_NIGHT_YES), Integer.toString(MODE_NIGHT_NO)};
            themePreference.setEntries(entries);
            themePreference.setEntryValues(entryValues);

            if (themePreference.getValue() == null) {
                themePreference.setValue(Integer.toString(MODE_NIGHT_YES));
            }

            themePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
                @Override
                public boolean onPreferenceChange(Preference preference, Object newValue) {
                    final String userSelectedValue = (String) newValue;

                    SharedPreferences.Editor sharedPreferencesEditor = sharedPreferences.edit();
                    sharedPreferencesEditor.putString(THEME, userSelectedValue);
                    sharedPreferencesEditor.apply();

                    AppCompatDelegate.setDefaultNightMode(Integer.parseInt(userSelectedValue));

                    return true;
                }
            });
        }

When I select a mode from the ListPreference entries, the mode gets changed but I get this error message in Logcat:

2020-04-28 16:50:42.342 17540-17540/de.xxxx.xxxx E/ActivityInjector: get life cycle exception
    java.lang.ClassCastException: android.os.BinderProxy cannot be cast to android.app.servertransaction.ClientTransaction
        at android.app.ActivityInjector.checkAccessControl(ActivityInjector.java:24)
        at android.app.Activity.onResume(Activity.java:1854)
        at androidx.fragment.app.FragmentActivity.onResume(FragmentActivity.java:455)
        at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1454)
        at android.app.Activity.performResume(Activity.java:8051)
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4236)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4278)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ClientTransactionHandler.executeTransaction(ClientTransactionHandler.java:57)
        at android.app.ActivityThread.handleRelaunchActivityLocally(ActivityThread.java:5298)
        at android.app.ActivityThread.access$3500(ActivityThread.java:220)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2050)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:224)
        at android.app.ActivityThread.main(ActivityThread.java:7520)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)

All explanations I have found for this error are about Binding and Services. I don't understand what this has got to do with my case though. Is it because of the recreation of the activity when it changes to night/day mode?

It happens when I try this on my Xiaomi Mi 9 but not with the emulator and for example Pixel 3 or Nexus 4. Maybe this is a Xiaomi problem?

Any idea how to fix this? Thanks in advance.

Edit:

Main Activity

public class MainActivity extends AppCompatActivity implements ProviderInstaller.ProviderInstallListener {

    private static final int ERROR_DIALOG_REQUEST_CODE = 1;

    private boolean retryProviderInstall;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.AppTheme_NoActionBar);
        super.onCreate(savedInstanceState);

        ProviderInstaller.installIfNeededAsync(this, this);

        setContentView(R.layout.activity_main);

        Toolbar actionBarToolBar = findViewById(R.id.toolbar);
        setSupportActionBar(actionBarToolBar);

        BottomNavigationView navView = findViewById(R.id.nav_view);

        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                R.id.navigation_search, R.id.navigation_bookmarks, R.id.navigation_settings, R.id.navigation_about)
                .build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        NavigationUI.setupWithNavController(navView, navController);

        onNewIntent(getIntent());
    }

    @Override
    public boolean onSupportNavigateUp() {
        return Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp()
                || super.onSupportNavigateUp();
    }

    @Override
    public void onProviderInstalled() {
        // Provider is up-to-date, app can make secure network calls.
    }

    @Override
    public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
        GoogleApiAvailability availability = GoogleApiAvailability.getInstance();
        if (availability.isUserResolvableError(errorCode)) {
            // Recoverable error. Show a dialog prompting the user to
            // install/update/enable Google Play services.
            availability.showErrorDialogFragment(
                    this,
                    errorCode,
                    ERROR_DIALOG_REQUEST_CODE,
                    new DialogInterface.OnCancelListener() {
                        @Override
                        public void onCancel(DialogInterface dialog) {
                            // The user chose not to take the recovery action
                            onProviderInstallerNotAvailable();
                        }
                    });
        } else {
            // Google Play services is not available.
            onProviderInstallerNotAvailable();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode,
                                    Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == ERROR_DIALOG_REQUEST_CODE) {
            retryProviderInstall = true;
        }
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        if (retryProviderInstall) {
            ProviderInstaller.installIfNeededAsync(this, this);
        }
        retryProviderInstall = false;
    }

    private void onProviderInstallerNotAvailable() {
    }
}
  • Colin did you find a solution? – Smekalisty Jul 15 '20 at 10:36
  • Smekalisty unfortunately, I haven't found a solution yet. –  Jul 15 '20 at 18:20
  • 2
    I found. AppCompatDelegate.setDefaultNightMode (AppCompatDelegate.MODE_NIGHT_NO) before super.onCreate (savedInstanceState). For me bug is reproduced only on xiaomi, on pixel it works both before and after – Smekalisty Jul 20 '20 at 09:22
  • Smekalisty when I want to change that value based on user input after onCreate() that does not work though. –  Jul 22 '20 at 11:47
  • Do you have a launcher activity which is not the MainActivity? I was able to get rid of this exception once I set this AppCompatDelegate.setDefaultNightMode (AppCompatDelegate.MODE_NIGHT_NO) in launcher activity as per the manifest – sydeng Sep 07 '20 at 05:57
  • Seems to be a MIUI Xiaomi issue https://stackoverflow.com/questions/63209993/getting-lifecycle-exception-while-recreating-the-activity – mathematics-and-caffeine Jun 28 '22 at 20:33

2 Answers2

3

This is of course device specific issue... what I suggest you to do like this

@Override
protected void onCreate(Bundle savedInstanceState) {

    // must do this before super call or setContentView
    // pick which theme DAY or NIGHT from settings
    setTheme(someSettings.get(PREFERRED_THEME) ? R.style.AppThemeLight : R.style.AppThemeDark);

    super.onCreate(savedInstanceState);
}

// Somewhere in your activity where the button switches the theme
btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        // save theme of as user preference
        someSettings.save(PREFERRED_THEME);

        MainActivity.this.recreate();
    }
});


MainActivity.this.recreate(); // it will recreate activity
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
devROYAL
  • 430
  • 4
  • 12
  • When I use ``MainActivity.this.recreate();`` Android Studio tells me it's not an enclosing class. I tried to use ``requireActivity().recreate();`` but then I get exactly the same error as before (BinderProxy cannot be cast to ClientTransaction). –  Apr 28 '20 at 15:59
  • Can you post complete MainActivity here – devROYAL Apr 29 '20 at 02:06
  • I updated the question with the MainActivity @devROYAL –  Apr 29 '20 at 12:56
3

This seem caused by calling Activity.recreate() in Xiaomi's firmware

Darksymphony
  • 2,155
  • 30
  • 54