欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android源码阅读技巧--查找开发者选项中显示触摸操作源码

程序员文章站 2022-03-21 18:10:07
在开发者模式下,在开发者选项中,可以勾选“显示触摸操作”,然后只要点击屏幕就会在点击的位置有圈圈显示。如何找到绘制圈圈的代码部分,有什么技巧来阅读代码量这么大的android系统源码呢?以下请跟着小老弟我来一起分析吧。 1. android设置功能的代码是在packages/apps/Setting ......

    在开发者模式下,在开发者选项中,可以勾选“显示触摸操作”,然后只要点击屏幕就会在点击的位置有圈圈显示。如何找到绘制圈圈的代码部分,有什么技巧来阅读代码量这么大的android系统源码呢?以下请跟着小老弟我来一起分析吧。

 

    1. android设置功能的代码是在packages/apps/settings/里面的,所以在settings中搜寻关键的字符串,

在源码目录下终端输入

grep -rn "显示触摸操作" ./packages/apps/settings/

    搜到如下:

./packages/apps/settings/res/values-zh-rcn/strings.xml:2108: <string name="show_touches" msgid="1356420386500834339">"显示触摸操作"</string>

    熟悉android应用编程的话就应该知道代码中 show_touches 与“显示触摸操作”是相关联的。


    2. 输入 

grep -rn "show_touches" --include "*.java" ./packages/apps/settings/

    得到

./packages/apps/settings/src/com/android/settings/developmentsettings.java:128: private static final string show_touches_key = "show_touches";

 

    3. 开始阅读源码,打开 developmentsettings.java 按以下阅读顺序,

private static final string show_touches_key = "show_touches";
mshowtouches = findandinitswitchpref(show_touches_key);
private void writeshowtouchesoptions() {
    settings.system.putint(getactivity().getcontentresolver(),
            settings.system.show_touches, mshowtouches.ischecked() ? 1 : 0);
}

    猜测 putint 应该是一个数据传递的功能, 所以在framework里面搜 show_touches 看看情况如何,

    输入

grep -rn "show_touches" frameworks/

    搜到好多,比如以下应该和数据处理注册相关,

frameworks/base/core/java/android/provider/settings.java:3094: public static final string show_touches = "show_touches";
frameworks/base/core/java/android/provider/settings.java:3097: public static final validator show_touches_validator = sbooleanvalidator;
frameworks/base/core/java/android/provider/settings.java:3439: private_settings.add(show_touches);
frameworks/base/core/java/android/provider/settings.java:3519: validators.put(show_touches, show_touches_validator);

    因为看不出有什么特殊操作,只是一些声明和 add 操作,所以忽略之。。。。。。

以下才是具体功能

frameworks/base/services/core/java/com/android/server/input/inputmanagerservice.java:1600: settings.system.geturifor(settings.system.show_touches), true,

 

    4. 打开 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);
}

    发现,关键方法 getcontentresolver 刚好在developmentsettings.java 中 putint 里面的参数一致,所以可以肯定是走这里了。

    接下来跟方法 updateshowtouchesfromsettings()

public void updateshowtouchesfromsettings() {
    int setting = getshowtouchessetting(0);
    nativesetshowtouches(mptr, setting != 0);
}

    看到 native 字样,说明会走到用 cpp 写的 jni 接口里面。

 

    3. 因为已经到 jni 了,所以后续都只需看 cpp 文件了,输入

grep -rn "nativesetshowtouches" --include "*.cpp" ./frameworks/ 

    搜到

./frameworks/base/services/core/jni/com_android_server_input_inputmanagerservice.cpp:1310:static void nativesetshowtouches(jnienv* /* env */,

   打开这份 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);
}

    看看 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;
    } // release lock

    minputmanager->getreader()->requestrefreshconfiguration(
            inputreaderconfiguration::change_show_touches);
}

    其中 mlocked.showtouches = enabled; 中 showtouches 是关键字,还有 change_show_touches 也很关键。

 

    4. 输入

 grep -rn "change_show_touches" --include "*.cpp" ./frameworks/ 

    搜到

./frameworks/native/services/inputflinger/inputreader.cpp:3177: | inputreaderconfiguration::change_show_touches

    打开 inputreader.cpp ,在 change_show_touches 中看不出啥东西,太费力了。

这时可以在 inputreader.cpp 中搜 showtouches ,

    输入

 grep -rn "showtouches" --include "*.cpp" ./frameworks/ 

    搜到

./frameworks/native/services/inputflinger/inputreader.cpp:3476: (mdevicemode == device_mode_direct && mconfig.showtouches)) {
./frameworks/native/services/inputflinger/inputreader.cpp:4334: && mconfig.showtouches && mpointercontroller != null) {

    如何把 change_show_touches 与 showtouches 关联起来呢?在 inputreader.cpp 中,

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 发现以下关键代码

    // create pointer controller if needed.
    if (mdevicemode == device_mode_pointer ||
            (mdevicemode == device_mode_direct && mconfig.showtouches)) {
        if (mpointercontroller == null) {
            mpointercontroller = getpolicy()->obtainpointercontroller(getdeviceid());
        }
    } else {
        mpointercontroller.clear();
    }

    这段注释耐人寻味 // create pointer controller if needed.

所以可以肯定,后续就在 inputreader.cpp 里面围绕 showtouches 来搞事情,果然 showtouches 在另外一出显现它的重要,

        if (mdevicemode == device_mode_direct
                && mconfig.showtouches && mpointercontroller != null) {
            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);
        }

    学了多年的英语要发挥它的作用了,可知 setspots 中 spots的中文意思为“斑点,小圆点”,所以就是走这里了,setspots传的参数应该就和触摸坐标数据有关了。

 

    5. 输入

grep -rn "setspots" --include "*.cpp" ./frameworks/

    搜到

./frameworks/base/libs/input/pointercontroller.cpp:246:void pointercontroller::setspots(const pointercoords* spotcoords,
./frameworks/base/libs/input/pointercontroller.cpp:249: alogd("setspots: idbits=%08x", spotidbits.value);

    打开 pointercontroller.cpp ,在函数 void pointercontroller::setspots(const pointercoords* spotcoords, const uint32_t* spotidtoindex, bitset32 spotidbits)
中可知 spot->updatesprite(&icon, x, y); 与显示圈圈有关,大胆预测 icon 为显示的图形,x和y为显示的坐标。添加 alogi 打印,编译导入后发现,每次显示圈圈的时候,这里都会走。猜想变成真理!

    6. 思考,icon 数据来自哪里, 怎么就能在android上显示呢?x,y数据又是怎么传入的呢?以后有空再一起探讨吧。