TouchLog:解决事件分发机制

目标

一个用于监听android事件分发流程的库,两行代码即可在运行时期监听事件的分发流程

在编写一些复杂的布局时,常常由于事件分发到底是哪个view处理产生困扰,做法通常需要经过以下步骤:

  • 自定义一个View,重写disaptchTouchEvent等方法。
  • 添加log日志。
  • 然后替换布局文件。
  • 编译,通过控制台查看事件分发流程。
  • 继续自定义View … 如果没有发现问题,无线循环…
  • 问题解决,删除之前定义的View,还原布局文件。

对于如上的流程,需要多次的修改代码,编译等,而且还有还原错误的风险。
那么有没有一种方式,能够在尽可能的少编写代码而实现上述流程,减少对于事件分发打印的困扰呢

简介

TouchLog为了解决如上问题而诞生,该库可以在运行时期打印完整的事件分发流程。

  • 监听View的dispatchTouchEvent,onTouchEvent,onInterceptTouchEvent。
  • 运行时期动态打印事件分发流程。
  • 每一次完整的事件分发记录以json的形式写入文件。
  • 去重功能,对相同的move事件会自动过滤。
  • 提供no-op版本,使用时可区分debug和release。
  • 提供不同模式显示

使用

添加依赖

在项目的app下的build.gradle中添加依赖

debugApi 'com.spearbothy:simple-touch:1.0.5'
releaseApi 'com.spearbothy:simple-touch-no-op:1.0.5'

初始化

在项目的Application的onCreate()中调用初始化方法Touch.inject(this);

Touch.init(this, new Config().setSimple(false));

Config对象提供一些配置选项

public class Config {

    // 输出的日志以极简模式输出
    private boolean isSimple = true;
    // 是否延迟打印日志,延迟打印日志会在触摸事件结束之后打印,并且具有去重功能
    private boolean isDelay = true;
    // 是否保留重复的,默认不保留
    private boolean isRepeat = false;
    // 是否写入到文件
    private boolean isPrint2File = true;
    // 是否处理,不处理则不会监听任何方法,任何功能都无法生效
    private boolean isProcess = true;
}

注入代理类(用于监听事件分发)

在Activity的onCreate()的super.onCreate(savedInstanceState);之前调用

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        Touch.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRootView = (LinearLayout) findViewById(R.id.root);
    }
使用

编译完成之后,打开app,开始触摸吧!!! 每一次手指离开到触摸请间隔大于1s,目的是对于每次触摸加以区分,暂时没想到合适的判断条件。

备注
  • 提供了no-op版本,该版本中包含有初始化和注入方法的空实现,以达到debug和release使用不同的版本,使release不包含任何注入和初始化逻辑。
  • 在注入的时候有点耗时,如果页面过于复杂,会有种页面卡顿的感觉.

思考

对于该库,其实核心就是怎么能够监听onTouchEvent()等事件分发方法。
从实现的角度,核心问题在于两个:

  • 如何生成代理类,该代理类中包含对view中事件的hook。
  • 如何将View替换为生成的代理类对象。

生成代理类

生成代理类有以下几种方式:

  • 静态方式:预先编写一些基本view的代理类,而对于自定义view,可以在编译期通过Processor生成。
  • 动态方式:在apk运行时期,动态的生成代理类,该方式参考java的动态代理机制。

替换代理类对象

  • 静态方式:在程序编译时期,监听xml的打包流程,动态的修改布局文件替换为代理类对象。类似于代码注入。
  • 动态方式:在运行时期,构造view对象的时候,替换为构造代理类。
    根据以上的两种方式,最终全部选择动态的方式,及运行时期动态的生成代理类以及动态的替换view对象。

实现

生成代理类

java本身提供了动态代理的机制,但是由于动态代理的对象必须是接口的方法,而view的事件分发方法都不是某一个接口的方法,那么java本身的动态代理机制是不行的。
cglib是java的一个动态代理库,可以代理类方法。但是因为android中是以dex方式存储代码,所以无法应用于android。
dexmaker是应用于android的动态生成代码的库。可以用该库实现动态生成代理类。
动态生成代理类的关键点在于ViewProxyBuilder类,通过该类可以生成代理类对象。
生成的方式如下:

private static View proxy(final View view, AttributeSet attrs) {
        try {
            return ViewProxyBuilder.forClass(view.getClass())
                    .handler(new TouchHandler())
                    .dexCache(view.getContext().getDir(Constants.DEX_CACHE_DIR, Context.MODE_PRIVATE))
                    .constructorArgTypes(Context.class, AttributeSet.class)
                    .constructorArgValues(view.getContext(), attrs)
                    .addProxyMethod(Arrays.asList(Constants.PROXY_METHODS))
                    .build();
        } catch (IOException e) {
            return null;
        }
    }

其中handler为代理方法处理类。

public class TouchHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        TouchMessageManager.getInstance().printBefore(proxy, method, args);
        Object result = ViewProxyBuilder.callSuper(proxy, method, args);
        TouchMessageManager.getInstance().printAfter(proxy, method, args ,result);
        return result;
    }
}

该类实际上只是在方法前和方法后都打印日志

动态替换对象

集成生成了代理对象,还有一个问题就是如何将生成的代理对象和原view`对象替换。

该思路来源于support-v7,对于继承AppCompatActivity的页面,其中的TextView等运行时期都会被替换为AppCompatTextView。核心便是LayoutInfalter类,该类用于生成所有的布局对象,同时该类提供生成布局对象的hook方法,可以添加一下自定义操作。

核心就是调用LayoutInflater.setFactory(),关键代码如下:

public static void inject(Context context) {
        if (sConfig == null || !sConfig.isProcess()) {
            return;
        }
        LayoutInflater inflater;
        if (context instanceof Activity) {
            inflater = ((Activity) context).getLayoutInflater();
        } else {
            inflater = LayoutInflater.from(context);
        }
        ViewFactory factory = new ViewFactory();
        if (context instanceof AppCompatActivity) {
            final AppCompatDelegate delegate = ((AppCompatActivity) context).getDelegate();
            factory.setInterceptFactory(new LayoutInflater.Factory2() {
                @Override
                public View onCreateView(String name, Context context, AttributeSet attrs) {
                    return delegate.createView(null, name, context, attrs);
                }

                @Override
                public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
                    return delegate.createView(parent, name, context, attrs);
                }
            });
        }
        // 设置hook
        inflater.setFactory2(factory);
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • 不知不觉,已经毕业一个多月了。忙碌的工作告一段落之后,闲暇之余开始怀念那些建设东路的日子。 四年前,带这些许懵懂与...
    葛上亭长阅读 619评论 1 2
  • 喜欢就争取 得到就珍惜 错过就忘记 生活就是这么简单 一天之中什么时间段最美,我会告诉你夜晚比白天更美,当...
    良尔传媒阅读 431评论 0 0
  • 一个月备考四科的学习方法: 一、看资料。 1、准备资料。首先报告科目下载资料,包括《课件》、《密训资料》、《主观...
    引领馨阳阅读 90评论 0 0
  • 感赏今天学会了如何面对客户的刁难。今天跟客户发生冲突,客户上来就开骂,后来经过指导了解说话的突破口。原来事情也可以...
    小妖玥儿阅读 184评论 0 0
  • 一叶知秋 也如鸿鸟传为佳话 它展开的硕大手掌 被思念的人一张张摘下 轻轻地卷着个成熟的秋 寄给了远方的他 没待到秋...
    曹天成阅读 177评论 1 1