Activity
Activity是最基礎的一個,是其它類的直接或間接父類。
Activity中只能使用系統自帶的host Fragment(API Level 11中加入),對應getFragmentManager方法來控制Activity和Fragment之間的交互。
FragmentActivity
在v4包中引入FragmentActivity,FragmentActivity間接繼承自Activity,並提供了對v4包中support Fragment的支持。
在FragmentActivity中必須使用getSupportFragmentManager方法來處理support Fragment的交互。也可以處理support Fragment的嵌套使用。
Known limitations: * When using the `<fragment>` tag, this implementation can not use the parent view's ID as the new fragment's ID. You must explicitly specify an ID (or tag) in the `<fragment>`.
AppCompatActivity
AppCompatActivity繼承自FragmentActivity,同時取代了ActionBarActivity。
AppCompatActivity支持ActionBar功能,同時更推薦使用ToolBar。AppCompatActivity為支持Material Design風格控件提供了便利。
使用場景
參考stack overflow中一個回答。
Activity is the baseline. Every activity inherits from Activity, directly or indirectly.
FragmentActivity is for use with the backport of fragments found in the support-v4 and support-v13 libraries. The native implementation of fragments was added in API Level 11, which is lower than your proposed minSdkVersion values. The only reason why you would need to consider FragmentActivity specifically is if you want to use nested fragments (a fragment holding another fragment), as that was not supported in native fragments until API Level 17.
AppCompatActivity is from the appcompat-v7 library. Principally, this offers a backport of the action bar. Since the native action bar was added in API Level 11, you do not need AppCompatActivity for that. However, current versions of appcompat-v7 also add a limited backport of the Material Design aesthetic, in terms of the action bar and various widgets. There are pros and cons of using appcompat-v7, well beyond the scope of this specific Stack Overflow answer.
ActionBarActivity is the old name of the base activity from appcompat-v7. For various reasons, they wanted to change the name. Unless some third-party library you are using insists upon an ActionBarActivity, you should prefer AppCompatActivity over ActionBarActivity.
So, given your minSdkVersion in the 15-16 range:
-
If you want the backported Material Design look, use AppCompatActivity
-
If not, but you want nested fragments, use FragmentActivity
-
If not, use Activity
Just adding from comment as note: AppCompatActivity extends FragmentActivity, so anyone who needs to use features of FragmentActivity can use AppCompatActivity.
AppCompatActivity中AppCompat系列組件的構造及替換
AppCompatActivity中通過AppCompatDelegate來擴展Activity。AppCompatDelegate可以在任一Activity中使用,需要與合適的生命周期方法掛鈎。具體可參考官方文檔https://developer.android.com/reference/android/support/v7/app/AppCompatDelegate或者AppCompatDelegate的源碼注釋。
在AppCompatActivity的onCreate方法中,調用AppCompatDelegate(基類)的create方法創建實例,並調用delegate.installViewFactory()設置factory。
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { final AppCompatDelegate delegate = getDelegate(); delegate.installViewFactory(); delegate.onCreate(savedInstanceState); ...... super.onCreate(savedInstanceState); }
創建AppCompatDelegate子類實例的代碼如下,根據手機的安卓版本來創建不同的子類實例
private static AppCompatDelegate create(Context context, Window window, AppCompatCallback callback) { if (Build.VERSION.SDK_INT >= 24) { return new AppCompatDelegateImplN(context, window, callback); } else if (Build.VERSION.SDK_INT >= 23) { return new AppCompatDelegateImplV23(context, window, callback); } else if (Build.VERSION.SDK_INT >= 14) { return new AppCompatDelegateImplV14(context, window, callback); } else if (Build.VERSION.SDK_INT >= 11) { return new AppCompatDelegateImplV11(context, window, callback); } else { return new AppCompatDelegateImplV9(context, window, callback); } }
以android 6.0(API 23)為例,來跟蹤AppCompatDelegate實例對象的創建。AppCompatDelegateImplV23間接繼承AppCompatDelegateImplV9,並實現了LayoutInflater.Factory2接口。
AppCompatDelegateImplV9的installViewFactory調用了 LayoutInflaterCompat.setFactory2,設置了自身的Factory2實現。
@Override public void installViewFactory() { LayoutInflater layoutInflater = LayoutInflater.from(mContext); if (layoutInflater.getFactory() == null) { LayoutInflaterCompat.setFactory2(layoutInflater, this); } else { if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) { Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed" + " so we can not install AppCompat's"); } } }
AppCompatActivity創建view時,通過PhoneWindow->LayoutInflater(createViewFromTag)進入了其實現Factory2的onCreateView方法。可參考LayoutInflater setFactory進階中setContentView調用流程分析
/** * From {@link LayoutInflater.Factory2}. */ @Override public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) { // First let the Activity's Factory try and inflate the view final View view = callActivityOnCreateView(parent, name, context, attrs); if (view != null) { return view; } // If the Factory didn't handle it, let our createView() method try return createView(parent, name, context, attrs); }
如果AppCompatActivity並沒有設置Factory,就會調用Delegate實例的createView方法創建view。
@Override public View createView(View parent, final String name, @NonNull Context context, @NonNull AttributeSet attrs) { if (mAppCompatViewInflater == null) { mAppCompatViewInflater = new AppCompatViewInflater(); } boolean inheritContext = false; if (IS_PRE_LOLLIPOP) { inheritContext = (attrs instanceof XmlPullParser) // If we have a XmlPullParser, we can detect where we are in the layout ? ((XmlPullParser) attrs).getDepth() > 1 // Otherwise we have to use the old heuristic : shouldInheritContext((ViewParent) parent); } return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext, IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */ true, /* Read read app:theme as a fallback at all times for legacy reasons */ VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */ ); }
根據上述代碼可知,Delegate實例調用了AppCompatViewInflater的createView來完成具體view的繪制。
public final View createView(View parent, final String name, @NonNull Context context, @NonNull AttributeSet attrs, boolean inheritContext, boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) { final Context originalContext = context; // We can emulate Lollipop's android:theme attribute propagating down the view hierarchy // by using the parent's context if (inheritContext && parent != null) { context = parent.getContext(); } if (readAndroidTheme || readAppTheme) { // We then apply the theme on the context, if specified context = themifyContext(context, attrs, readAndroidTheme, readAppTheme); } if (wrapContext) { context = TintContextWrapper.wrap(context); } View view = null; // We need to 'inject' our tint aware Views in place of the standard framework versions switch (name) { case "TextView": view = new AppCompatTextView(context, attrs); break; case "ImageView": view = new AppCompatImageView(context, attrs); break; case "Button": view = new AppCompatButton(context, attrs); break; case "EditText": view = new AppCompatEditText(context, attrs); break; case "Spinner": view = new AppCompatSpinner(context, attrs); break; case "ImageButton": view = new AppCompatImageButton(context, attrs); break; case "CheckBox": view = new AppCompatCheckBox(context, attrs); break; case "RadioButton": view = new AppCompatRadioButton(context, attrs); break; case "CheckedTextView": view = new AppCompatCheckedTextView(context, attrs); break; case "AutoCompleteTextView": view = new AppCompatAutoCompleteTextView(context, attrs); break; case "MultiAutoCompleteTextView": view = new AppCompatMultiAutoCompleteTextView(context, attrs); break; case "RatingBar": view = new AppCompatRatingBar(context, attrs); break; case "SeekBar": view = new AppCompatSeekBar(context, attrs); break; } if (view == null && originalContext != context) { // If the original context does not equal our themed context, then we need to manually // inflate it using the name so that android:theme takes effect. view = createViewFromTag(context, name, attrs); } if (view != null) { // If we have created a view, check its android:onClick checkOnClickListener(view, attrs); } return view; }
在上述代碼中可看到,AppCompatActivity中TextView等組件,被替代為AppCompatTextView等,從而可以利用AppCompat系列組件的特性。但是書寫自定義view時,需要手動使用AppCompat組件。
參考AppCompatTextView的提示
This will automatically be used when you use TextView in your layouts and the top-level activity / dialog is provided by appcompat. You should only need to manually use this class when writing custom views.
作者:zizi192
鏈接:https://www.jianshu.com/p/9d590c478828
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。