文章目录
- activity的创建
- performLaunchActivity
- handleResumeActivity
- setContentView
- mInstrumentation.newActivity
- new出phonewindow
- WindowManager的创建
- 回到setContextView
- findViewById
- addView
- ViewRoot
- Android在哪里“画画”
我们知道Android绘制图形依靠的是surface和surface flinger,但是当我说起这句话的时候,你脑海里面能复现出一幅图画,里面展示了Android绘制图像,然后渲染到屏幕这整个执行流程吗?如果没有,一起来研究研究Android的这个机制。我想,理清楚之后,作为Android开发的我们,必定获益良多。
先看看surface系统的的整体关系:
不论是使用Skia绘制二维图像,还是使用OpenGL绘制三维图像,APP都会Surface进行交互。surface就行画布一样,app是一个艺术家,在surface上面作画。但是它们关系如何,我们目前不得而知,下面我们通过源码分析,一步步打通它们。
activity的创建
我们知道zygote响应请求之后会fork一个子进程,这个进程就是App进程,入口函数在ActivityThread.java的main函数,它会执行到handleLaunchActivity函数:
private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {// If we are getting ready to gc after going to the background, well// we are back active so skip it.unscheduleGcIdler();if (localLOGV) Slog.v(TAG, "Handling launch of " + r);//返回一个activityActivity a = performLaunchActivity(r, customIntent);if (a != null) {r.createdConfig = new Configuration(mConfiguration);Bundle oldState = r.state;//重点handleResumeActivity(r.token, false, r.isForward);if (!r.activity.mFinished && r.startsNotResumed) {// The activity manager actually wants this one to start out// paused, because it needs to be visible but isn't in the// foreground. We accomplish this by going through the// normal startup (because activities expect to go through// onResume() the first time they run, before their window// is displayed), and then pausing it. However, in this case// we do -not- need to do the full pause cycle (of freezing// and such) because the activity manager assumes it can just// retain the current state it has.try {r.activity.mCalled = false;mInstrumentation.callActivityOnPause(r.activity);// We need to keep around the original state, in case// we need to be created again.r.state = oldState;if (!r.activity.mCalled) {throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() +" did not call through to super.onPause()");}} catch (SuperNotCalledException e) {throw e;} catch (Exception e) {if (!mInstrumentation.onException(r.activity, e)) {throw new RuntimeException("Unable to pause activity "+ r.intent.getComponent().toShortString()+ ": " + e.toString(), e);}}r.paused = true;}} else {// If there was an error, for any reason, tell the activity// manager to stop us.try {ActivityManagerNative.getDefault().finishActivity(r.token, Activity.RESULT_CANCELED, null);} catch (RemoteException ex) {}}}
performLaunchActivity
先看下activity的创建
private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent) {// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");ActivityInfo aInfo = r.activityInfo;if (r.packageInfo == null) {r.packageInfo = getPackageInfo(aInfo.applicationInfo,Context.CONTEXT_INCLUDE_CODE);}ComponentName component = r.intent.getComponent();if (component == null) {component = r.intent.resolveActivity(mInitialApplication.getPackageManager());r.intent.setComponent(component);}if (r.activityInfo.targetActivity != null) {component = new ComponentName(r.activityInfo.packageName,r.activityInfo.targetActivity);}Activity activity = null;try {java.lang.ClassLoader cl = r.packageInfo.getClassLoader();//通过反射创建的Activityactivity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);r.intent.setExtrasClassLoader(cl);if (r.state != null) {r.state.setClassLoader(cl);}} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to instantiate activity " + component+ ": " + e.toString(), e);}}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);if (localLOGV) Slog.v(TAG, "Performing launch of " + r);if (localLOGV) Slog.v(TAG, r + ": app=" + app+ ", appName=" + app.getPackageName()+ ", pkg=" + r.packageInfo.getPackageName()+ ", comp=" + r.intent.getComponent().toShortString()+ ", dir=" + r.packageInfo.getAppDir());if (activity != null) {//平时我们在Activity中调用的getContext就是它。ContextImpl appContext = new ContextImpl();appContext.init(r.packageInfo, r.token, this);appContext.setOuterContext(activity);CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());Configuration config = new Configuration(mConfiguration);if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "+ r.activityInfo.name + " with config " + config);activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstance,r.lastNonConfigurationChildInstances, config);if (customIntent != null) {activity.mIntent = customIntent;}r.lastNonConfigurationInstance = null;r.lastNonConfigurationChildInstances = null;activity.mStartedActivity = false;int theme = r.activityInfo.getThemeResource();if (theme != 0) {activity.setTheme(theme);}activity.mCalled = false;//调用生命周期的onCreatemInstrumentation.callActivityOnCreate(activity, r.state);if (!activity.mCalled) {throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() +" did not call through to super.onCreate()");}r.activity = activity;r.stopped = true;if (!r.activity.mFinished) {activity.performStart();r.stopped = false;}if (!r.activity.mFinished) {if (r.state != null) {mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);}}if (!r.activity.mFinished) {activity.mCalled = false;mInstrumentation.callActivityOnPostCreate(activity, r.state);if (!activity.mCalled) {throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() +" did not call through to super.onPostCreate()");}}}r.paused = true;mActivities.put(r.token, r);} catch (SuperNotCalledException e) {throw e;} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to start activity " + component+ ": " + e.toString(), e);}}return activity;}
可以看到,这里new了一个activity,并执行了onCreate,开始他的生命周期生涯,让activity活起来了。
handleResumeActivity
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {// If we are getting ready to gc after going to the background, well// we are back active so skip it.unscheduleGcIdler();ActivityRecord r = performResumeActivity(token, clearHide);if (r != null) {final Activity a = r.activity;if (localLOGV) Slog.v(TAG, "Resume " + r + " started activity: " +a.mStartedActivity + ", hideForNow: " + r.hideForNow+ ", finished: " + a.mFinished);final int forwardBit = isForward ?WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;// If the window hasn't yet been added to the window manager,// and this guy didn't finish itself or start another activity,// then go ahead and add the window.boolean willBeVisible = !a.mStartedActivity;if (!willBeVisible) {try {willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(a.getActivityToken());} catch (RemoteException e) {}}if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();//1:decor view,我们应该很熟悉View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE);//2:wm,重要ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;l.softInputMode |= forwardBit;if (a.mVisibleFromClient) {a.mWindowAdded = true;//3:把decorview添加wm中wm.addView(decor, l);}// If the window has already been added, but during resume// we started another activity, then don't yet make the// window visible.} else if (!willBeVisible) {if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");r.hideForNow = true;}// The window is now visible if it has been added, we are not// simply finishing, and we are not starting another activity.if (!r.activity.mFinished && willBeVisible&& r.activity.mDecor != null && !r.hideForNow) {if (r.newConfig != null) {if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "+ r.activityInfo.name + " with newConfig " + r.newConfig);performConfigurationChanged(r.activity, r.newConfig);r.newConfig = null;}if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="+ isForward);WindowManager.LayoutParams l = r.window.getAttributes();if ((l.softInputMode& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)!= forwardBit) {l.softInputMode = (l.softInputMode& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))| forwardBit;if (r.activity.mVisibleFromClient) {ViewManager wm = a.getWindowManager();View decor = r.window.getDecorView();wm.updateViewLayout(decor, l);}}r.activity.mVisibleFromServer = true;mNumVisibleActivities++;if (r.activity.mVisibleFromClient) {r.activity.makeVisible();}}r.nextIdle = mNewActivities;mNewActivities = r;if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);Looper.myQueue().addIdleHandler(new Idler());} else {// If an exception was thrown when trying to resume, then// just end this activity.try {ActivityManagerNative.getDefault().finishActivity(token, Activity.RESULT_CANCELED, null);} catch (RemoteException ex) {}}}
代码注释的1 、2、 3,我们看到了UI被丢到wm上了,但是wm是怎么和activity联系起来的?
每次写代码的时候,我们都看到setContentView这函数吧?
setContentView
/*** Set the activity content to an explicit view. This view is placed* directly into the activity's view hierarchy. It can itself be a complex* view hierarhcy.* * @param view The desired content to display.*/public void setContentView(View view) {getWindow().setContentView(view);}
查下Google官方文档,有这样的描述:
Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.
The framework will instantiate an implementation of this class on behalf of the application.
引用:https://developer.android.com/reference/android/view/Window
This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.). The ViewGroup subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.
引用:https://developer.android.com/reference/android/view/View
可以得知,view是在window上面的。按照前面的分析,它们关系目前长这样:
mInstrumentation.newActivity
前面我们见过这行代码,但是没有进去看看,现在我们再进去看看里面做了什么:
//通过反射创建的Activityactivity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
//frameworks/base/core/java/android/app/Instrumentation.java/*** Perform instantiation of an {@link Activity} object. This method is intended for use with* unit tests, such as android.test.ActivityUnitTestCase. The activity will be useable* locally but will be missing some of the linkages necessary for use within the sytem.* * @param clazz The Class of the desired Activity* @param context The base context for the activity to use* @param token The token for this activity to communicate with* @param application The application object (if any)* @param intent The intent that started this Activity* @param info ActivityInfo from the manifest* @param title The title, typically retrieved from the ActivityInfo record* @param parent The parent Activity (if any)* @param id The embedded Id (if any)* @param lastNonConfigurationInstance Arbitrary object that will be* available via {@link Activity#getLastNonConfigurationInstance()* Activity.getLastNonConfigurationInstance()}.* @return Returns the instantiated activity* @throws InstantiationException* @throws IllegalAccessException*/public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id,Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException {Activity activity = (Activity)clazz.newInstance();ActivityThread aThread = null;activity.attach(context, aThread, this, token, application, intent, info, title,parent, id, lastNonConfigurationInstance, new Configuration());return activity;}
这里又执行了Activity的attach,来看下做了啥:
//frameworks/base/core/java/android/app/Activity.java
final void attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, int ident,Application application, Intent intent, ActivityInfo info,CharSequence title, Activity parent, String id,Object lastNonConfigurationInstance,HashMap<String,Object> lastNonConfigurationChildInstances,Configuration config) {attachBaseContext(context);//利用PolicyManager创建window对象mWindow = PolicyManager.makeNewWindow(this);mWindow.setCallback(this);if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {mWindow.setSoftInputMode(info.softInputMode);}mUiThread = Thread.currentThread();mMainThread = aThread;mInstrumentation = instr;mToken = token;mIdent = ident;mApplication = application;mIntent = intent;mComponent = intent.getComponent();mActivityInfo = info;mTitle = title;mParent = parent;mEmbeddedID = id;mLastNonConfigurationInstance = lastNonConfigurationInstance;mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;//设置WindowManager对象mWindow.setWindowManager(null, mToken, mComponent.flattenToString());if (mParent != null) {mWindow.setContainer(mParent.getWindow());}mWindowManager = mWindow.getWindowManager();mCurrentConfig = config;}
看下这行代码 mWindow = PolicyManager.makeNewWindow(this);
new出phonewindow
/*** {@hide}*/
public final class PolicyManager {private static final String POLICY_IMPL_CLASS_NAME ="com.android.internal.policy.impl.Policy";private static final IPolicy sPolicy;static {// Pull in the actual implementation of the policy at run-timetry {Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);//sPolicy sPolicy = (IPolicy)policyClass.newInstance();} catch (ClassNotFoundException ex) {throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);} catch (InstantiationException ex) {throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);} catch (IllegalAccessException ex) {throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);}}// Cannot instantiate this classprivate PolicyManager() {}// The static methods to spawn new policy-specific objectspublic static Window makeNewWindow(Context context) {return sPolicy.makeNewWindow(context);}public static LayoutInflater makeNewLayoutInflater(Context context) {return sPolicy.makeNewLayoutInflater(context);}public static WindowManagerPolicy makeNewWindowManager() {return sPolicy.makeNewWindowManager();}public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {return sPolicy.makeNewFallbackEventHandler(context);}
}
看下sPolicy的实现类Policy :
/*** {@hide}*/
// Simple implementation of the policy interface that spawns the right
// set of objects
public class Policy implements IPolicy {private static final String TAG = "PhonePolicy";private static final String[] preload_classes = {"com.android.internal.policy.impl.PhoneLayoutInflater","com.android.internal.policy.impl.PhoneWindow","com.android.internal.policy.impl.PhoneWindow$1","com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback","com.android.internal.policy.impl.PhoneWindow$DecorView","com.android.internal.policy.impl.PhoneWindow$PanelFeatureState","com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",};static {// For performance reasons, preload some policy specific classes when// the policy gets loaded.//加载它们for (String s : preload_classes) {try {Class.forName(s);} catch (ClassNotFoundException ex) {Log.e(TAG, "Could not preload class for phone policy: " + s);}}}public PhoneWindow makeNewWindow(Context context) {//PhoneWindowreturn new PhoneWindow(context);}public PhoneLayoutInflater makeNewLayoutInflater(Context context) {return new PhoneLayoutInflater(context);}public PhoneWindowManager makeNewWindowManager() {return new PhoneWindowManager();}
}
到这里,我们得知 mWindow = PolicyManager.makeNewWindow(this);
执行后new出来的是个phonewindow。
WindowManager的创建
接着我们研究下windowmanager,回头看下 这行代码:mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
frameworks/base/core/java/android/view/Window.java/*** Set the window manager for use by this Window to, for example,* display panels. This is <em>not</em> used for displaying the* Window itself -- that must be done by the client.** @param wm The ViewManager for adding new windows.*/public void setWindowManager(WindowManager wm,IBinder appToken, String appName) {mAppToken = appToken;mAppName = appName;if (wm == null) {//拿到WindowManagerImplwm = WindowManagerImpl.getDefault();}//LocalWindowManagermWindowManager = new LocalWindowManager(wm);}
frameworks/base/core/java/android/view/Window.java
private class LocalWindowManager implements WindowManager {LocalWindowManager(WindowManager wm) {//传了WindowManager mWindowManager = wm;//拿到mDefaultDisplay mDefaultDisplay = mContext.getResources().getDefaultDisplay(mWindowManager.getDefaultDisplay());}public final void addView(View view, ViewGroup.LayoutParams params) {// Let this throw an exception on a bad params.WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;CharSequence curTitle = wp.getTitle();if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {if (wp.token == null) {View decor = peekDecorView();if (decor != null) {wp.token = decor.getWindowToken();}}if (curTitle == null || curTitle.length() == 0) {String title;if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) {title="Media";} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) {title="MediaOvr";} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {title="Panel";} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) {title="SubPanel";} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) {title="AtchDlg";} else {title=Integer.toString(wp.type);}if (mAppName != null) {title += ":" + mAppName;}wp.setTitle(title);}} else {if (wp.token == null) {wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;}if ((curTitle == null || curTitle.length() == 0)&& mAppName != null) {wp.setTitle(mAppName);}}if (wp.packageName == null) {wp.packageName = mContext.getPackageName();}mWindowManager.addView(view, params);}public void updateViewLayout(View view, ViewGroup.LayoutParams params) {mWindowManager.updateViewLayout(view, params);}public final void removeView(View view) {mWindowManager.removeView(view);}public final void removeViewImmediate(View view) {mWindowManager.removeViewImmediate(view);}public Display getDefaultDisplay() {return mDefaultDisplay;}private final WindowManager mWindowManager;private final Display mDefaultDisplay;}
构造函数中 mWindowManager = wm;
传递了mWindowManager ,它实际上是WindowManagerImpl。
frameworks/base/core/java/android/view/WindowManagerImpl.javapublic static WindowManagerImpl getDefault(){return mWindowManager;}
到这里,把分析的类的继承和关联关系整理下:
总结下:activity的mWindow成员变量真实类型是PhoneWindow,mWindowManager成员变量真实类型是LocalWindowManage。然后mWindowManager和LocalWindowManage都实现了WindowManager的接口。
LocalWindowManage把它的工作通过委托交给了WindowManagerImpl完成了。
回到setContextView
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java// This is the view in which the window contents are placed. It is either// mDecor itself, or a child of mDecor where the contents go.ViewGroup mContentParent;@Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window// decor, when theme attributes and the like are crystalized. Do not check the feature// before this happens.if (mContentParent == null) {//重点installDecor();} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {view.setLayoutParams(params);final Scene newScene = new Scene(mContentParent, view);transitionTo(newScene);} else {//把view添加到view groupmContentParent.addView(view, params);}mContentParent.requestApplyInsets();final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}mContentParentExplicitlySet = true;}
需要留意mContentParent是个viewgroup
private void installDecor() {mForceDecorInstall = false;if (mDecor == null) {//DecorView,继承于FrameLayoutmDecor = generateDecor(-1);mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);mDecor.setIsRootNamespace(true);if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);}} else {mDecor.setWindow(this);}if (mContentParent == null) {//生成viewgroupmContentParent = generateLayout(mDecor);// Set up decor part of UI to ignore fitsSystemWindows if appropriate.mDecor.makeFrameworkOptionalFitsSystemWindows();final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);if (decorContentParent != null) {mDecorContentParent = decorContentParent;mDecorContentParent.setWindowCallback(getCallback());if (mDecorContentParent.getTitle() == null) {mDecorContentParent.setWindowTitle(mTitle);}final int localFeatures = getLocalFeatures();for (int i = 0; i < FEATURE_MAX; i++) {if ((localFeatures & (1 << i)) != 0) {mDecorContentParent.initFeature(i);}}mDecorContentParent.setUiOptions(mUiOptions);if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||(mIconRes != 0 && !mDecorContentParent.hasIcon())) {mDecorContentParent.setIcon(mIconRes);} else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&mIconRes == 0 && !mDecorContentParent.hasIcon()) {mDecorContentParent.setIcon(getContext().getPackageManager().getDefaultActivityIcon());mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;}if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||(mLogoRes != 0 && !mDecorContentParent.hasLogo())) {mDecorContentParent.setLogo(mLogoRes);}// Invalidate if the panel menu hasn't been created before this.// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu// being called in the middle of onCreate or similar.// A pending invalidation will typically be resolved before the posted message// would run normally in order to satisfy instance state restoration.PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {invalidatePanelMenu(FEATURE_ACTION_BAR);}} else {//创建标题mTitleView = findViewById(R.id.title);if (mTitleView != null) {if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {final View titleContainer = findViewById(R.id.title_container);if (titleContainer != null) {titleContainer.setVisibility(View.GONE);} else {mTitleView.setVisibility(View.GONE);}mContentParent.setForeground(null);} else {mTitleView.setText(mTitle);}}}if (mDecor.getBackground() == null && mBackgroundFallbackDrawable != null) {mDecor.setBackgroundFallback(mBackgroundFallbackDrawable);}// Only inflate or create a new TransitionManager if the caller hasn't// already set a custom one.if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {if (mTransitionManager == null) {final int transitionRes = getWindowStyle().getResourceId(R.styleable.Window_windowContentTransitionManager,0);if (transitionRes != 0) {final TransitionInflater inflater = TransitionInflater.from(getContext());mTransitionManager = inflater.inflateTransitionManager(transitionRes,mContentParent);} else {mTransitionManager = new TransitionManager();}}mEnterTransition = getTransition(mEnterTransition, null,R.styleable.Window_windowEnterTransition);mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,R.styleable.Window_windowReturnTransition);mExitTransition = getTransition(mExitTransition, null,R.styleable.Window_windowExitTransition);mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,R.styleable.Window_windowReenterTransition);mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,R.styleable.Window_windowSharedElementEnterTransition);mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,USE_DEFAULT_TRANSITION,R.styleable.Window_windowSharedElementReturnTransition);mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,R.styleable.Window_windowSharedElementExitTransition);mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,USE_DEFAULT_TRANSITION,R.styleable.Window_windowSharedElementReenterTransition);if (mAllowEnterTransitionOverlap == null) {mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(R.styleable.Window_windowAllowEnterTransitionOverlap, true);}if (mAllowReturnTransitionOverlap == null) {mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(R.styleable.Window_windowAllowReturnTransitionOverlap, true);}if (mBackgroundFadeDurationMillis < 0) {mBackgroundFadeDurationMillis = getWindowStyle().getInteger(R.styleable.Window_windowTransitionBackgroundFadeDuration,DEFAULT_BACKGROUND_FADE_DURATION_MS);}if (mSharedElementsUseOverlay == null) {mSharedElementsUseOverlay = getWindowStyle().getBoolean(R.styleable.Window_windowSharedElementsUseOverlay, true);}}}}
先看下generateLayout:
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.javaprotected ViewGroup generateLayout(DecorView decor) {// Apply data from current theme.TypedArray a = getWindowStyle();if (false) {System.out.println("From style:");String s = "Attrs:";for (int i = 0; i < R.styleable.Window.length; i++) {s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="+ a.getString(i);}System.out.println(s);}mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)& (~getForcedWindowFlags());if (mIsFloating) {setLayout(WRAP_CONTENT, WRAP_CONTENT);setFlags(0, flagsToUpdate);} else {setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);getAttributes().setFitInsetsSides(0);getAttributes().setFitInsetsTypes(0);}if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {requestFeature(FEATURE_NO_TITLE);} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {// Don't allow an action bar if there is no title.requestFeature(FEATURE_ACTION_BAR);}if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {requestFeature(FEATURE_ACTION_BAR_OVERLAY);}if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {requestFeature(FEATURE_ACTION_MODE_OVERLAY);}if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,false)) {setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS& (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,false)) {setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION& (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB)) {setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));}a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()+ ", major: " + mMinWidthMajor.coerceToString());if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();a.getValue(R.styleable.Window_windowFixedWidthMajor,mFixedWidthMajor);}if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();a.getValue(R.styleable.Window_windowFixedWidthMinor,mFixedWidthMinor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();a.getValue(R.styleable.Window_windowFixedHeightMajor,mFixedHeightMajor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();a.getValue(R.styleable.Window_windowFixedHeightMinor,mFixedHeightMinor);}if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {requestFeature(FEATURE_CONTENT_TRANSITIONS);}if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {requestFeature(FEATURE_ACTIVITY_TRANSITIONS);}mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);final Context context = getContext();final int targetSdk = context.getApplicationInfo().targetSdkVersion;final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;if (!mForcedStatusBarColor) {mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);}if (!mForcedNavigationBarColor) {mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,0x00000000);}if (!targetPreQ) {mEnsureStatusBarContrastWhenTransparent = a.getBoolean(R.styleable.Window_enforceStatusBarContrast, false);mEnsureNavigationBarContrastWhenTransparent = a.getBoolean(R.styleable.Window_enforceNavigationBarContrast, true);}WindowManager.LayoutParams params = getAttributes();// Non-floating windows on high end devices must put up decor beneath the system bars and// therefore must know about visibility changes of those.if (!mIsFloating) {if (!targetPreL && a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds,false)) {setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());}if (mDecor.mForceWindowDrawsBarBackgrounds) {params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;}params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;}final int sysUiVis = decor.getSystemUiVisibility();final int statusLightFlag = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;final int statusFlag = a.getBoolean(R.styleable.Window_windowLightStatusBar, false)? statusLightFlag : 0;final int navLightFlag = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;final int navFlag = a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)? navLightFlag : 0;decor.setSystemUiVisibility((sysUiVis & ~(statusLightFlag | navLightFlag)) | (statusFlag | navFlag));if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT|| mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "+ a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));}params.layoutInDisplayCutoutMode = mode;}if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB) {if (a.getBoolean(R.styleable.Window_windowCloseOnTouchOutside,false)) {setCloseOnTouchOutsideIfNotSet(true);}}if (!hasSoftInputMode()) {params.softInputMode = a.getInt(R.styleable.Window_windowSoftInputMode,params.softInputMode);}if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,mIsFloating)) {/* All dialogs should have the window dimmed */if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;}if (!haveDimAmount()) {params.dimAmount = a.getFloat(android.R.styleable.Window_backgroundDimAmount, 0.5f);}}if (a.getBoolean(R.styleable.Window_windowBlurBehindEnabled, false)) {if ((getForcedWindowFlags() & WindowManager.LayoutParams.FLAG_BLUR_BEHIND) == 0) {params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;}params.setBlurBehindRadius(a.getDimensionPixelSize(android.R.styleable.Window_windowBlurBehindRadius, 0));}setBackgroundBlurRadius(a.getDimensionPixelSize(R.styleable.Window_windowBackgroundBlurRadius, 0));if (params.windowAnimations == 0) {params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);}// The rest are only done if this window is not embedded; otherwise,// the values are inherited from our container.if (getContainer() == null) {if (mBackgroundDrawable == null) {if (mFrameResource == 0) {mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);}if (a.hasValue(R.styleable.Window_windowBackground)) {mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground);}}if (a.hasValue(R.styleable.Window_windowBackgroundFallback)) {mBackgroundFallbackDrawable =a.getDrawable(R.styleable.Window_windowBackgroundFallback);}if (mLoadElevation) {mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);}mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);}// Inflate the window decor.int layoutResource;int features = getLocalFeatures();// System.out.println("Features: 0x" + Integer.toHexString(features));if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {if (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogTitleIconsDecorLayout, res, true);layoutResource = res.resourceId;} else {layoutResource = R.layout.screen_title_icons;}// XXX Remove this once action bar supports these features.removeFeature(FEATURE_ACTION_BAR);// System.out.println("Title Icons!");} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {// Special case for a window with only a progress bar (and title).// XXX Need to have a no-title version of embedded windows.layoutResource = R.layout.screen_progress;// System.out.println("Progress!");} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {// Special case for a window with a custom title.// If the window is floating, we need a dialog layoutif (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogCustomTitleDecorLayout, res, true);layoutResource = res.resourceId;} else {layoutResource = R.layout.screen_custom_title;}// XXX Remove this once action bar supports these features.removeFeature(FEATURE_ACTION_BAR);} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {// If no other features and not embedded, only need a title.// If the window is floating, we need a dialog layoutif (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogTitleDecorLayout, res, true);layoutResource = res.resourceId;} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {layoutResource = a.getResourceId(R.styleable.Window_windowActionBarFullscreenDecorLayout,R.layout.screen_action_bar);} else {layoutResource = R.layout.screen_title;}// System.out.println("Title!");} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {layoutResource = R.layout.screen_simple_overlay_action_mode;} else {// Embedded, so no decoration is needed.layoutResource = R.layout.screen_simple;// System.out.println("Simple!");}mDecor.startChanging();mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);if (contentParent == null) {throw new RuntimeException("Window couldn't find content container view");}if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {ProgressBar progress = getCircularProgressBar(false);if (progress != null) {progress.setIndeterminate(true);}}// Remaining setup -- of background and title -- that only applies// to top-level windows.if (getContainer() == null) {mDecor.setWindowBackground(mBackgroundDrawable);final Drawable frame;if (mFrameResource != 0) {frame = getContext().getDrawable(mFrameResource);} else {frame = null;}mDecor.setWindowFrame(frame);mDecor.setElevation(mElevation);mDecor.setClipToOutline(mClipToOutline);if (mTitle != null) {setTitle(mTitle);}if (mTitleColor == 0) {mTitleColor = mTextColor;}setTitleColor(mTitleColor);}mDecor.finishChanging();return contentParent;}
一系列的操作,创建了viewgroup。
findViewById
frameworks/base/core/java/android/view/Window.java/*** Finds a view that was identified by the {@code android:id} XML attribute* that was processed in {@link android.app.Activity#onCreate}.* <p>* This will implicitly call {@link #getDecorView} with all of the associated side-effects.* <p>* <strong>Note:</strong> In most cases -- depending on compiler support --* the resulting view is automatically cast to the target class type. If* the target class type is unconstrained, an explicit cast may be* necessary.** @param id the ID to search for* @return a view with given ID if found, or {@code null} otherwise* @see View#findViewById(int)* @see Window#requireViewById(int)*/@Nullablepublic <T extends View> T findViewById(@IdRes int id) {return getDecorView().findViewById(id);}
到这里,我们对AndroidUI体系的理解又加深了:
addView
有了前面的研究“成果”,再来看下addView的实现:
frameworks/base/core/java/android/view/WindowManagerImpl.javaprivate void addView(View view, ViewGroup.LayoutParams params, boolean nest){if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);if (!(params instanceof WindowManager.LayoutParams)) {throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");}final WindowManager.LayoutParams wparams= (WindowManager.LayoutParams)params;//ViewRootViewRoot root;View panelParentView = null;synchronized (this) {// Here's an odd/questionable case: if someone tries to add a// view multiple times, then we simply bump up a nesting count// and they need to remove the view the corresponding number of// times to have it actually removed from the window manager.// This is useful specifically for the notification manager,// which can continually add/remove the same view as a// notification gets updated.int index = findViewLocked(view, false);if (index >= 0) {if (!nest) {throw new IllegalStateException("View " + view+ " has already been added to the window manager.");}root = mRoots[index];root.mAddNesting++;// Update layout parameters.view.setLayoutParams(wparams);root.setLayoutParams(wparams, true);return;}// If this is a panel window, then find the window it is being// attached to for future reference.if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {final int count = mViews != null ? mViews.length : 0;for (int i=0; i<count; i++) {if (mRoots[i].mWindow.asBinder() == wparams.token) {panelParentView = mViews[i];}}}//new 了 ViewRootroot = new ViewRoot(view.getContext());root.mAddNesting = 1;view.setLayoutParams(wparams);if (mViews == null) {index = 1;mViews = new View[1];mRoots = new ViewRoot[1];mParams = new WindowManager.LayoutParams[1];} else {index = mViews.length + 1;Object[] old = mViews;mViews = new View[index];System.arraycopy(old, 0, mViews, 0, index-1);old = mRoots;mRoots = new ViewRoot[index];System.arraycopy(old, 0, mRoots, 0, index-1);old = mParams;mParams = new WindowManager.LayoutParams[index];System.arraycopy(old, 0, mParams, 0, index-1);}index--;mViews[index] = view;mRoots[index] = root;mParams[index] = wparams;}// do this last because it fires off messages to start doing things//把decorView设置进去给viewRootroot.setView(view, wparams, panelParentView);}
ViewRoot
/*** The top of a view hierarchy, implementing the needed protocol between View* and the WindowManager. This is for the most part an internal implementation* detail of {@link WindowManagerImpl}.** {@hide}*/
@SuppressWarnings({"EmptyCatchBlock"})
public final class ViewRoot extends Handler implements ViewParent,View.AttachInfo.Callbacks {private static final String TAG = "ViewRoot";private static final boolean DBG = false;private static final boolean SHOW_FPS = false;@SuppressWarnings({"ConstantConditionalExpression"})private static final boolean LOCAL_LOGV = false ? Config.LOGD : Config.LOGV;/** @noinspection PointlessBooleanExpression*/private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV;private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;private static final boolean DEBUG_IMF = false || LOCAL_LOGV;private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;private static final boolean WATCH_POINTER = false;private static final boolean MEASURE_LATENCY = false;private static LatencyTimer lt;/*** Maximum time we allow the user to roll the trackball enough to generate* a key event, before resetting the counters.*/static final int MAX_TRACKBALL_DELAY = 250;static long sInstanceCount = 0;static IWindowSession sWindowSession;static final Object mStaticInit = new Object();static boolean mInitialized = false;static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();static boolean sFirstDrawComplete = false;static final ArrayList<ComponentCallbacks> sConfigCallbacks= new ArrayList<ComponentCallbacks>();private static int sDrawTime;long mLastTrackballTime = 0;final TrackballAxis mTrackballAxisX = new TrackballAxis();final TrackballAxis mTrackballAxisY = new TrackballAxis();final int[] mTmpLocation = new int[2];final InputMethodCallback mInputMethodCallback;final SparseArray<Object> mPendingEvents = new SparseArray<Object>();int mPendingEventSeq = 0;final Thread mThread;final WindowLeaked mLocation;final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//重点final W mWindow;View mView;View mFocusedView;View mRealFocusedView; // this is not set to null in touch modeint mViewVisibility;boolean mAppVisible = true;final Region mTransparentRegion;final Region mPreviousTransparentRegion;int mWidth;int mHeight;Rect mDirty; // will be a graphics.Region soonboolean mIsAnimating;CompatibilityInfo.Translator mTranslator;final View.AttachInfo mAttachInfo;final Rect mTempRect; // used in the transaction to not thrash the heap.final Rect mVisRect; // used to retrieve visible rect of focused view.boolean mTraversalScheduled;boolean mWillDrawSoon;boolean mLayoutRequested;boolean mFirst;boolean mReportNextDraw;boolean mFullRedrawNeeded;boolean mNewSurfaceNeeded;boolean mHasHadWindowFocus;boolean mLastWasImTarget;boolean mWindowAttributesChanged = false;// These can be accessed by any thread, must be protected with a lock.// Surface can never be reassigned or cleared (use Surface.clear()).//重点private final Surface mSurface = new Surface();boolean mAdded;boolean mAddedTouchMode;/*package*/ int mAddNesting;// These are accessed by multiple threads.final Rect mWinFrame; // frame given by window manager.final Rect mPendingVisibleInsets = new Rect();final Rect mPendingContentInsets = new Rect();final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets= new ViewTreeObserver.InternalInsetsInfo();final Configuration mLastConfiguration = new Configuration();final Configuration mPendingConfiguration = new Configuration();class ResizedInfo {Rect coveredInsets;Rect visibleInsets;Configuration newConfig;}boolean mScrollMayChange;int mSoftInputMode;View mLastScrolledFocus;int mScrollY;int mCurScrollY;Scroller mScroller;EGL10 mEgl;EGLDisplay mEglDisplay;EGLContext mEglContext;EGLSurface mEglSurface;GL11 mGL;Canvas mGlCanvas;boolean mUseGL;boolean mGlWanted;final ViewConfiguration mViewConfiguration;/*** see {@link #playSoundEffect(int)}*/AudioManager mAudioManager;private final int mDensity;}
ViewRoot继承于Handler。有个成员变量,叫做mSurface,有个W类型的mWindow,他其实和Binder通信有关。还有个View类型的mView。
Android在哪里“画画”
我们前面分析到的,是UI控件,但是还没看到作画的画布在哪。篇幅原因,另开一篇。