在开发者选项中,打开 “显示点按操作反馈” 开关,当我们在触摸屏幕时,会显示一个小圆点,来分析下小圆点的显示流程。
操作这个开关时,其实就是操作Settings数据库中的 SHOW_TOUCHES
//packages\apps\Settings\src\com\android\settings\development\ShowTapsPreferenceController.java
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean isEnabled = (Boolean) newValue;
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SHOW_TOUCHES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
return true;
}
而在IMS中,会对 SHOW_TOUCHES 这个key进行监听
//frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
private void registerShowTouchesSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateShowTouchesFromSettings();
}
}, UserHandle.USER_ALL);
}
当SHOW_TOUCHES 这个key的值有改变时,调用updateShowTouchesFromSettings方法,在updateShowTouchesFromSettings方法中,是调用nativeSetShowTouches这个native方法,直接来看下这个方法
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeSetShowTouches(JNIEnv* /* env */,
jclass /* clazz */, jlong ptr, jboolean enabled) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
im->setShowTouches(enabled);//调用NativeInputManager的setShowTouches方法
}
void NativeInputManager::setShowTouches(bool enabled) {
{ // acquire lock
AutoMutex _l(mLock);
if (mLocked.showTouches == enabled) {
return;
}
ALOGI("Setting show touches feature to %s.", enabled ? "enabled" : "disabled");
mLocked.showTouches = enabled;//1
} // release lock
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_SHOW_TOUCHES);//2
}
注释1处mLocked.showTouches就为传进来的enabled(打开为true,关闭为false),注释2处调用InputReader的requestRefreshConfiguration继续处理
//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::requestRefreshConfiguration(uint32_t changes) {
AutoMutex _l(mLock);
if (changes) {
bool needWake = !mConfigurationChangesToRefresh;
mConfigurationChangesToRefresh |= changes;//changes为InputReaderConfiguration::CHANGE_SHOW_TOUCHES
if (needWake) {
mEventHub->wake();//唤醒InputReader线程
}
}
}
InputReader线程被唤醒,loopOnce继续执行
//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::loopOnce() {
//省略
{ // acquire lock
AutoMutex _l(mLock);
uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
timeoutMillis = 0;
refreshConfigurationLocked(changes);//1
} else if (mNextTimeout != LLONG_MAX) {
//省略
}
} // release lock
//省略
注释1处,调用refreshConfigurationLocked继续处理
//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::refreshConfigurationLocked(uint32_t changes) {
mPolicy->getReaderConfiguration(&mConfig);//1
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
if (changes) {
//省略
if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
mEventHub->requestReopenDevices();
} else {
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
device->configure(now, &mConfig, changes);//2
}
}
}
}
注释1处主要是将前面的mLocked.showTouches又赋值给mConfig的showTouches,注释2处调用InputDevice的configure继续处理。
1,getReaderConfiguration
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
//省略
{ // acquire lock
AutoMutex _l(mLock);
//省略
outConfig->showTouches = mLocked.showTouches;//赋值
//省略
} // release lock
}
2,InputDevice:configure
//frameworks\native\services\inputflinger\reader\InputDevice.cpp
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
uint32_t changes) {
//省略
for_each_mapper([this, when, config, changes](InputMapper& mapper) {
mapper.configure(when, config, changes);
mSources |= mapper.getSources();
});
//省略
}
对于InputReaderConfiguration::CHANGE_SHOW_TOUCHES,又调用对应mapper的configure处理。多指触摸的话mapper为MultiTouchInputMapper,MultiTouchInputMapper没有实现configure方法,在其父类TouchInputMapper中实现
//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
uint32_t changes) {
//省略
if (!changes ||
(changes &
(InputReaderConfiguration::CHANGE_DISPLAY_INFO |
InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
// Configure device sources, surface dimensions, orientation and
// scaling factors.
configureSurface(when, &resetNeeded);
}
//省略
调用configureSurface继续处理
//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
//省略
// Create pointer controller if needed.
if (mDeviceMode == DEVICE_MODE_POINTER ||
(mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
if (mPointerController == nullptr) {
mPointerController = getContext()->getPointerController(getDeviceId());
}
} else {
mPointerController.clear();
}
//省略
}
getPointerController方法最终调用InputReader的getPointerControllerLocked方法
//frameworks\native\services\inputflinger\reader\InputReader.cpp
sp<PointerControllerInterface> InputReader::getPointerControllerLocked(int32_t deviceId) {
sp<PointerControllerInterface> controller = mPointerController.promote();
if (controller == nullptr) {
controller = mPolicy->obtainPointerController(deviceId);//1
mPointerController = controller;
updatePointerDisplayLocked();//2
}
return controller;
}
注释1处创建PointerController对象,注释2处调用updatePointerDisplayLocked继续处理
1,obtainPointerController
//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) {
ATRACE_CALL();
AutoMutex _l(mLock);
sp<PointerController> controller = mLocked.pointerController.promote();
if (controller == nullptr) {
ensureSpriteControllerLocked();//创建SpriteController对象
controller = new PointerController(this, mLooper, mLocked.spriteController);//创建PointerController对象
mLocked.pointerController = controller;
updateInactivityTimeoutLocked();
}
return controller;
}
2,updatePointerDisplayLocked
//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::updatePointerDisplayLocked() {
sp<PointerControllerInterface> controller = mPointerController.promote();//取出前面创建的PointerController对象
if (controller == nullptr) {
return;
}
//省略
controller->setDisplayViewport(*viewport);
}
来看一下PointerController的setDisplayViewport方法
frameworks\base\libs\input\PointerController.cpp
void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
//省略
loadResourcesLocked();//加载小圆点图片资源
//省略
updatePointerLocked();//保存资源信息到SpriteController中,其中包含了小圆点图片的bitmap
}
loadResourcesLocked加载图片信息,主要是通过JNI调用,调用到PointerIcon中的getSystemIcon方法中
//frameworks\base\core\java\android\view\PointerIcon.java
public static PointerIcon getSystemIcon(@NonNull Context context, int type) {
//省略
int typeIndex = getSystemIconTypeIndex(type);
if (typeIndex == 0) {
typeIndex = getSystemIconTypeIndex(TYPE_DEFAULT);
}
int defStyle = sUseLargeIcons ?
com.android.internal.R.style.LargePointer : com.android.internal.R.style.Pointer;
TypedArray a = context.obtainStyledAttributes(null,
com.android.internal.R.styleable.Pointer,
0, defStyle);
int resourceId = a.getResourceId(typeIndex, -1);
a.recycle();
icon = new PointerIcon(type);
if ((resourceId & 0xff000000) == 0x01000000) {
icon.mSystemIconResourceId = resourceId;//设置ID
} else {
icon.loadResource(context, context.getResources(), resourceId);
}
systemIcons.append(type, icon);
return icon;
}
private static int getSystemIconTypeIndex(int type) {
switch (type) {
case TYPE_ARROW:
return com.android.internal.R.styleable.Pointer_pointerIconArrow;
case TYPE_SPOT_HOVER:
return com.android.internal.R.styleable.Pointer_pointerIconSpotHover;
case TYPE_SPOT_TOUCH:
return com.android.internal.R.styleable.Pointer_pointerIconSpotTouch;
//省略
}
可以看出,加载的资源ID为pointer_spot_touch_icon
//frameworks\base\core\res\res\drawable\pointer_spot_touch_icon.xml
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_spot_touch"
android:hotSpotX="16dp"
android:hotSpotY="16dp" />
小圆点的图片资源已经被加载,并将其保存在相应的变量中了,接下来就需要将其显示出来了。当有触摸数据产生时,InputReader在读取到数据,然后会调用到cookAndDispatch来处理原始数据
//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
//省略
if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches &&
mPointerController != nullptr) {//如果需要显示小圆点
mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
mCurrentCookedState.cookedPointerData.touchingIdBits,
mViewport.displayId);
}
//省略
}
主要是通过调用setSpots去处理,在setSpots函数中,主要是先创建一个Spot对象,然后调用其updateSprite方法去请求更新小圆点
//frameworks\base\libs\input\PointerController.cpp
void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
int32_t displayId) {
sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
sprite->setAlpha(alpha);
sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
sprite->setPosition(x, y);
sprite->setDisplayId(displayId);
this->x = x;
this->y = y;
if (icon != lastIcon) {
lastIcon = icon;
if (icon) {
sprite->setIcon(*icon);
sprite->setVisible(true);//显示
} else {
sprite->setVisible(false);
}
}
}
接下来的流程,主要是通过发送发送MSG_UPDATE_SPRITES消息,然后调用doUpdateSprites来更新绘制小圆点
//frameworks\base\libs\input\SpriteController.cpp
void SpriteController::doUpdateSprites() {
//省略
if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
&& update.state.wantSurfaceVisible()) {
sp<Surface> surface = update.state.surfaceControl->getSurface();
ANativeWindow_Buffer outBuffer;
status_t status = surface->lock(&outBuffer, NULL);
if (status) {
ALOGE("Error %d locking sprite surface before drawing.", status);
} else {
graphics::Paint paint;
paint.setBlendMode(ABLEND_MODE_SRC);
graphics::Canvas canvas(outBuffer, (int32_t) surface->getBuffersDataSpace());
canvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
const int iconWidth = update.state.icon.bitmap.getInfo().width;
const int iconHeight = update.state.icon.bitmap.getInfo().height;
if (outBuffer.width > iconWidth) {
paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint);
}
if (outBuffer.height > iconHeight) {
paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint);
}
status = surface->unlockAndPost();
//省略
}
总结
触摸圆点的显示逻辑都是在InputReader线程中完成。以下为显示的流程图