一、問(wèn)題來(lái)源
二、原因分析
1.getSupportActionBar()
2.setSupportActionBar()
3.完整過(guò)程圖
三、解決方法
問(wèn)題來(lái)源
給Activity在代碼中設(shè)置主題theme,然后Activity被銷毀重建了。先看主體代碼:
/*=====NoActionBarActivity.java=====*/
public class NoActionBarActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.No_Actionbar);
Toolbar toolbar = (Toolbar) getView().findViewById(R.id.home_page_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle(" ");
}
//省略代碼...
}
/*=====Style.xml=====*/
<style name="No_Actionbar" parent="Theme.AppCompat.Light">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/bg_actionmode</item>
</style>
正常運(yùn)行都沒(méi)問(wèn)題,但是當(dāng)Activity被銷毀重建時(shí)(模擬 Activity銷毀重建 方法)就出現(xiàn)crash了,報(bào)錯(cuò) java.lang.IllegalStateException: This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.

原因分析
當(dāng)Activity設(shè)置ActionBar被銷毀重建時(shí),主要涉及AppCompatDelegateImplV9調(diào)用getSupportActionBar()和setSupportActionBar()兩個(gè)方法的過(guò)程
1.getSupportActionBar()
/*=====AppCompatDelegateImplBase.java=====*/
@Override
public ActionBar getSupportActionBar() {
// The Action Bar should be lazily created as hasActionBar
// could change after onCreate
initWindowDecorActionBar(); //***(1)***
return mActionBar;
}
/*=====AppCompatDelegateImplV9.java=====*/
@Override
public void initWindowDecorActionBar() {
ensureSubDecor(); //***(2)***
if (!mHasActionBar || mActionBar != null) { //***(6)***
return;
}
if (mOriginalWindowCallback instanceof Activity) {
mActionBar = new WindowDecorActionBar((Activity) mOriginalWindowCallback,
mOverlayActionBar);
} else if (mOriginalWindowCallback instanceof Dialog) {
mActionBar = new WindowDecorActionBar((Dialog) mOriginalWindowCallback);
}
if (mActionBar != null) {
mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
}
}
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor(); //***(3)***
// If a title was set before we installed the decor, propagate it now
CharSequence title = getTitle();
if (!TextUtils.isEmpty(title)) {
onTitleChanged(title);
}
//省略代碼...
}
}
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
a.recycle();
throw new IllegalStateException(
"You need to use a Theme.AppCompat theme (or descendant) with this activity.");
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) { //***(4)***
requestWindowFeature(Window.FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) { //***(5)***
// Don't allow an action bar if there is no title.
requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
}
//省略代碼..
}
首先,會(huì)從(1) initWindowDecorActionBar() —> (2) ensureSubDecor() —> (3) createSubDecor();
然后,根據(jù)主題theme中設(shè)置的值給requestWindowFeature()傳不同的參數(shù),在theme中設(shè)置了<item name="windowActionBar">false</item>時(shí),正常情況下會(huì)走(4)requestWindowFeature(Window.FEATURE_NO_TITLE),但銷毀重建時(shí)就會(huì)走(5)requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR)。為什么會(huì)走到(5)去呢?大致原因就是銷毀重建getSupportActionBar()時(shí),還沒(méi)有走到代碼中設(shè)置theme的地方(就是上面NoActionBarActivity.java中的setTheme方法),于是createSubDecor()獲取出來(lái)的theme就為默認(rèn)的了。代碼中No_Actionbar主題的父類是Theme.AppCompat.Light,默認(rèn)帶有Actionbar,所以走到了(5)傳入?yún)?shù)FEATURE_SUPPORT_ACTION_BAR;
關(guān)鍵就在調(diào)用requestWindowFeature()方法這里:

最后,回到initWindowDecorActionBar()中的(6),當(dāng)mHasActionBar為true時(shí)就不會(huì)return,而是繼續(xù)往下走,于是就創(chuàng)建了mActionBar。
2.setSupportActionBar()
調(diào)用完getSupportActionBar()后會(huì)進(jìn)行setSupportActionBar(),crash異常也就是在這個(gè)里面拋出的:
/*=====AppCompatDelegateImplV9.java=====*/
@Override
public void setSupportActionBar(Toolbar toolbar) {
if (!(mOriginalWindowCallback instanceof Activity)) {
// Only Activities support custom Action Bars
return;
}
final ActionBar ab = getSupportActionBar(); //***(1)***
if (ab instanceof WindowDecorActionBar) {
throw new IllegalStateException("This Activity already has an action bar supplied " + //***(2)***
"by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set " +
"windowActionBar to false in your theme to use a Toolbar instead.");
}
// If we reach here then we're setting a new action bar
// First clear out the MenuInflater to make sure that it is valid for the new Action Bar
mMenuInflater = null;
// If we have an action bar currently, destroy it
if (ab != null) {
ab.onDestroy();
}
if (toolbar != null) {
final ToolbarActionBar tbab = new ToolbarActionBar(toolbar,
((Activity) mOriginalWindowCallback).getTitle(), mAppCompatWindowCallback);
mActionBar = tbab;
mWindow.setCallback(tbab.getWrappedWindowCallback());
} else {
mActionBar = null;
// Re-set the original window callback since we may have already set a Toolbar wrapper
mWindow.setCallback(mAppCompatWindowCallback);
}
invalidateOptionsMenu();
}
由上面知道,在get銷毀重建時(shí)在getSupportActionBar()中會(huì)創(chuàng)建一個(gè)ActionBar出來(lái),也就是在(1)出獲取的ActionBar不為null了,于是就走到了(2)中,拋出了異常。
3.完整過(guò)程圖

解決方法
1.在AndroidManifest.xml中為Activity設(shè)置theme,不在代碼中動(dòng)態(tài)設(shè)置
2.代碼動(dòng)態(tài)設(shè)置theme時(shí),在super.onCreate()前面設(shè)置theme
/*=====NoActionBarActivity.java=====*/
public class NoActionBarActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(R.style.No_Actionbar); //修改setTheme()在super.onCreate()之前
super.onCreate(savedInstanceState);
Toolbar toolbar = (Toolbar) getView().findViewById(R.id.home_page_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle(" ");
}
//省略代碼...
}