Android系统触屏事件传递派发浅析(二)

上一篇文章提到在InputDispatcher中,connection调用inputPublisher.publishMotionEvent后分发就完成了,然后将dispatchEntry放入到waitQueue队列,大概是有的事件必须等应用回复,收到应用回复后,dispatchEntry将从waitQueue队列中移出.

要弄明白事件如何传递给应用,不得不看看inputPublisher.publishMotionEvent这个函数了.
在InputTransport.cpp文件中看看函数的实现(省略了参数和赋值)

status_t InputPublisher::publishMotionEvent() {
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_MOTION;
    msg.body.motion.seq = seq;
    return mChannel->sendMessage(&msg);
}

功能就是实例化InputMessage,调用InputChannel的sendMessage方法.
InputChannel::sendMessage方法很简单,就是调用socket 的send方法发送InputMessage.有sendMessage 当然也有receiveMessage用来接收用户回复,这里略去不表,至此InputDispatcher的戏份全部完成.

上一文章末尾提到在WindowManagerService中有这么一个方法

public int addWindow() {
// ...
String name = win.makeInputChannelName();

InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);

win.setInputChannel(inputChannels[0]);     inputChannels[1].transferTo(outInputChannel);             mInputManager.registerInputChannel(win.mInputChannel, 
win.mInputWindowHandle);
// ...
 }

这里的win是WindowState类的实例,通过调用makeInputChannelName返回一个String 类型name.然后调用openInputChannelPair返回一对InputChannel.上面提到InputChannel就是通过socket发送和接收数据. 之后InputDispatcher和应用就是通过这对InputChannel通信的.

顺便看看本地代码中openInputChannelPair的实现

status_t InputChannel::openInputChannelPair(const String8& name,
    sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
    status_t result = -errno;
    ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
            name.string(), errno);
    outServerChannel.clear();
    outClientChannel.clear();
    return result;
}   

int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);

String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK; 
}

是创建了一对UNIX域套接字.
再看看Java中openInputChannelPair的实现

 public static InputChannel[] openInputChannelPair(String name) {
    if (name == null) {
        throw new IllegalArgumentException("name must not be null");
    }

    if (DEBUG) {
        Slog.d(TAG, "Opening input channel pair '" + name + "'");
    }
    return nativeOpenInputChannelPair(name);
}

调用的是nativeOpenInputChannelPair.

看看nativeOpenInputChannelPair

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
    jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
String8 name(nameChars);
env->ReleaseStringUTFChars(nameObj, nameChars);

sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

if (result) {
    String8 message;
    message.appendFormat("Could not open input channel pair.  status=%d", result);
    jniThrowRuntimeException(env, message.string());
    return NULL;
}   

jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if (env->ExceptionCheck()) {
    return NULL;
}   

jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
        new NativeInputChannel(serverChannel));
if (env->ExceptionCheck()) {
    return NULL;
}   

jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
        new NativeInputChannel(clientChannel));
if (env->ExceptionCheck()) {
    return NULL;
}   

env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}

其实调用的还是本地代码中的openInputChannelPair.至此我们明白了在WindowManagerService中addWindow方法会创建一对InputChannel(本质是socket), 一个自己保存,一个传给InputDispatcher,至于怎么传递给InputDispatcher有时间再跟进去看看.

要理清应用如何与InputDispatcher通信,打开应用的入口类ActivityThread.java文件,我们看看performLaunchActivity这个方法.

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                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();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        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);
        }
    }
     activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.voiceInteractor);
}

这里只粘贴了代码的一部分,大概功能是通过反射机制创建一个用户activity的实例.然后调用activity的attach方法,让用户activity和ActivityThread关联起来.

打开Activity.java文件,看看attach方法

 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, IVoiceInteractor voiceInteractor) {
    attachBaseContext(context);

    mFragments.attachActivity(this, mContainer, null);

    mWindow = PolicyManager.makeNewWindow(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    mUiThread = Thread.currentThread();

    mMainThread = aThread;
    mInstrumentation = instr;

// ...

}

继续跟下去没有发现间接调用addWindow方法.也许是延迟创建窗口造成的.看看ActivityThread的handleResumeActivity方法

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;

    // TODO Push resumeArgs into the activity for consideration
    ActivityClientRecord 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();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            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;
                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;
        }

        // Get rid of anything left hanging around.
        cleanUpPendingRemoveWindows(r);

        // ...

看这两行 
a.mWindowAdded = true; 
wm.addView(decor, l); 
感觉是和addWindow相似,也没有其他看起来像是加载窗口的.先跟进去看看吧.ViewManager是个接口,WindowManager继承ViewManager接口,wm是Activity getWindowManager返回的, 往上跟, 其实是mWindow.getWindowManager返回的.只好看看mWindow的来头.

Activity.java attach()方法中知道了mWindow的来头

mWindow = PolicyManager.makeNewWindow(this)

PolicyManager.java 文件关于makeNewWindow()方法

Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);

public static Window makeNewWindow(Context context) {
    return sPolicy.makeNewWindow(context);
}   

利用java反射机制,创建一个sPolicy实例.
找到这个Policy类,看看makeNewWindow方法

  public Window makeNewWindow(Context context) {
      return new PhoneWindow(context);
  }

进入PhoneWindow.java文件,可是没有找到getWindowManager的实现.PhoneWindow继承Window, 进Window.java看看.
找到了WindowManager相关的函数

  if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

WindowManagerImpl.java文件中

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {

60 return new WindowManagerImpl(mDisplay, parentWindow);
61 }

调用的比较深,总结起来就是ActivityThread类里,前面handleResumeActivity方法里提到的 wm.addView()方法其实就是调用WindowManagerImpl类的addView方法.
看看WindowManagerImpl类的addView方法

 @Override
  public void addView(View view, ViewGroup.LayoutParams params) {
      mGlobal.addView(view, params, mDisplay, mParentWindow);
 }

mGlobal这个实例是这么来的

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

又需要去WindowManagerGlobal里面去看看了.
找到addView方法,看看实现.里面会实例化一个ViewRootImpl类.
这里不再跟了,简单介绍一个ViewRootImpl.
每一个Activity都会有一个Window,每一个Window都会有一个ViewRootImpl.

在WindowManagerGlobal里面,会维护一个ViewRootImpl列表和View列表.ViewRootImpl列表里每一个ViewRootImpl用来实现Activity和WindowManagerService通信,View列表里每一个View是Activity的根View.

ViewRootImpl setView方法部分代码

            requestLayout();
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { 
                mInputChannel = new InputChannel();
            }
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mInputChannel);
            } 

构建一个InputChannel实例,然后调用mWindowSession.addToDisplay. mWindowSession看起来像是WindowManagerService 在ViewRootImpl端的代理, mWindowSession调用的方法实际上就是调用WindowManagerService端对应的方法. 可在WindowManagerService中没有找到addToDisplay这个方法.后来仔细查看,发现不是想当然的.
看看windowSession是如何得到的

InputMethodManager imm = InputMethodManager.getInstance();
                IWindowManager windowManager = getWindowManagerService();
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {
                                ValueAnimator.setDurationScale(scale);
                            }
                        },
                        imm.getClient(), imm.getInputContext());

windowSession是通过WindowManagerService的openSession方法得到的.
看看这个方法

  public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
        IInputContext inputContext) {
    if (client == null) throw new IllegalArgumentException("null client");
    if (inputContext == null) throw new IllegalArgumentException("null inputContext");
    Session session = new Session(this, callback, client, inputContext);
    return session;
}

原来mWindowSession是一个Session实例,而不是想当然的WindowManagerService.看看Session类的addToDisplay方法

 @Override
 public int addToDisplay(IWindow window, 
     int seq,
     WindowManager.LayoutParams attrs,
     int viewVisibility, 
     int displayId, 
     Rect outContentInsets,
     InputChannel outInputChannel) {
     return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outInputChannel);
 }

构造方法中

 public Session(WindowManagerService service, IWindowSessionCallback callback,
          IInputMethodClient client, IInputContext inputContext) {
      mService = service;
      mCallback = callback;
      mClient = client;

    // ...          

}

mService就是WindowManagerService,所以ViewRootImpl调用addToDisplay最终调到WindowManagerService的addWindow方法.上一篇文章最后提到这个,至此,事件的获取,加工,到派送给应用全部通了.当然这只是理通了事件的大概流向,目前只是明白了每一个Activity通过ViewRootImpl一个socket对与InputDispatcher通信,具体事件如何传递给应用,有时间再跟进去分析.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值