Activity销毁重建 ActionBar IllegalStateException

一、问题来源
二、原因分析
  1.getSupportActionBar()
  2.setSupportActionBar()
  3.完整过程图
三、解决方法


问题来源

给Activity在代码中设置主题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=====*/

正常运行都没问题,但是当Activity被销毁重建时(模拟 Activity销毁重建 方法)就出现crash了,报错 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.

原因分析

当Activity设置ActionBar被销毁重建时,主要涉及AppCompatDelegateImplV9调用getSupportActionBar()setSupportActionBar()两个方法的过程
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);
    }
   //省略代码..
}

首先,会从(1) initWindowDecorActionBar() —> (2) ensureSubDecor() —> (3) createSubDecor();
然后,根据主题theme中设置的值给requestWindowFeature()传不同的参数,在theme中设置了false时,正常情况下会走(4)requestWindowFeature(Window.FEATURE_NO_TITLE),但销毁重建时就会走(5)requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR)。为什么会走到(5)去呢?大致原因就是销毁重建getSupportActionBar()时,还没有走到代码中设置theme的地方(就是上面NoActionBarActivity.java中的setTheme方法),于是createSubDecor()获取出来的theme就为默认的了。代码中No_Actionbar主题的父类是Theme.AppCompat.Light,默认带有Actionbar,所以走到了(5)传入参数FEATURE_SUPPORT_ACTION_BAR;
关键就在调用requestWindowFeature()方法这里:

Activity销毁重建 ActionBar IllegalStateException_第1张图片

最后,回到initWindowDecorActionBar()中的(6),当mHasActionBar为true时就不会return,而是继续往下走,于是就创建了mActionBar。
2.setSupportActionBar()
调用完getSupportActionBar()后会进行setSupportActionBar(),crash异常也就是在这个里面抛出的:

/*=====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销毁重建时在getSupportActionBar()中会创建一个ActionBar出来,也就是在(1)出获取的ActionBar不为null了,于是就走到了(2)中,抛出了异常。
3.完整过程图

Activity销毁重建 ActionBar IllegalStateException_第2张图片

解决方法

1.在AndroidManifest.xml中为Activity设置theme,不在代码中动态设置
2.代码动态设置theme时,在super.onCreate()前面设置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(" ");
    }
    //省略代码...
}

你可能感兴趣的:(Activity销毁重建 ActionBar IllegalStateException)