Android - DecorView、Activity、Window事件分发流程

339 阅读4分钟
Android如何监听屏幕触摸事件?

通过SystemServer中的InputManagerService做监听,其内部InputReader线程负责读事件,InputDispatcher线程负责分发事件

Activity、Window、DecorView谁先得到事件?

准确来说,是ViewRootImpl先得到事件,然后分发给DecorView,分发流程ViewRootImpl->DecorView->Activity->Window->DecorView

ViewRootImpl如何得到事件呢?

这里涉及到ViewRootImpl的构建以及注册;ActivityThread.handleResumeActivity发起绘制任务,就包括ViewRootImpl的构建以及注册;

//ActivityThread类中
 @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
            unscheduleGcIdler();
           ......       
          ActivityClientRecord r = performResumeActivity(token, clearHide);
         ......       
         if (a.mVisibleFromClient) {
              a.mWindowAdded = true;
               wm.addView(decor, l);//wm是WindowManageImpl类型,addView调用下面的函数
           }
           ......       
    }


//WindowManageImpl类中
 @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//mGlobal是WindowManagerGlobal类型,addView调用下面的函数
    }

//WindowManagerGlobal类中
 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ......
        ViewRootImpl root;.
        ......
        root = new ViewRootImpl(view.getContext(), display);//创建ViewRootImpl
        ......
        root.setView(view, wparams, panelParentView);//setView调用下面的函数
        ......
    }
   
   //ViewRootImpl类中
     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
          ......
          //创建InputChannel 
          mInputChannel = new InputChannel(); 
          //通过Binder进入systemserver进程
          res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);//1
          ......
    }
  • 先捋顺调用,ActivityThread.handleResumeActivity->WindowManageImpl.addView->WindowManagerGlobal.addView->ViewRootImpl.addiew;
  • WindowManagerGlobal负责创建ViewRootImpl以及维护ViewRootImpl;
  • 注释1:创建mInputChannel注册到SystemServer,mInputChannel是接收触摸事件的通道,这里相当于把ViewRootImpl注册到SystemServer
ViewRootImpl如何分发事件?

mInputChannel接收到触摸事件,会通过ViewRootImpl分发给DecorView,而DecorView.dispatchTouchEvent就是事件分发的入口;

//DecorView类中
@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);//1
}


//Activity类中
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();//这里可以重写onUserInteraction,处理down事件
    }
    if (getWindow().superDispatchTouchEvent(ev)) {//2
        return true;
    }
    return onTouchEvent(ev);
}

//PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);//3
}

//DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);//4
}
  • 注释1: DecorView.dispatchTouchEvent是分发的入口,cb属性这里对应Activity实例,因为Activity实现了对应的接口,cb.dispatchTouchEvent(ev)分发到Activity;
  • 注释2: getWindow()就是PhoneWindow,所以分发到PhoneWindow.superDispatchTouchEvent;
  • 注释3: mDecor就是DecorView,这里又回调了DecorView;
  • 注释4: DecorView的父类是FrameLayout,所以从这里开始就是我们常说的事件分发;
触摸事件分发流程

1.png

ViewRootImpl、DecorView、Activity、Window关系如何建立

DecorView、Activity、Window建立关系

我们知道ActivityThread.performLaunchActivity负责创建Activity,同时也建立三者的联系;

//ActivithThread类中
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ......
    Activity activity = null;
    ......

    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);//反射创建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);//调用下面attch函数
    ......
    if (r.isPersistable()) {//2
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    return activity;
}

//Activity类中
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) {

    ......
    mWindow = new PhoneWindow(this);//1
    ......
}

  • 注释1:创建PhoneWindow并赋值给mWindow属性,Activity持有PhoneWindow属性
  • 注释2:这是Widnow与DecorView建立联系的关键,无论判断条件是什么,最终都是触发Activity.onCreate函数,在这里我们会做一件很重要的事件,就是setContentView
//Activity类中
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);//调用下面的setContentView
    ......
}

//PhoneWindow类中
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor();//调用下面的installDecor
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    ......
}
//PhoneWindow类中
private void installDecor() {
    mDecor = generateDecor();//调用下面的generateDecor
    ......
}
//PhoneWindow类中
protected DecorView generateDecor() {
    return new DecorView(getContext(), -1);//1
}
  • 注释1:创建DecorView,最后赋值给mDecor,所以PhoneWindow持有DecorView

ViewRootImpl、DecorView建立关系

ActivityThread.handleResumeActivity是创建ViewRootImpl的入口;

//ActivityThread类中
final void handleResumeActivity(IBinder token,
                                boolean clearHide, boolean isForward, boolean reallyResume) {
    ......
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();//获取PhoneWindow的decorView
        ......
        if (a.mVisibleFromClient) {
            a.mWindowAdded = true;
            wm.addView(decor, l);//wm是类型是WindowManagerImpl,所以调用下面的addView
        }
    }
    ......
}

//WindowManagerImpl类中
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);//mGlobal是WindowManagerGlobal单例,所以调用下面的addView
}


//WindowManagerGlobal类中
public void addView(View view, ViewGroup.LayoutParams params,
                    Display display, Window parentWindow) {

    ViewRootImpl root;
    root = new ViewRootImpl(view.getContext(), display);//1
    ......
    root.setView(view, wparams, panelParentView);//2
}
  • 注释1:创建ViewRootImpl对象;
  • 注释2:这里的view就是DecorView,通过setView,ViewRootImpl持有DecorView对象
为什么不从ViewRootImpl直接分发给DecorView呢?

我理解ViewRootImpl是事件的接收者,它不负责事件的具体分发,Activity作为容器组件,负责处理事件分发以及兜底处理,当没有任何View消费事件,事件最后会回到Activity中;Activity不知道DecorView的存在,所以需要把事件分发给Window;这样做也是一个分层的思想。

ViewRootImpl、DecorView、Activity、Window作用
  • ViewRootImpl,负责触发绘制以及分发触摸事件;
  • DecorView,负责标题、背景的处理;
  • Window,是对DecorView的管理,理解对应一个窗口,WMS管理的就是Window;
  • Activity,提供生命周期模板,让开发者只关注setContentView的内容;

以上分析有不对的地方,请指出,互相学习,谢谢哦!