要说它们之间的关系,就要从Activity的创建说起
在这里我们暂时不分析具体的startActivity启动过程,直接看创建Activity的部分,调用startActivity新启动了一个Activity会走到ActivityThread里的performLaunchActivity()方法
//ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
···
//创建ContextImpl,然后创建Activity
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
···
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
···
//首先如果没有创建Application,会先创建Application
Application app = r.packageInfo.makeApplication(falsemInstrumentation);
if (activity != null) {
//调用Activity的attach
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
}
···
}
···
上面主要是创建出了Activity并调用了Activity的attach方法,而在attach()方法中会创建Window
//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,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
//将ContextImpl赋值给mBase对象
attachBaseContext(context);
//创建出PhoneWindow,并设置Activity实现的Callback接口,Callback接口主要回调输入事件、窗口改变等
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setCallback(this);
//将WindowManagerImpl设置进PhoneWindow中
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
}
PhoneWindow只是负责处理一些应用窗口通用的逻辑(如标题栏,导航栏等)。
上面流程:
在开发时设置layout界面,使用Activity的setContentView方法设置一个View进去,系统自动帮我们处理好。仔细分析一下这里应该就是创建View的地方,下面从Activity的setContentView来分析View的创建
//Activity.java
public void setContentView(@LayoutRes int layoutResID) {
//这里的getWindow对应的就是PhoneWindow
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
Activity的setContentView实际上是交给PhoneWindow来执行的
//PhoneWindow.java
@Override
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);
} else {
//将layout插入到mContentParent中,即我们设置的View都是mContentParent的子View
mLayoutInflater.inflate(layoutResID, mContentParent);
}
}
private void installDecor() {
//创建DecorView
if (mDecor == null) {
mDecor = generateDecor(-1);
···
}
//创建mContentParent
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
protected DecorView generateDecor(int featureId) {
//这个this就是PhoneWindow
return new DecorView(context, featureId, this, getAttributes());
}
protected ViewGroup generateLayout(DecorView decor) {
···
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
···
}
@Nullable
public <T extends View> T findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
在setContentView后,先判断是否创建了mContenParent和DecorView,如果没有先创建。
接下来将要设置的View加入到mContentParent,作为子View。
上面主要流程:
由此可以推断出目前的一个关系
到目前,DecorView和Activity并没有建立联系,Activity间接通过PhoneWindow调用mDecor。也不知道DecorView是何时被绘制到屏幕上的。
在我们最早接触Android时,分析Activity生命周期,在OnResume()后,界面对于用户可见。造成这样的原因是,在创建Activity后,会执行attach()->onCreate() 都是在准备需要显示的数据,而在onResume阶段才会将PhoneWindow中的DecorView绘制到界面上。
在ActivityThread的handleResumeActivity中会调用WindowManager的addView方法将DecorView添加到WindowManagerService中,
//ActivityThread.java
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest,boolean isForward,String reason) {
···
//其中回到用Activity的OnResume方法
final ActivityClientRecord r = performResumeActivity(tokefinalStateRequest, reason);
···
ViewManager wm = a.getWindowManager();
···
//Activity对于用户可见且window没有添加过,则调用wm.addView(decor, l);
//否则调用Activity的onWindowAttributesChanged
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
}
WindowManager的addView的作用是:
Activity的getWindowManager实际调用的是WindowManagerImpl对象。
调用路径是Activity#getWindowManager() -> Window#getWindowManager()
拿到的就是Activity中attach方法设置WindowManagerImpl对象
接下来看WindowManagerImpl的addView方法
//WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParamsparams) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
//WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
ViewRootImpl root;
//创建了ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
//触发启动操作
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
WindowManagerGlobal是一个进程单例对象。上面这段的核心就是创建ViewRootImpl,然后调用它的setView方法。
//ViewRootImpl.java
//只有1个子View
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//在添加到窗口管理器之前安排第一个布局
//确保从我们的系统接收任何其他事件之前进行重新布局measure -> layout -> draw。
requestLayout();
//mWindowSession是和WMS交互的代理对象,将View将调到WMS中
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
}
上面流程:
来看mWindowSession是一个什么类型的对象
//ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
mWindowSession = WindowManagerGlobal.getWindowSession();
}
//WindowManagerGlobal.java
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
//模拟传统行为。
//在此处实例化了InputMethodManager的全局实例
InputMethodManagerensureDefaultInstanceForDefaultDisplayIfNecessary();
//获取WMS的Proxy代理对象
IWindowManager windowManager = getWindowManagerService();
//sWindowSession是一个IWindowSession的Binder代理对象,调用WMS的openSession获取IWindowSession,真正实现的是系统服务的Session对象
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
//WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback,IInputMethodClient client,
IInputContext inputContext) {
//实际操作的Session对象,创建Session对象,传入了WMS和回调等
Session session = new Session(this, callback, client, inputContext);
return session;
}
上面获取IWindowSession的流程
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
}
在这一步就将Window成功传递到了WMS
在上面的ViewRootImpl的setView方法中还有接受触摸事件的操作
//ViewRootImpl.java
//只有1个子View
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//在添加到窗口管理器之前安排第一个布局
//确保从我们的系统接收任何其他事件之前进行重新布局measure -> layout -> draw。
requestLayout();
//mWindowSession是和WMS交互的代理对象,将View将调到WMS中
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
// 设置输入管道,使用的责任链模式,依次接受事件
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
}
final class ViewPostImeInputStage extends InputStage {
public ViewPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
//触摸事件调用
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
···
//mView是DecorView,而dispatchPointerEvent是在View中,DecorView是View的子类
boolean handled = mView.dispatchPointerEvent(event);
···
mAttachInfo.mHandlingPointerEvent = false;
···
return handled ? FINISH_HANDLED : FORWARD;
}
}
//View.java
@UnsupportedAppUsage
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
//DecorView.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
cb是在Activity#attach时,创建PhoneWindow后setCallback(this)的,而传入的是当前Activity,所以实际调用的就是Activity的dispatchTouchEvent,因此触摸事件就传递到了Activity中
//Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
//PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
//DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
上面流程:
通过Activity创建、setContentView的流程,来分析了Activity、Window、View之间关系,并分析了他们之间事件传递的路径
最后在简单的总结几点