大家都知道Activity中的requestWindowFeature需要在setContent前调用,否者会抛异常:
AndroidRuntimeException(“requestFeature() must be called before adding content”)
在一部5.0手机上在插件Activity中调用时发现,即使在插件中setContent前调用,也会抛出此异常。
分析下源码。
Activity中的RequestWindowFeature实际上是调用 window.requestFeature(int)
window变量实现类是PhoneWindow。
在7.0以上的版本中代码是:
public boolean requestFeature(int featureId) {
337 if (mContentParentExplicitlySet) {
338 throw new AndroidRuntimeException("requestFeature() must be called before adding content");
339 }
变量mContentParentExplicitlySet只在两个地方改变过值
@Override
399 public void setContentView(int layoutResID) {
421 mContentParentExplicitlySet = true;
422 }
430 public void setContentView(View view, ViewGroup.LayoutParams params) {
452 mContentParentExplicitlySet = true;
453 }
在6.0以下版本中,代码是:
@Override
315 public boolean requestFeature(int featureId) {
316 if (mContentParent != null) {
317 throw new AndroidRuntimeException("requestFeature() must be called before adding content");
318 }
private void installDecor() {
3980 if (mContentParent == null) {
3981 mContentParent = generateLayout(mDecor);
如下几处调用过installDecor
@Override
378 public void setContentView(int layoutResID) {
382 if (mContentParent == null) {
383 installDecor();
}
}
408 public void setContentView(View view, ViewGroup.LayoutParams params) {
412 if (mContentParent == null) {
413 installDecor();
414 }
430 }
433 public void addContentView(View view, ViewGroup.LayoutParams params) {
434 if (mContentParent == null) {
435 installDecor();
436 }
}
1967 public final View getDecorView() {
1968 if (mDecor == null) {
1969 installDecor();
1970 }
1972 }
4285 private ImageView getLeftIconView() {
4289 if (mContentParent == null) {
4290 installDecor();
4291 }
}
4303 private ProgressBar getCircularProgressBar(boolean shouldInstallDecor) {
4307 if (mContentParent == null && shouldInstallDecor) {
4308 installDecor();
4309 }
4315 }
4316
4317 private ProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) {
4321 if (mContentParent == null && shouldInstallDecor) {
4322 installDecor();
4323 }
4329 }
4330
4331 private ImageView getRightIconView() {
4335 if (mContentParent == null) {
4336 installDecor();
4337 }
4339 }
排查一下如果没有调用setContent,在哪里会调用到installDecor。
- 首先setContentView可以排除。
- addContentView会被 Dialog会调用
- getDecorView会被Dialog调用且其他调用地方较多
- getLeftIconView getRightIconView被 onDrawableChanged调用,onDrawableChanged方法系统没有调用
- getCircularProgressBar、 getHorizontalProgressBar
getCircularProgressBar、 getHorizontalProgressBar调用处
3648 protected ViewGroup generateLayout(DecorView decor) {
3649 // Apply data from current theme.
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
3919 ProgressBar progress = getCircularProgressBar(false);
3920 if (progress != null) {
3921 progress.setIndeterminate(true);
3922 }
3923 }
}
generateLayout<installDecor
1538 private void updateProgressBars(int value) {
1539 ProgressBar circularProgressBar = getCircularProgressBar(true);
1540 ProgressBar horizontalProgressBar = getHorizontalProgressBar(true);
updateProgressBars<onIntChanged<updateInt<setFeatureInt|setChildInt
setChildInt<onIntChanged
最后排除后发现setFeatureInt被Activity调用地方较多,可能是存在问题的原因
5383 public final void setProgressBarVisibility(boolean visible) {
5384 getWindow().setFeatureInt(Window.FEATURE_PROGRESS, visible ? Window.PROGRESS_VISIBILITY_ON :
5385 Window.PROGRESS_VISIBILITY_OFF);
5386 }
5396 public final void setProgressBarIndeterminateVisibility(boolean visible) {
5397 getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
5398 visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF);
5399 }
5410 public final void setProgressBarIndeterminate(boolean indeterminate) {
5411 getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
5412 indeterminate ? Window.PROGRESS_INDETERMINATE_ON
5413 : Window.PROGRESS_INDETERMINATE_OFF);
5414 }
5426 public final void setProgress(int progress) {
5427 getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START);
5428 }
5443 public final void setSecondaryProgress(int secondaryProgress) {
5444 getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
5445 secondaryProgress + Window.PROGRESS_SECONDARY_START);
5446 }
经过分析,7.0以下(不含)的版本,requestWindowFeature会去判断mContentParent 是否为null。如果不为null就会抛出异常。而mContentParent 在多处有有赋值,所以一定要在onCreate方法的最开始调用。
最后发现是在宿主中onCreate中一开始调用了如下代码:
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
activity.getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
UiModeManager uiModeManager = (UiModeManager) activity.getSystemService(Context.UI_MODE_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && uiModeManager != null
&& uiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES) {
setStatusBarTextWhite(activity);
} else {
setStatusBarTextBlack(activity);
}
}
});
删除后就好了。
还有,7.0以下的版本一定要在onCreate中同步调用,我们可以异步调用setContentView方法,但是不能异步调用requestWindowFeature,因为一旦onCreate执行结束后,其他一些系统自己执行的代码会把mContentParent 赋值,导致即使没有调用setContentView,也会抛出异常。