Android创建窗口机制,请看如下转载:
http://blog.youkuaiyun.com/sfdev/article/details/9130527
一、Android4.2系统服务侧——与View关系
1.服务端channel注册过程
frameworks/base/core/java/android/view/ViewRootImpl.java
- public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
- mInputChannel = new InputChannel(); //创建InputChannel
- res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
- getHostVisibility(), mDisplay.getDisplayId(),
- mAttachInfo.mContentInsets, mInputChannel); //创建与上述InputChannel对应的通道至服务端
- /*
- mWindowSession = WindowManagerGlobal.getWindowSession(context.getMainLooper());
- frameworks/base/core/java/android/view/WindowManagerGlobal.java
- public static IWindowSession getWindowSession(Looper mainLooper) {
- IWindowManager windowManager = getWindowManagerService();
- sWindowSession = windowManager.openSession(
- imm.getClient(), imm.getInputContext());
- return sWindowSession;
- }
- frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
- public IWindowSession openSession(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, client, inputContext);
- return session;
- }
- */
- mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
- Looper.myLooper()); //将本通道注册进InputEventReceiver
- }
- 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 int addWindow(Session session, IWindow client, int seq,
- WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
- Rect outContentInsets, InputChannel outInputChannel) {
- //以下包括了管道的创建(用于WMS与应用程序View通信)等
- String name = win.makeInputChannelName();
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- win.setInputChannel(inputChannels[0]);
- inputChannels[1].transferTo(outInputChannel);
- //以下便是注册至server端过程
- //final InputManagerService mInputManager;
- mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
- }
- public void registerInputChannel(InputChannel inputChannel,
- InputWindowHandle inputWindowHandle) {
- nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
- }
- private static native void nativeRegisterInputChannel(int ptr, InputChannel inputChannel,
- InputWindowHandle inputWindowHandle, boolean monitor);
- static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
- jint ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- status_t status = im->registerInputChannel(
- env, inputChannel, inputWindowHandle, monitor);
- }
- status_t NativeInputManager::registerInputChannel(JNIEnv* env,
- const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
- return mInputManager->getDispatcher()->registerInputChannel(
- inputChannel, inputWindowHandle, monitor);
- //mInputManager = new InputManager(eventHub, this, this);
- /*
- frameworks/base/services/input/InputManager.cpp
- sp<InputDispatcherInterface> InputManager::getDispatcher() {
- return mDispatcher;
- }
- mDispatcher = new InputDispatcher(dispatcherPolicy);
- */
- }
frameworks/base/services/input/InputDispatcher.cpp
- status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
- int fd = inputChannel->getFd();
- mConnectionsByFd.add(fd, connection);
- //该fd监听对应的处理函数为handleReceiveCallback
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
- }
2.1.InputReaderThread线程从驱动读取数据并处理,如实现鼠标右键上报back键即在此处完成、以下代码将会看到
frameworks/base/services/input/InputReader.cpp
- bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
- }
- void InputReader::loopOnce() {
- size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
- /*
- frameworks/base/services/input/EventHub.cpp
- size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
- int32_t readSize = read(device->fd, readBuffer,
- sizeof(struct input_event) * capacity);//从驱动读取事件
- }
- */
- processEventsLocked(mEventBuffer, count);
- }
- void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
- processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
- }
- void InputReader::processEventsForDeviceLocked(int32_t deviceId,
- const RawEvent* rawEvents, size_t count) {
- device->process(rawEvents, count);
- }
- void InputDevice::process(const RawEvent* rawEvents, size_t count) {
- //该设备的所有mapper进行处理;注意:这里使用了多态
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->process(rawEvent);
- }
- }
- //以下就是各个mapper
- //CursorInput鼠标设备
- void CursorInputMapper::process(const RawEvent* rawEvent) {
- mCursorButtonAccumulator.process(rawEvent);
- mCursorMotionAccumulator.process(rawEvent);
- mCursorScrollAccumulator.process(rawEvent);
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
- }
- //CursorButtonAccumulator::process(const RawEvent* rawEvent)
- //CursorMotionAccumulator::process(const RawEvent* rawEvent)
- //CursorScrollAccumulator::process(const RawEvent* rawEvent)
- void CursorInputMapper::sync(nsecs_t when) {
- int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
- /*
- uint32_t CursorButtonAccumulator::getButtonState() const {
- if (mBtnRight) {
- //Changed by tank for mouse left button to back
- result |= AMOTION_EVENT_BUTTON_BACK;
- // result |= AMOTION_EVENT_BUTTON_SECONDARY;
- }
- if (mBtnMiddle) {
- //change by tank@tcl.com for mouse middle button to menu
- result |= AMOTION_EVENT_BUTTON_MENU;
- //result |= AMOTION_EVENT_BUTTON_TERTIARY;
- }
- }
- */
- getListener()->notifyMotion(&args);
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
- policyFlags, lastButtonState, currentButtonState);
- /*
- static void synthesizeButtonKeys(InputReaderContext* context, int32_t action,
- nsecs_t when, int32_t deviceId, uint32_t source,
- uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) {
- synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
- lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
- synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
- lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
- //add by tank mouse key event middle->menu.
- synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
- lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_MENU, AKEYCODE_MENU);
- //end tank
- }
- static void synthesizeButtonKey(InputReaderContext* context, int32_t action,
- nsecs_t when, int32_t deviceId, uint32_t source,
- uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState,
- int32_t buttonState, int32_t keyCode) {
- if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState)
- && (currentButtonState & buttonState))
- || (action == AKEY_EVENT_ACTION_UP
- && (lastButtonState & buttonState)
- && !(currentButtonState & buttonState))) {
- context->getListener()->notifyKey(&args);
- }
- }
- */
- }
- //TouchInput触摸板设备
- void SingleTouchInputMapper::process(const RawEvent* rawEvent)
- TouchInputMapper::process(rawEvent);
- mSingleTouchMotionAccumulator.process(rawEvent);
- }
- //SingleTouchMotionAccumulator::process(const RawEvent* rawEvent)
- void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
- TouchInputMapper::process(rawEvent);
- mMultiTouchMotionAccumulator.process(rawEvent);
- }
- //MultiTouchMotionAccumulator::process(const RawEvent* rawEvent)
- void TouchInputMapper::process(const RawEvent* rawEvent) {
- mCursorButtonAccumulator.process(rawEvent);
- mCursorScrollAccumulator.process(rawEvent);
- mTouchButtonAccumulator.process(rawEvent);
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
- }
- }
- //TouchButtonAccumulator::process(const RawEvent* rawEvent)
- void TouchInputMapper::sync(nsecs_t when) {
- dispatchTouches(when, policyFlags);
- }
- void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
- dispatchMotion(when, policyFlags, mSource,
- AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- mCurrentCookedPointerData.pointerProperties,
- mCurrentCookedPointerData.pointerCoords,
- mCurrentCookedPointerData.idToIndex,
- currentIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime);
- }
- void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
- int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
- const PointerProperties* properties, const PointerCoords* coords,
- const uint32_t* idToIndex, BitSet32 idBits,
- int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) {
- getListener()->notifyMotion(&args);
- }
- //SwitchInput设备
- void SwitchInputMapper::process(const RawEvent* rawEvent) {
- sync(rawEvent->when);
- }
- void SwitchInputMapper::sync(nsecs_t when) {
- getListener()->notifySwitch(&args);
- }
- //JoystickInput游戏手柄设备
- void JoystickInputMapper::process(const RawEvent* rawEvent) {
- sync(rawEvent->when, false /*force*/);
- }
- void JoystickInputMapper::sync(nsecs_t when, bool force) {
- getListener()->notifyMotion(&args);
- }
- //KeyboardInput按键设备
- void KeyboardInputMapper::process(const RawEvent* rawEvent) {
- processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
- }
- void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
- int32_t scanCode, uint32_t policyFlags) {
- getListener()->notifyKey(&args);
- }
2.2.InputReaderThread线程对系统层按键做处理(比较重要的是POWER键,最终在PhoneWindowManager中的interceptKeyBeforeQueueing和interceptMotionBeforeQueueingWhenScreenOff)后分发给InputDispatcherThread线程,以下分析将看到之前一个鼠标操作过程中无法待机的问题解决
以下几种情况都会唤醒InputDispatcherThread线程,即调用mLooper->wake()唤醒正在awoken()中的InputReaderThread线程:
frameworks/base/services/input/InputDispatcher.cpp
- //有新输入设备注册等
- void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
- ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(args->eventTime);
- needWake = enqueueInboundEventLocked(newEntry);
- if (needWake) {
- mLooper->wake();
- }
- }
- //分发按键事件
- void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
- //说明:PhoneWindowManager.java中policyFlags位决定系统按键(如HOME等是否需要由系统处理)
- mPolicy->interceptKeyBeforeQueueing(&event, policyFlags);
- //以下分析将看到,该调用实际是在PhoneWindowManager.java中实现
- /*
- frameworks/base/services/input/InputManager.cpp
- InputManager::InputManager(
- const sp<EventHubInterface>& eventHub,
- const sp<InputReaderPolicyInterface>& readerPolicy,
- const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
- mDispatcher = new InputDispatcher(dispatcherPolicy);
- mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
- }
- frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
- NativeInputManager::NativeInputManager(jobject contextObj,
- jobject serviceObj, const sp<Looper>& looper) :
- mLooper(looper) {
- mInputManager = new InputManager(eventHub, this, this);
- }
- void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
- uint32_t& policyFlags) {
- wmActions = env->CallIntMethod(mServiceObj,
- gServiceClassInfo.interceptKeyBeforeQueueing,
- keyEventObj, policyFlags, isScreenOn);
- //如下函数中将有待机和开机的处理
- handleInterceptActions(wmActions, when, policyFlags);
- }
- frameworks/base/service/java/com/android/server/input/InputManagerService.java
- private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
- return mWindowManagerCallbacks.interceptKeyBeforeQueueing(
- event, policyFlags, isScreenOn);
- }
- frameworks/base/service/java/com/android/server/SystemServer.java
- inputManager = new InputManagerService(context, wmHandler);
- wm = WindowManagerService.main(context, power, display, inputManager,
- uiHandler, wmHandler,
- factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
- !firstBoot, onlyCore);
- inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
- frameworks/base/service/java/com/android/server/wm/WindowManagerService.java
- public InputMonitor getInputMonitor() {
- return mInputMonitor;
- }
- frameworks/base/service/java/com/android/server/wm/InputMonitor.java
- public int interceptKeyBeforeQueueing(
- KeyEvent event, int policyFlags, boolean isScreenOn) {
- return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
- }
- public InputMonitor(WindowManagerService service) {
- mService = service;
- }
- frameworks/base/service/java/com/android/server/wm/WindowManagerService.java
- final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
- frameworks/base/core/java/com/android/internal/policy/PolicyManager.java
- public static WindowManagerPolicy makeNewWindowManager() {
- return sPolicy.makeNewWindowManager();
- }
- private static final String POLICY_IMPL_CLASS_NAME =
- "com.android.internal.policy.impl.Policy";
- Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
- sPolicy = (IPolicy)policyClass.newInstance();
- frameworks/base/core/java/com/android/internal/policy/Policy.java
- package com.android.internal.policy.impl;
- public class Policy implements IPolicy {
- public WindowManagerPolicy makeNewWindowManager() {
- return new PhoneWindowManager();
- }
- }
- frameworks/base/core/java/com/android/internal/policy/PhoneWindowManager.java
- public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
- case KeyEvent.KEYCODE_POWER: {
- result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
- }
- }
- */
- KeyEntry* newEntry = new KeyEntry(args->eventTime,
- args->deviceId, args->source, policyFlags,
- args->action, flags, args->keyCode, args->scanCode,
- metaState, repeatCount, args->downTime);
- needWake = enqueueInboundEventLocked(newEntry);
- if (needWake) {
- mLooper->wake();
- }
- }
- //分发Motion事件
- void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
- mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
- /*
- 如上分析,不再累赘;该接口是:
- frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
- void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
- jint wmActions = env->CallIntMethod(mServiceObj,
- gServiceClassInfo.interceptMotionBeforeQueueingWhenScreenOff,
- policyFlags);
- handleInterceptActions(wmActions, when, policyFlags);
- }
- 如上interceptMotionBeforeQueueingWhenScreenOff在PhoneWindowManager中实现;分析同上,不再累赘:
- frameworks/base/core/java/com/android/internal/policy/PhoneWindowManager.java
- public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
- //result |= ACTION_WAKE_UP;
- //add by tank
- result = result & (~ACTION_WAKE_UP);
- //end tank
- return result;
- }
- 看看handleInterceptActions函数:
- void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
- uint32_t& policyFlags) {
- //接上边PhoneWindowManager中interceptKeyBeforeQueueing对于power键的返回值可知,系统将待机
- if (wmActions & WM_ACTION_GO_TO_SLEEP) {
- #if DEBUG_INPUT_DISPATCHER_POLICY
- ALOGD("handleInterceptActions: Going to sleep.");
- #endif
- android_server_PowerManagerService_goToSleep(when);
- }
- //以下说明PhoneWindowManager中interceptMotionBeforeQueueingWhenScreenOff返回值WM_ACTION_WAKE_UP将会导致唤醒
- //当然,是可是收到motion事件的前提下
- if (wmActions & WM_ACTION_WAKE_UP) {
- #if DEBUG_INPUT_DISPATCHER_POLICY
- ALOGD("handleInterceptActions: Waking up.");
- #endif
- android_server_PowerManagerService_wakeUp(when);
- }
- //以下是可以上报给系统的
- if (wmActions & WM_ACTION_PASS_TO_USER) {
- policyFlags |= POLICY_FLAG_PASS_TO_USER;
- }
- }
- */
- MotionEntry* newEntry = new MotionEntry(args->eventTime,
- args->deviceId, args->source, policyFlags,
- args->action, args->flags, args->metaState, args->buttonState,
- args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
- args->displayId,
- args->pointerCount, args->pointerProperties, args->pointerCoords);
- needWake = enqueueInboundEventLocked(newEntry);
- if (needWake) {
- mLooper->wake();
- }
- }
- //设备重置
- void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- DeviceResetEntry* newEntry = new DeviceResetEntry(args->eventTime, args->deviceId);
- needWake = enqueueInboundEventLocked(newEntry);
- if (needWake) {
- mLooper->wake();
- }
- }
- //C层的按键注入接口
- int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
- uint32_t policyFlags) {
- needWake |= enqueueInboundEventLocked(entry);
- if (needWake) {
- mLooper->wake();
- }
- }
- //setInputWindows
- //setFocusedApplication
- //setInputDispatchMode
- //setInputFilterEnabled
- //transferTouchFocus
- //registerInputChannel
- //unregisterInputChannel
- //monitor
2.3.InputDispatcherThread线程处理,根据PhoneWindowManager中的interceptKeyBeforeDispatching决定是否丢弃按键
InputDispatcherThread线程被唤醒
- bool InputDispatcherThread::threadLoop() {
- mDispatcher->dispatchOnce();
- return true;
- }
- void InputDispatcher::dispatchOnce() {
- dispatchOnceInnerLocked(&nextWakeupTime);
- mLooper->pollOnce(timeoutMillis);
- }
- void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
- if (!mPolicy->isKeyRepeatEnabled()) {
- resetKeyRepeatLocked();
- }
- switch (mPendingEvent->type) {
- case EventEntry::TYPE_CONFIGURATION_CHANGED: {
- done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
- }
- case EventEntry::TYPE_DEVICE_RESET: {
- done = dispatchDeviceResetLocked(currentTime, typedEntry);
- }
- case EventEntry::TYPE_KEY: {
- done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
- }
- case EventEntry::TYPE_MOTION: {
- done = dispatchMotionLocked(currentTime, typedEntry,
- &dropReason, nextWakeupTime);
- }
- }
- dropInboundEventLocked(mPendingEvent, dropReason); //丢弃的事件!!!!
- }
- bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) {
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
- /*
- void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
- CommandEntry* commandEntry) {
- //说明:PhoneWindowManager.java中可以截断事件而不上报,即返回-1、将被丢弃
- nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
- &event, entry->policyFlags);
- if (delay < 0) {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
- } else if (!delay) {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
- } else {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
- entry->interceptKeyWakeupTime = now() + delay;
- }
- }
- */
- else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
- if (*dropReason == DROP_REASON_NOT_DROPPED) {
- *dropReason = DROP_REASON_POLICY; //dropReason是因为策略丢弃
- }
- }
- if (*dropReason != DROP_REASON_NOT_DROPPED) {
- setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
- ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
- return true;
- }
- dispatchEventLocked(currentTime, entry, inputTargets);
- }
- bool InputDispatcher::dispatchMotionLocked(
- nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
- dispatchEventLocked(currentTime, entry, inputTargets);
- }
2.4.InputDispatcherThread线程分发给应用程序进程
在这里解决了up事件上报两次的问题!!!!!!
frameworks/base/services/input/InputDispatcher.cpp
- void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
- EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
- pokeUserActivityLocked(eventEntry); //和Activity相关,后边三中有设备删除的分析;基本同下
- ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
- sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
- prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
- }
- void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
- enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
- }
- void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); //将按键注入队列
- /*
- void InputDispatcher::enqueueDispatchEntryLocked(
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
- int32_t dispatchMode) {
- DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
- inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->scaleFactor);
- if (!connection->inputState.trackKey(keyEntry,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags) || (dispatchEntry->resolvedFlags == 0x28)){
- //add by tankai 0x28
- delete dispatchEntry;
- return;
- }
- }
- */
- //dropInboundEventLocked
- //synthesizeCancelationEventsForAllConnectionsLocked->
- //synthesizeCancelationEventsForConnectionLocked->
- /*
- void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
- const sp<Connection>& connection, const CancelationOptions& options) {
- Vector<EventEntry*> cancelationEvents;
- connection->inputState.synthesizeCancelationEvents(currentTime,
- cancelationEvents, options);
- //关键在这里,mKeyMementos;在enqueueDispatchEntryLocked时调用trackKey由addKeyMemento注入!!!!!!
- if (!cancelationEvents.isEmpty()) {
- enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
- &target, InputTarget::FLAG_DISPATCH_AS_IS);
- }
- }
- */
- //enqueueDispatchEntriesLocked,注入了0x28标志的按键
- startDispatchCycleLocked(currentTime, connection);
- }
- void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
- switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
- status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
- keyEntry->deviceId, keyEntry->source,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
- keyEntry->keyCode, keyEntry->scanCode,
- keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
- keyEntry->eventTime);
- }
- case EventEntry::TYPE_MOTION: {
- status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
- motionEntry->deviceId, motionEntry->source,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
- motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
- xOffset, yOffset,
- motionEntry->xPrecision, motionEntry->yPrecision,
- motionEntry->downTime, motionEntry->eventTime,
- motionEntry->pointerCount, motionEntry->pointerProperties,
- usingCoords);
- }
- }
- }
- status_t InputPublisher::publishKeyEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
- return mChannel->sendMessage(&msg);
- }
- status_t InputChannel::sendMessage(const InputMessage* msg) {
- do {
- nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
- } while (nWrite == -1 && errno == EINTR);
- }
二、Android4.2系统应用程序侧——与View关系
InputManagerService也就是InputDispatcher与应用程序通信是靠looper。
说明:
InputReader从设备文件中读取的是RawEvent,在交给InputDispatcher进行分发之前,它需要先把RawEvent进行转化分类,拆分成KeyEvent、MotionEvent、TrackEvent各种类型等。
InputDispatcher获得按键事件后,根据当前设备的状况来优先消化事件(该过程交由PhoneWindowManager.java来处理);最后,剩余事件分发给ViewRoot;ViewRoot再分发给IME输入法或View、Activity。
1.应用程序View中channel注册过程
frameworks/base/core/java/android/view/ViewRootImpl.java
- public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
- mInputChannel = new InputChannel(); //创建InputChannel
- res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
- getHostVisibility(), mDisplay.getDisplayId(),
- mAttachInfo.mContentInsets, mInputChannel); //创建与上述InputChannel对应的通道至服务端
- mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
- Looper.myLooper()); //将本通道注册进InputEventReceiver
- }
- final class WindowInputEventReceiver extends InputEventReceiver {
- public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
- @Override
- public void onInputEvent(InputEvent event) {
- enqueueInputEvent(event, this, 0, true);
- }
- }
frameworks/base/core/java/android/view/InputEventReceiver.java
- public InputEventReceiver(InputChannel inputChannel, Looper looper) {
- mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue);
- }
- private static native int nativeInit(InputEventReceiver receiver,
- InputChannel inputChannel, MessageQueue messageQueue);
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
- static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
- jobject inputChannelObj, jobject messageQueueObj) {
- sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
- receiverObj, inputChannel, messageQueue);
- status_t status = receiver->initialize();
- }
- status_t NativeInputEventReceiver::initialize() {
- int receiveFd = mInputConsumer.getChannel()->getFd();
- mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
- return OK;
- }
frameworks/native/libs/utils/Looper.cpp
- int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
- request.callback = callback;
- }
2.应用程序View响应过程
frameworks/native/libs/utils/Looper.cpp
- int Looper::pollInner(int timeoutMillis) {
- awoken(); //阻塞,等待
- int callbackResult = response.request.callback->handleEvent(fd, events, data);
- }
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
- int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
- status_t status = consumeEvents(env, false /*consumeBatches*/, -1);
- }
- status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
- bool consumeBatches, nsecs_t frameTime) {
- env->CallVoidMethod(mReceiverObjGlobal,
- gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
- }
- private void dispatchInputEvent(int seq, InputEvent event) {
- mSeqMap.put(event.getSequenceNumber(), seq);
- onInputEvent(event);
- }
- final class WindowInputEventReceiver extends InputEventReceiver {
- public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
- @Override
- public void onInputEvent(InputEvent event) {
- enqueueInputEvent(event, this, 0, true);
- }
- }
- void enqueueInputEvent(InputEvent event,
- InputEventReceiver receiver, int flags, boolean processImmediately) {
- scheduleProcessInputEvents();
- }
/
有关handler机制请看下文:
http://blog.youkuaiyun.com/itachi85/article/details/8035333
- final ViewRootHandler mHandler = new ViewRootHandler();
- private void scheduleProcessInputEvents() {
- Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
- mHandler.sendMessage(msg);
- }
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_PROCESS_INPUT_EVENTS:
- doProcessInputEvents();
- }
- }
///
- void doProcessInputEvents() {
- deliverInputEvent(q);
- }
- private void deliverInputEvent(QueuedInputEvent q) {
- deliverKeyEvent(q);
- deliverPointerEvent(q);
- deliverTrackballEvent(q);
- deliverGenericMotionEvent(q);
- }
- private void deliverKeyEvent(QueuedInputEvent q) {
- imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback); //分发给输入法
- deliverKeyEventPostIme(q);//分发给View
- /*
- private void deliverKeyEventPostIme(QueuedInputEvent q) {
- mView.dispatchKeyEvent(event)
- }
- */
- }
- private void deliverPointerEvent(QueuedInputEvent q) {
- boolean handled = mView.dispatchPointerEvent(event); //分发给View
- }
- private void deliverTrackballEvent(QueuedInputEvent q) {
- imm.dispatchTrackballEvent(mView.getContext(), seq, event,
- mInputMethodCallback); //分发给输入法
- deliverTrackballEventPostIme(q); //分发给View
- /*
- private void deliverTrackballEventPostIme(QueuedInputEvent q) {
- mView.dispatchTrackballEvent(event)
- }
- */
- }
- private void deliverGenericMotionEvent(QueuedInputEvent q) {
- imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
- mInputMethodCallback); //分发给输入法
- deliverGenericMotionEventPostIme(q); //分发给View
- /*
- private void deliverGenericMotionEventPostIme(QueuedInputEvent q) {
- updateJoystickDirection(event, false); //游戏手柄的摇杆就是在这处理
- mView.dispatchGenericMotionEvent(event)
- }
- */
- }
分发给应用程序Activity:
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
- private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
- public boolean dispatchKeyEvent(KeyEvent event) {
- final Callback cb = getCallback();
- //cb为应用程序MainActivity
- final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event) : super.dispatchKeyEvent(event);
- //给应用程序Activity的dispatchKeyEvent处理或交给View的dispatchKeyEvent
- }
- }
而上述应用程序中的dispatchKeyEvent一般会调用其父类的该方法,例如:
packages/apps/Launcher2/src/com/android/launcher2/Launcher.java
- public boolean dispatchKeyEvent(KeyEvent event) {
- return super.dispatchKeyEvent(event);
- }
应用程序Activity在分发给与之关联的某个View,如果这个View没有处理、最终交给该Activity自己处理。
应用程序有关View的设置:
- private Dialog mMenuWin;
- mMenuWin = new Dialog(aActivity, R.style.CameraDialog);
- mMenuWin.setContentView(mMenuLayout);
- mMenuWin.setOnClickListener(); //鼠标单击
- mMenuWin.setOnLongClickListener(); //
- mMenuWin.setOnTouchListener(); //触摸板
- mMenuWin.setOnKeyListener(new OnKeyListener() {
- public boolean onKey(); //按键
- public void onClick(View v); //鼠标单击
- }
frameworks/base/core/java/android/app/Activity.java
- public boolean dispatchKeyEvent(KeyEvent event) {
- onUserInteraction();
- Window win = getWindow();
- if (win.superDispatchKeyEvent(event)) { //首先由Window消化,即如果View消化了、则Activity将不在回调onKeyDown
- return true;
- }
- View decor = mDecor; //如果没被消化,会调用Activity的onKeyDown
- if (decor == null) decor = win.getDecorView();
- return event.dispatch(this, decor != null ? decor.getKeyDispatcherState() : null, this);
- }
- }
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
- public class PhoneWindow extends Window implements MenuBuilder.Callback {
- public boolean superDispatchKeyEvent(KeyEvent event) {
- return mDecor.superDispatchKeyEvent(event);
- }
- }
- private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
- public boolean superDispatchKeyEvent(KeyEvent event) {
- super.dispatchKeyEvent(event)
- }
- }
- public boolean dispatchKeyEvent(KeyEvent event) {
- mInputEventConsistencyVerifier.onKeyEvent(event, 1);
- super.dispatchKeyEvent(event)
- }
frameworks/base/core/java/android/view/View.java
- public boolean dispatchKeyEvent(KeyEvent event) {
- li.mOnKeyListener.onKey(this, event.getKeyCode(), event); //回调应用程序View相应方法
- event.dispatch(this, mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null, this)
- /*
- frameworks/base/core/java/android/view/KeyEvent.java
- public final boolean dispatch(Callback receiver, DispatcherState state,
- Object target) {
- //按键响应
- boolean res = receiver.onKeyDown(mKeyCode, this); //应用程序回调函数
- }
- */
- }
- public final boolean dispatchPointerEvent(MotionEvent event) {
- if (event.isTouchEvent()) {
- return dispatchTouchEvent(event);
- } else {
- return dispatchGenericMotionEvent(event);
- }
- }
- public boolean dispatchTouchEvent(MotionEvent event) {
- //触摸板响应
- li.mOnTouchListener.onTouch(this, event) //应用程序继承OnTouchListener,实现的回调接口
- //鼠标左键响应
- onTouchEvent(event)
- /*
- public boolean onTouchEvent(MotionEvent event) {
- performClick();
- //该接口调用li.mOnClickListener.onClick(this);为应用程序继承OnClickListener的回调函数
- }
- */
- }
dispatchGenericMotionEvent
dispatchTrackballEvent
dispatchConfigurationChanged //添加或删除键盘设备Activity重启,见http://blog.youkuaiyun.com/tankai19880619/article/details/16805401
三、Input设备与Activity关系
1.InputReaderThread线程检测到设备插入删除
frameworks/base/service/input/InputReader.cpp
- void InputReader::loopOnce() {
- size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
- /*
- frameworks/base/services/input/EventHub.cpp
- size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
- int32_t readSize = read(device->fd, readBuffer,
- sizeof(struct input_event) * capacity);//从驱动读取事件
- }
- */
- processEventsLocked(mEventBuffer, count);
- }
- void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
- case EventHubInterface::FINISHED_DEVICE_SCAN:
- handleConfigurationChangedLocked(rawEvent->when);
- }
- void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
- updateGlobalMetaStateLocked();
- // Enqueue configuration changed.
- NotifyConfigurationChangedArgs args(when);
- mQueuedListener->notifyConfigurationChanged(&args);
- }
说明:有的平台需要在接入硬件键盘时Activity不需要刷新;可以在上处做屏蔽:
- // add by tank
- // do not send configuration change
- //NotifyConfigurationChangedArgs args(when);
- //mQueuedListener->notifyConfigurationChanged(&args);
- // end tank
2.InputReaderThread线程分发给InputDispatcherThread线程
frameworks/base/service/input/InputDispatcher.cpp
- void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
- needWake = enqueueInboundEventLocked(newEntry);
- if (needWake) {
- mLooper->wake();
- }
- }
frameworks/base/service/input/InputDispatcher.cpp
- bool InputDispatcherThread::threadLoop() {
- mDispatcher->dispatchOnce();
- return true;
- }
- void InputDispatcher::dispatchOnce() {
- dispatchOnceInnerLocked(&nextWakeupTime);
- }
- void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
- case EventEntry::TYPE_CONFIGURATION_CHANGED: {
- ConfigurationChangedEntry* typedEntry =
- static_cast<ConfigurationChangedEntry*>(mPendingEvent);
- done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
- }
- }
- bool InputDispatcher::dispatchConfigurationChangedLocked(
- nsecs_t currentTime, ConfigurationChangedEntry* entry) {
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyConfigurationChangedInterruptible);
- }
- void InputDispatcher::doNotifyConfigurationChangedInterruptible(
- CommandEntry* commandEntry) {
- mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
- }
frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
- void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
- env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyConfigurationChanged, when);
- }
- private void notifyConfigurationChanged(long whenNanos) {
- mWindowManagerCallbacks.notifyConfigurationChanged();
- }
frameworks/base/service/java/com/android/server/wm/InputMonitor.java
- public void notifyConfigurationChanged() {
- mService.sendNewConfiguration();
- }
- void sendNewConfiguration() {
- mActivityManager.updateConfiguration(null);
- /*
- mActivityManager = ActivityManagerNative.getDefault();
- frameworks/base/core/java/android/app/ActivityManagerNative.java
- static public IActivityManager getDefault() {
- return gDefault.get();
- }
- private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
- IBinder b = ServiceManager.getService("activity");
- IActivityManager am = asInterface(b);
- return am;
- }
- frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
- public static void setSystemProcess() {
- ActivityManagerService m = mSelf;
- ServiceManager.addService("activity", m, true);
- }
- */
- }
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
- public void updateConfiguration(Configuration values) {
- updateConfigurationLocked(values, null, false, false);
- }
- boolean updateConfigurationLocked(Configuration values,
- ActivityRecord starting, boolean persistent, boolean initLocale) {
- kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
- public void setWindowManager(WindowManagerService wm) {
- mWindowManager = wm;
- }
- }
- final boolean ensureActivityConfigurationLocked(ActivityRecord r,
- int globalChanges) {
- //一般会重启Activity
- if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) {
- relaunchActivityLocked(r, r.configChangeFlags, false);
- return false;
- }
- //应用程序AndroidMenifest中写标记将不会重启
- r.app.thread.scheduleActivityConfigurationChanged(r.appToken);
- }
- public void scheduleActivityConfigurationChanged(IBinder token) {
- queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);
- }
- //消息循环同上,不再分析
- public void handleMessage(Message msg) {
- case ACTIVITY_CONFIGURATION_CHANGED:
- handleActivityConfigurationChanged((IBinder)msg.obj);
- }
- final void handleActivityConfigurationChanged(IBinder token) {
- performConfigurationChanged(r.activity, mCompatConfiguration);
- }
- private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {
- cb.onConfigurationChanged(config); //回调Activity类的onConfigurationChanged方法
- }
四、项目问题
1.resumeTopActivity时的Activity重启。
http://blog.youkuaiyun.com/jivin_shen/article/details/6839175
操作逻辑:打开Launcher界面下的一个应用(比如播放器),完后接入USB键盘;之后退出该应用,也就是resumeTopActivity到Launcher时也引发了config配置更新导致的Activity重启。
原理以及解决部分:
frameworks/base/services/java/com/android/server/am/ActivityStack.java
- final boolean resumeTopActivityLocked(ActivityRecord prev) {
- return resumeTopActivityLocked(prev, null);
- }
- final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
- Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
- mService.mConfiguration,
- next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
- }
frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
- public Configuration updateOrientationFromAppTokens(
- Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
- config = updateOrientationFromAppTokensLocked(currentConfig,
- freezeThisOneIfNeeded);
- }
- private Configuration updateOrientationFromAppTokensLocked(
- Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
- computeScreenConfigurationLocked(mTempConfiguration)
- }
- boolean computeScreenConfigurationLocked(Configuration config) {
- if ((sources & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) {
- //change by tank
- config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
- //config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
- //end tank
- }
- else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
- && config.navigation == Configuration.NAVIGATION_NONAV) {
- //change by tank
- //config.navigation = Configuration.NAVIGATION_DPAD;
- //navigationPresence |= presenceFlag;
- //end tank
- }
- if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
- //change by tank
- //config.keyboard = Configuration.KEYBOARD_QWERTY;
- //keyboardPresence |= presenceFlag;
- //end tank
- }
- }
2.面板设备与虚拟驱动导致的up上报两次:
drop类按键
down或up:
dispatchOnceInnerLocked>
dropInboundEventLocked>synthesizeCancelationEventsForAllConnectionsLocked-synthesizeCancelationEventsForConnectionLocked>inputState.synthesizeCancelationEvents->mKeyMementos.itemAt(i),最后上报系统(synthesizeCancelationEventsForConnectionLocked调用enqueueDispatchEntryLocked)
非drop类按键
down:
dispatchOnceInnerLocked->
dispatchKeyLocked->dispatchEventLocked->prepareDispatchCycleLocked->enqueueDispatchEntriesLocked->enqueueDispatchEntryLocked->InputState::trackKey->addKeyMemento //只在down时保存对up的处理
问题:
面板down->drop
虚拟down->非drop,保存up
面板down->drop,将虚拟保存的up送上去
虚拟up->非drop,直接上报
结果——两个虚拟的up
修改方法:
frameworks/base/service/input/InputDispatcher.cpp
- void InputDispatcher::enqueueDispatchEntryLocked(
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
- int32_t dispatchMode)
- {
- if (!connection->inputState.trackKey(keyEntry,
- dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)/*add by tank@tcl.com end */ || (dispatchEntry->resolvedFlags == 0x28))
- {
- #if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
- connection->getInputChannelName());
- #endif
- delete dispatchEntry;
- return; // skip the inconsistent event
- }
- /*
- //add by tankai
- if(dispatchEntry->resolvedFlags == 0x28 && keyEntry->deviceId == 3){
- ALOGD("TK--------->>>delete sim KeyMementos up\n");
- delete dispatchEntry;
- return; // skip the inconsistent event
- }
- //end tankai
- */
- }
3.焦点request错误导致不能响应按键
正确调用:setFocusable(true)和requestFocus()重新获取焦点
错误调用:setFocusable(false)和requestFocus()
系统侧为该应用tv.huan.deezer强制修改:
frameworks/base/core/java/android/view/View.java
- public final boolean requestFocus() {
- Log.d("TKTK","TK---->>>View.java>>>>requestFocus()");//add by tank
- if(SystemProperties.get("sys.user.camera",null).equals("tv.huan.deezer"))
- {
- setFocusable(true);
- }
- //end tank
- return requestFocus(View.FOCUS_DOWN);
- }