//調用了 window 的 setContentView,在 attach 中給 mWindow=new PhoneWindow(this);
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
複製代碼
/**@methord installDecor() 爲 activity 添加根佈局DecorView(mDecor),全部的事件都是經過它從 activity 傳到 view 上的。並解析 activity 的theme初始化 titleview 和過場動畫。 *@field mContentParent 根據 theme 的不一樣inflate 幾種佈局加到DecorView上,在上一個方法中同時初始化mDecor和mContentParent @field FEATURE_CONTENT_TRANSITIONS判斷是否有5.0共享元素過場動畫,沒有直接調用了 inflate **/
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);//處理makeSceneTransitionAnimation用的
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();//調用了requestFitSystemWindows
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {//調用設置 contentChange 的回調
cb.onContentChanged();
}
}
複製代碼
這裏直接調用了LayoutInflater.inflate,然而LayoutInflater又是經過getSystemService(LAYOUT_INFLATER_SERVICE)
得到的,在 activity 中的 mBase 又是從哪裏來的呢,在 startActivity 後啓動了新的Activity,在 Instrumentation的execStartActivity中ActivityManagerNative.getDefault().startActivity()
使用ActivityManagerProxy.startActivity經過IBinder調用了系統進程。並無找到 activity 是怎麼 new 出來的java
換個套路,因爲 activity歷來不復寫構造方法,mBase 賦值的地方只有一個,attachBaseContext();
複寫這個方法並在裏面拋出異常。在一層層的去分析,log代表在ActivityThread中收到了 handler,並在performLaunchActivity直接調用了 attach,在 attach 中第一行就是設置 context 的方法android
public ContextThemeWrapper() {
super(null);//咱們繼承 activity 的時候並無調用 super 的構造,確定不是在這裏初始化的
}
複製代碼
at android.app.Activity.attach(Activity.java:6641)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2629)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2768)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1481)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6153)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:758)
複製代碼
//activity.attach 的 context 參數是這樣初始化的
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
int displayId = Display.DEFAULT_DISPLAY;
try {
displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
} catch (RemoteException e) {
}
//就是這裏 new ContextImpl()。這個類中找 getSystemService
ContextImpl appContext = ContextImpl.createActivityContext( this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
......
return baseContext;
}
複製代碼
/**在 contextImpl 中只有一行代碼 SystemServiceRegistry.getSystemService(),進入到SystemServiceRegistry是這樣實現的 *@params SYSTEM_SERVICE_FETCHERS 這個參數中保存了 getSystemService的全部能夠用的 service,在這個類中有一個靜態方法塊,添加service *@params ServiceFetcher 是一個接口 **/
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
//靜態代碼塊中,是這樣註冊的,PhoneLayoutInflater就是實際調用的 view 解析器。
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
複製代碼
到這裏整個應用每次獲取的 LayoutInflate仍是同樣的。可是在每一個 activity 中把 LayoutInflate 的 hashcode 輸出缺是不同的。由於在 ContextThemeWrapper.getSystemService 還調用了cloneInContext緩存
//在PhoneLayoutInflater中是這樣實現的,全部每一個 activity 使用的inflate並非同一個
public LayoutInflater cloneInContext(Context newContext) {
return new PhoneLayoutInflater(this, newContext);
}
複製代碼
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
final XmlResourceParser parser = res.getLayout(resource);//建立了 xml 解析器
try {//最終解析的方法
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
複製代碼
//這裏解析的是 xml 的第一層
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");//保存 log 到系統中
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
// Empty
}//按理說只執行一次 next,這樣寫多是防止 parser 在前面的代碼已經向後移動了,當前的 type 並無在 START 的位置
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription() + ": No start tag found!");
}
final String name = parser.getName();
//判斷 是否是 merge
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true");
}//由於 merge 不是 view,不建立根節點必需要添加到root 上
//根據root 初始化佈局
rInflate(parser, root, inflaterContext, attrs, false);
} else {//經過 string和 xml 的屬性建立view 的邏輯
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {//若是設置了root 要建立出 layoutParams
params = root.generateLayoutParams(attrs);//根據 attrs 建立 layoutParams
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
rInflateChildren(parser, temp, attrs, true);//和rInflate不一樣的是,這裏是加到了createViewFromTag的 view 上了。
if (root != null && attachToRoot) {//把createViewFromTag加到了root 上,比 merge 多了一層view
root.addView(temp, params);
}
if (root == null || !attachToRoot) {
result = temp;//若是傳入root 而且addview 了返回 root,不然返回建立的 view
}
}
} catch (XmlPullParserException e) {
final InflateException ie = new InflateException(e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(parser.getPositionDescription() + ": " + e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
複製代碼
rInflateChildren 和 rInflate 方法的差別是少了一個 context 的參數,由於,merge 的 root 的 attr 不必定和 xml 裏面用同一個。全部須要單獨傳 context。bash
這裏解析xml 除了根標籤的子標籤,每一層子標籤會遞歸一層app
void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {//當解析到最後一個 tag 的時候結束
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {//view 下有 <requestFocus /> 請求焦點
parseRequestFocus(parser, parent);
} else if (TAG_TAG.equals(name)) {// <tag android:id="@+id/mytag" android:value="@string/mytag_value" />給 view 添加多個 tag
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {//解析到了 include
if (parser.getDepth() == 0) {//不能把 xml 的第一個標籤就聲明爲 include
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);//只是合併 include標籤和 layout 的屬性邏輯,不作分析
} else if (TAG_MERGE.equals(name)) {//xml 中 merge 必須是根元素
throw new InflateException("<merge /> must be the root element");
} else {//普通的 view
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);//只要繼承了 ViewGroup 都會複寫這個方法。
rInflateChildren(parser, view, attrs, true);//遞歸調用 rInflate 直到完成解析
viewGroup.addView(view, params);
}
}
if (finishInflate) {//當全部的子 view 解析完成調用
parent.onFinishInflate();
}
}
複製代碼
createViewFromTag是經過xml標籤生成 view 的重點方法。
複製代碼
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) {
if (name.equals("view")) {//view 的 className 支持兩種傳入方式,讀取 class 屬性值
name = attrs.getAttributeValue(null, "class");
}
//include設置了theme 這裏是 false,不然都會使用 context 的屬性
if (!ignoreThemeAttr) {
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
if (themeResId != 0) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
}
if (name.equals(TAG_1995)) {//繼承了 FragmentLayout,閃爍的效果,不多用
// Let's party like it's 1995!
return new BlinkLayout(context, attrs);
}
try {
View view;
if (mFactory2 != null) {//設置了自定義的 view 建立方式。這個接口就是我想設置的
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {//和 Factory2相比這個裏面的onCreateView方法。少 parent 參數
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null){//系統的註釋很到位,這個是爲手機廠家留的,for use by framework
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {//若是用戶和手機廠商沒有設置 factory,就使用系統默認的解析
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {//TextView,ImageView這些系統控件,在調用的時候不是全 className,這個方法裏拼接了 createView(name, "android.view.", attrs)
view = onCreateView(parent, name, attrs);
} else {//自定義 view
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
} catch (InflateException e) {
throw e;
} catch (ClassNotFoundException e) {
final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name, e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name, e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
}
}
複製代碼
若是須要自定義 View 的實例化須要調用 setFactory2或setFactory,這兩個方法差異不大,這兩個方法若是某一個調用過一次了,若是再次調用會拋出異常,`new FactoryMerger(factory, factory, mFactory, mFactory2)`由於只能設置一次,不使用反射,後面兩個參數確定是null,前面兩個都是設置的factory。若是調用setFactory2會拿到 parent。setFactory則不會。
複製代碼
createView()方法中 經過反射獲取 class,並獲取構造方法,並使用 sConstructorMap 緩存爲後面加載一樣的 View 使用,view = constructor.newInstance(args);
反射構造傳入 context 和 attr 建立一個實例,這裏只使用了兩個構造參數的方法 mConstructorSignature = new Class[] {Context.class, AttributeSet.class}
。在 View 的源碼中能夠看到兩個參數的構造方法調用了4個參數的構造:ide
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this(context);
.........
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_background: ...
break;
case com.android.internal.R.styleable.View_padding: ...
break;
case com.android.internal.R.styleable.View_paddingLeft: ...
break;
case com.android.internal.R.styleable.View_paddingTop: ...
break;
case com.android.internal.R.styleable.View_paddingRight: ...
break;
.........
複製代碼
點進源碼看到它繼承了 FragmentActivity 在 onCreate 方法的第一行AppCompatDelegate delegate = getDelegate(); delegate.installViewFactory();
修改了 Factory,xml 中生命的 TextView 都被實例化成了 AppCompatTextView。就有了RippleDrawable的效果oop
public final View createView(View parent, final String name, @NonNull Context context, @NonNull AttributeSet attrs, boolean inheritContext,boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
......
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) {
view = createViewFromTag(context, name, attrs);
}
return view;
}
複製代碼
add by dingshaoran佈局