Activity、Window和View的关系源码分析

Activity、Window和View的关系

文章目录

  • Activity、Window和View的关系
    • Activity的创建
    • Window的创建
    • View的创建
      • WindowManager的addView
        • mWindowSession.addToDisplay
      • 触摸事件是怎么传递到Activity的
    • 总结

要说它们之间的关系,就要从Activity的创建说起

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

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只是负责处理一些应用窗口通用的逻辑(如标题栏,导航栏等)。
上面流程:

  • 将ContextImpl赋值给mBase对象
  • 创建了PhoneWindow并设置了回调
  • 将WindowManagerImpl设置进PhoneWindow中

View的创建

在开发时设置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。
上面主要流程:

  • Activity的setContentView 调用 PhoneWindow的setContentView
  • 如果没有创建mContentParent,则创建出DecorView和mContentParent,
  • 创建DecorView时传入PhoneWindow将Window和DecorView关联上
  • 创建mContentParent时,从DecorView中查找到id为R.id.content的View

由此可以推断出目前的一个关系

Activity、Window和View的关系源码分析_第1张图片

到目前,DecorView和Activity并没有建立联系,Activity间接通过PhoneWindow调用mDecor。也不知道DecorView是何时被绘制到屏幕上的。

WindowManager的addView

在我们最早接触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的作用是:

  • DecorView渲染绘制到屏幕上显示
  • DecorView可以接受屏幕触摸事件

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);
}


上面流程:

  • 在接收到WMS消息之前,先完成View的测量、布局和绘制操作
  • 通过mWindowSession的addToDisplay方法将View添加到WMS中

来看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的流程

  • ViewRootImpl构造函数中为mWindowSession赋值
  • 然后这个值又是WindowManagerGlobal的getWindowSession()
  • sWindowSession是一个单例,创建时先获取WMS代理对象windowManager,然后通过windowManager的openSession远程调用WMS获取到sWindowSession
  • 而sWindowSession在WMS中返回的是创建的Session

mWindowSession.addToDisplay

@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

触摸事件是怎么传递到Activity的

在上面的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);
}


上面流程:

  • 在ViewRootImpl的setView时调用添加接受触摸事件的管道拦截器
  • 当触摸事件传入,会到达ViewPostImeInputStage#onProcess方法中处理,触摸事件会调用processPointerEvent
  • 之后会传入DecorView的processPointerEvent,然后通过window的callback回调传入Activity
  • 传递到Activity后,会调用getWindow().superDispatchTouchEvent(ev)传递到PhoneWindow中
  • 然后PhoneWindow中调用mDecor.superDispatchTouchEvent(event)传递到了DecorView
  • 然后依照ViewGroup的事件传递机制层层传递

总结

通过Activity创建、setContentView的流程,来分析了Activity、Window、View之间关系,并分析了他们之间事件传递的路径

最后在简单的总结几点

  • 一个Activity有一个PhoneWindow,在PhoneWindow中有一个DecorView,我们添加的view是放在DecorView的ContentParent中
  • 一个进程仅有一个WindowManagerGlobal
  • 一个PhoneWindow对应一个ViewRootImpl对象
  • WindowManagerGlobal通过ViewRootImpl的setView方法完成WMS的添加过程。以及输入事件管道的过程
  • ViewRootImpl的setView方法主要完成:View的渲染事件和接受触摸事件

你可能感兴趣的:(android,android源码解析,android)