AppCompatDelegate.setDefaultNightMode()是第一次由主要活动拾取的?

l33*_*33t 6 android androidx

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

AppCompatDelegate.setDefaultNightMode(nightMode);
recreate();
Run Code Online (Sandbox Code Playgroud)

更新主题后,我返回MainActivity。在那里,主题已成功更新。然后,我重新打开PreferenceActivity再次更改主题。

到目前为止,一切都很好!

最后,我MainActivity再次回到。会议的主题是更新,如果你重复这些步骤也不会更新!

因此,重现步骤似乎是:

  1. 在活动A中打开活动B。
  2. 在B中,依次调用AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES)recreate()。主题已更新!
  3. 返回A。主题已更新!
  4. 再次打开活动B。
  5. 在B中,依次调用AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)recreate()。主题已更新!
  6. 返回到A。主题没有更新,并且如果重复步骤3-6 ,将不会更新!

我打过电话recreate()从returing时PreferenceActivity而是产生另一个问题,当库在主题的变化作出反应:

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

当库对更新的主题不起作用时,该方法有效。否则,该活动将被重新创建两次(调试时可能会重新创建),这会降低性能等:

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
Run Code Online (Sandbox Code Playgroud)

问:这是怎么回事用setDefaultNightMode()API?更重要的是,如何才能成功更新所有正在运行的活动,而又没有多次重新创建活动的风险?

更新

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

nav*_*ver 7

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

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

在其他活动的 onCreate() 方法中:

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

  • 从设置活动返回时,不会调用“onCreate”(除非我重新创建该活动)。“setDefaultNightMode”方法在应用程序启动时调用。一切正常,除了主题更新机制似乎在运行一次后就中断了。我相信[这个标志](https://android.googlesource.com/platform/frameworks/support/+/oreo-cts-release/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java#47 Android 源代码中的 ) 可能是罪魁祸首。 (2认同)

l33*_*33t 2

我找到了解决这个问题的一个非常简单的方法。它在两种情况下都有效;何时AppCompatActivity成功重新创建自身以及何时失败。回想一下,活动调用主题更改的A活动,然后我们返回到主题并不总是更新的地方。BA

活动B

在活动中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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

    super.finish();
}
Run Code Online (Sandbox Code Playgroud)

活动A

然后在活动中A我们使用这条信息:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

更新

看起来这个问题已经被修复了AppCompat 1.1.0。我不再需要这种解决方法来获得所需的行为。