AppCompatDelegate.setDefaultNightMode() 仅在第一次被 main activity 拾取?

AppCompatDelegate.setDefaultNightMode() picked up by main activity only the first time?

运行 Android P,使用 androidx 1.0.0 (minSdkVersion 17)。从我的 MainActivity 打开我的 PreferenceActivity。我在那里更改了 UI 主题,并重新创建了 activity 以获取更改:

AppCompatDelegate.setDefaultNightMode(nightMode);
recreate();

更新主题后,我return到MainActivity。主题已成功更新。然后我重新打开 PreferenceActivity 并再次更改主题

目前一切顺利!

终于,我return又来了MainActivity。主题更新,重复步骤将不会更新!

因此,重现的步骤似乎是:

  1. 从activityA,打开activityB.
  2. 在B中调用AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES)然后调用recreate()。主题已更新!
  3. Return to A.主题已更新!
  4. 再次打开activity B。
  5. 在B中调用AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)然后调用recreate()。主题已更新!
  6. Return 到 A。主题 已更新,如果重复步骤 3-6,将 更新!

我尝试在从 PreferenceActivity 返回时调用 recreate(),但是当库 对主题更改做出反应时,这会产生另一个问题:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (...) {
        recreate();
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

当图书馆对更新的主题没有反应时,这会起作用。否则 activity 会被重新创建两次(调试时可能更多),这会降低性能等:

D/MainActivity: onActivityResult(): instance 1
D/MainActivity: onResume(): instance 1
D/MainActivity: onPause(): instance 1
D/MainActivity: onDestroy(): instance 1

D/MainActivity: onCreate(): instance 2
D/MainActivity: onResume(): instance 2
D/MainActivity: onPause(): instance 2
D/MainActivity: onDestroy(): instance 2

D/MainActivity: onCreate(): instance 3
D/MainActivity: onResume(): instance 3

问: setDefaultNightMode() API 是怎么回事?更重要的是,我如何才能成功更新所有 运行 活动而不冒多次重新创建它们的风险?

更新

这里有一个演示该问题的示例项目:https://issuetracker.google.com/issues/119757688

当您更改夜间模式时,将模式值存储到共享首选项中。

AppCompatDelegate.setDefaultNightMode(nightMode);
recreate(); //only recreate setting activity 
...//store mode value, these lines are omitted,please complete yourself

在其他 activity onCreate() 方法中:

...//get mode from share preference, these lines are omitted.
AppCompatDelegate.setDefaultNightMode(mode)//must place before super.onCreate();
super.onCreate(savedInstanceState);

我找到了一个相当解决这个问题的简单方法。它在两种情况下都有效;当 AppCompatActivity 成功重新创建自己时 失败时。回想一下 activity A 在主题改变的地方调用 activity B,然后我们在主题不总是更新的地方 return 到 A

Activity B

在 activity B - 即首选项 - 我们会跟踪主题更改:

private boolean mThemeChanged;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mThemeChanged = getIntent().getBooleanExtra(EXTRA_THEME_CHANGED, false);
}

private void onNightModeChanged() {
    int nightMode = getNightModeFromPreferences();
    if (AppCompatDelegate.getDefaultNightMode() != nightMode) {
        AppCompatDelegate.setDefaultNightMode(nightMode);

        getIntent().putExtra(EXTRA_THEME_CHANGED, true);
        getDelegate().applyDayNight();
    }
}

我们将这条信息提供给调用activity,即Main:

@Override
public void finish() {
    Intent data = new Intent();
    data.putExtra(EXTRA_THEME_CHANGED, mThemeChanged);
    setResult(RESULT_OK, data);

    super.finish();
}

Activity一个

然后在activityA我们用到这条信息:

private boolean mShouldRecreateActivity;

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == ActivityResults.OPEN_SETTINGS_RESULT) {
        if (data != null && data.getBooleanExtra(SetPreferenceActivity.EXTRA_THEME_CHANGED, false)) {
            mShouldRecreateActivity = true;
        }
    }
}

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

    if (mShouldRecreateActivity) {
        recreate();
        return; // No need to continue resuming!
    }
}

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

    mShouldRecreateActivity = false;
}

在极少数情况下(通常是第一次)AppCompatActivity 正确调用 recreate() 我们的标志将被重置,避免在我们到达 [= 时重新创建 activity 21=]。因此,这段代码应该是面向未来的。不过,我真的希望这个问题在 androidx 的下一版本中得到解决,让我们摆脱解决方法。

更新

看起来这已在 AppCompat 1.1.0 中修复。我不再需要此解决方法来获得所需的行为。