关于解决键盘快速输入问题的(keyboard input issue)的突破的体会

下面是Team内部关于技术问题keyboard input issue突破的体会,主要由Parry, Roy和我提供,以邮件的形式展示如下:

Hi All

 

首先感谢 Roy, Rex 的大力协助,也感谢全组成员对此 Case 的高度关注,正是由于整个团队的努力付出,才使此 Case 最终得以解决。

 

这个 Case 的确是一个非常棘手的 Case, 4 14 号接手这个 Case 以来,已有将近 3 周的时间,期间虽然偶有所获,但是都未找出问题的根源,现在将此 case 的来龙去脉整理下,与大家共享。

 

【问题来源】

客户生产流程中需要连续不断的扫描产品上的条形码,并将条形码信息通过 FTPC Form( DSGrid ) 记录下来,客户的扫描器每扫描一次条形码,会自动触发一次键盘回车事件,这样 DSGrid 会切换到下一行来接受下一次扫描的条形码,当扫描速度过快时,发现 DSGrid 记录的条形码数据有很严重的错误现象(很多字符被丢失,或多行条形码值出现在同一个 Grid 中)

 

【如何复现问题】

客户提供了 Macro Scheduler 软件,能够模拟键盘输入,模拟器的行为也和客户的扫描器一样,我们用客户提供的脚本程序能模拟多次扫描条形码的场景。

 

【研究方向】

我们着手研究的方向有下面几个:

1.     先是想到其他类似的控件有没有相同的问题,如 Excel, JDK 自带的 JTable

经测试, Excel JTable 也有相同的问题,但是其出现的几率远比 FTPC 小,而 FTPC 几乎每次测试都会出现,我们初期给的 workaround 是在每次敲击回车后暂停 0.1s ,这样 FTPC 就不会有任何问题。

但是这样的 workaround 是客户无法接受的,因为在模拟键盘的脚本程序里暂停 0.1s ,对应到现实的生产车间里,就是让机器在每次检验产品时都需要暂停 0.1s, 这样大幅降低了工厂的生产效率,这是客户不希望看到的事情。

2.     客户提供的信息中有提到 FTPC 5.3 DSGrid 中并没有出现这样的性能问题,但是我们也没有 5.3 的测试环境,我们没有必要在 5.3 上去测试,就算测出来跟客户一样,也不能解决 FTPC9.0 的问题,所以仅在代码层面比较了下 2 个版本的区别

5.3 的事件处理是交由 KeyEventHandler Microsoft Visual J++ WFC )处理,而 9.0 KeyListener SUN JDK )来处理,底层的东西我们也不好去比较,只是顺着 FTPC 的处理逻辑,对比 2 个版本的代码,发现在敲击回车事件后, 9.0 多执行了一段代码:

DSGrid.cellSelected () 方法中 assignValueOnCellChanged(oldrow, oldcol, text); 当注释后再测试,发现依然没有性能提升,所以这条路也没有任何突破。

3.     于是开始怀疑 JVM 是不是丢失了键盘事件,经过 Rex debug, 发现敲击键盘的事件,都按照事件发生的先后顺序,依次被放进了 EventQueue Queue[] queues 中,并且由 EventDispathThread 从该 queue 中依次取出每个事件 dispatch Event 对应的 source. queue 中完整的记录着我们每一次键盘敲击事件,而且每次键盘事件都会被细分成 3 种事件( KeyPressed,KeyTyped,KeyReleased ),这也说明在事件触发的源头并没有丢失任何事件。

4.     那出现问题的只有一种可能就是 EventDispathThread dispatch 事件时, FTPC 里负责监听事件的 control 没收到通知,可能是原本注册在该 control 上的 listener remove 了,于是开始在代码中寻找 removeXXXListener 之类的方法,但是经过调试发现,这样的 remove 方法在敲击键盘时根本没执行。

5.     其实从一开始大家就一直怀疑是 DSGrid 在处理回车事件时,处理的业务逻辑过多,导致没反应过来, FTPC catch 住回车后面的几个键盘事件,或者是回车导致 DSGrid 切换焦点不及时,导致后面事件的丢失。所以昨天我仔细看了下回车事件才会被调用的方法 cellSelected(int oldrow, int oldcol, int newrow, int newcol) 中,该方法中先将换行前的单元格中的 Control 给隐藏掉,然后将换行后的新单元格的 Control 显示出来,而在这 2 个过程当中,还有很长一段 FTPC 逻辑需要执行(对 SmartEdit DateTimePicker 的处理逻辑),代码如下:

protected void cellSelected(int oldrow, int oldcol, int newrow, int newcol)

{

            if (oldrow == newrow && oldcol == newcol)

            {

                        return;

            }

 

            // check for visible control to hide

            CControl control = getCellControl(oldrow, oldcol);

            CControl newcontrol = getCellControl(newrow, newcol);        // 1

            newcontrol.setVisible(true);                                                     // 2

            if (control != null && !m_sorting)

            {

                        control.setVisible(false);                                                 // 3

 

                        // ...

                        // some code here...

                        // ...

                       

            }

 

            // check for a new control to make visible

            control = getCellControl(newrow, newcol);

            if (control != null)

            {

 

                        // ...

                        // some code here...

                        // ...

 

                        control.setVisible(true);                                                   // 4

                        control.focus();

 

                        // ...

                        // some code here...

                        // ...

            }

}

 

其中 1,2 的蓝色代码是我昨天加上去的,想让新的 control 能先显示出来,这样前半段对旧的 control 的逻辑处理的时间再长也不会有任何影响,但是测试后发现仍然没有任何性能提升。到这里我就没有继续再调试下去,这样与真正的 solution 擦肩而过了。

原以为 DSGrid 里每个 cell 都是独立的 control ,而草率的认为这里 getCellControl(oldrow, oldcol); getCellControl(newrow, newcol); 是不同的 control 对象,但事实上 DSGrid 中,每列的所有 cell 都是共享一个 control 的,在 1,2 处虽然将 new control 提前显示出来,但是很快又被 3 处的代码隐藏掉了,依然还会经历“很长”一段代码才能到 4 处,此过程中虽然程序会执行的很快,但是客户提供的模拟器执行效率远比我们的高,在 3 4 的这段时间,没有 control 来接受 EventDispathThread dispatch 的事件( control 都被隐藏了),于是导致了回车后的事件被丢失了,这也是为什么我们在每个回车事件后,增加 0.1s 的等待时间,就能解决问题的原因。

6.     今天一大早, Roy 跑过来兴奋的和我说,找到解决办法了,就是把 3 处的代码注释掉,经过测试,此方法确实可行, Roy 的做法是让旧的 control 不隐藏,继续接受键盘事件,但是 Roy 也提醒我,这样做肯定会导致其他问题的,我也针对多个 scenario 进行了详细的测试,发现这样修改后,当操作不同列时,前一列的 control 还会被显示出来。

 

【解决方法】

所以为了解决问题,只要在 3 处将 control 隐藏的代码外加个条件判断下就行了,修改代码如下:

CControl newcontrol = getCellControl( newrow, newcol);

if (control != null && newcontrol != control)

{

            control.setVisible(false);                                     // 3

}

 

【收获与总结】

1.  当我们修改了 Client 代码时,一般不需要重启 PD ,但是有时执行时还是修改前的代码,很有可能你改的代码就是真正的 solution ,但是由于你没重启 PD, 导致测试结果没立即更新,而误导了你的判断,让你与真正的 solution 擦肩而过,为了避免这种情况,我建议大家每次都重启下 PD

2.  当我们尝试了很多途径,依然没有寻找到突破口,不妨 更加深入的追踪核心代码的逻辑,彻底搞清楚核心逻辑所走的核心路线,比如此 Case对应的 JDK Event Delegation Model,就算我们最后都没能完全解决问题,我们也能从中学到很多核心的知识。

3.  有时候需要设断点来调试代码,但是对于这种 Event case,一旦你设了断点,程序会阻塞,后续发生的事件有可能不会被触发,所以我们有时候还得用最原始的 System.out.println()的方式在控制台打印出变量的值,来帮助分析问题。

4.  团队的力量是强大的,我们在解 Case 时,既要保持独立思考的习惯,也要及时和团队成员的沟通,从他们那获取更多的有用信息,集思广益。

5.  在遇到很棘手的 case 时,一定不要失去信心,一定要有解决它的决心和毅力。相信再复杂的 case 都是会被解决的,只是还没有找到突破口。

6.  通过此 case,也使我对 Event dispatch机制有了更深入的了解,我也会收集相应的资料,整理并和大家一起分享。

 

再次感谢 Rex Roy, 特别是 Roy 在早上 5 点被孩子吵醒的情况下,还依然恋恋不忘这个 case, 这种刻苦钻研的精神实在是值得我们学习! ^_^

 

Thanks

Parry

 

 

From: Roy Lu [mailto:[email protected]]
Sent: Thursday, May 05, 2011 14:16
To: 'Ken Xie'; 'Neil Ji'; 'Rex Wu'; [email protected]; 'Leo Xu'; 'Jack Mei'; [email protected]
Cc: 'Martin Sheng'
Subject: RE:
关于 PC-13633 keyboard input issue 的突破的体会

 

Hi All,

这个问题的突破口有三点

1 Parry在最初的按键事件监听加了打印,这样可以很方便看看我们丢失了多少键盘事件,是否接受到了键盘事件都会在 dsgrid中显示出来。

(如果接受了按键事件没有在 dsgrid中显示出来,那就是我们 FTPC逻辑造成丢失)

 

2, 后来用了普通的 jtext去接受事件,发现不像 dsgrid那样会丢失某些键盘输入,这样就可以判断不是 awt keyboard event queue原生性能低下会导致按键丢失,所以定位很有可能是 FTPC的代码逻辑导致的问题

 

3 键盘的 listener control只添加了一次,并且整个 edit 列都共享同一个 control,原先担心是 listener注销导致键盘事件丢失,但是发现 listener只添加一次而且不会注销。所以看到 control.setVisible(false )后, component事件监听失效导致事件丢失(其实 control.setVisible(false )后不久马上新的一行 control.setVisible(true ),间隔很短,一般情况是不会丢失的,但是模拟软件的速度真的很快,切换的那些短暂的微秒,就导致了一些 key 的丢失

 

Thanks

Roy

 

From: Ken Xie [mailto:[email protected]]
Sent: Thursday, May 05, 2011 12:45 PM
To: 'Neil Ji'; 'Rex Wu'; [email protected]; [email protected]; 'Leo Xu'; 'Jack Mei'; [email protected]
Cc: 'Martin Sheng'
Subject: RE:
关于 PC-13633 keyboard input issue 的突破的体会

 

Hi All

通过这个 case, 相信大家对解决复杂的 case 更有信心了。我们平常所说的: 相信再复杂的 case都是会被解决的,只是还没有找到突破口。在这个 case 的解决过程中,我相信 Parry是最有心理体会的。欢迎 Parry和大家分享下这个“痛并快乐”的过程。

Thanks

Ken

 

From: Neil Ji [mailto:[email protected]]
Sent: 2011
5 5 11:53
To: 'Rex Wu'; [email protected]; [email protected]; [email protected]; 'Leo Xu'; 'Jack Mei'; [email protected]
Cc: Martin Sheng
Subject: RE:
关于 PC-13633 keyboard input issue 的突破的体会

 

Hi All,

 

Good team work to resolve this complex issue, and it’s great you write down what you’ve experienced. Please continue updating this doc and put it on SharePoint after it’s done.

 

Thanks

Neil

 

From: Rex Wu [mailto:[email protected]]
Sent: Thursday, May 05, 2011 11:43 AM
To: [email protected]; [email protected]; [email protected]; 'Leo Xu'; 'Jack Mei'; [email protected]
Cc: [email protected]
Subject:
关于 PC-13633 keyboard input issue 的突破的体会

 

Hi All,

 

这个问题的突破是由 Roy 触发的,但由于我也参与了其中一段时间的研究,所以有一些体会,记录下来与大家分享。

我想 Parry Roy 应该也有一些体会。 Parry 负责整个 Case 的全局,我和 Roy 都是试图在问题点上获取突破。

*        问题症状 :快速夹杂回车的键盘输入,会导致在 Excel DsGrid ,以及 JTable 中出现问题:丢失或乱行。但这个问题对于 Excel DsGrid 有明显的区别区别: Excel 远远比 DsGrid 稳定,出问题的几率小很多,而 DsGrid 几乎每次都会出错。这种快速的键盘的输入,我们是使用模拟键盘输入的软件 Macro Scheduler 做到的。

*        研究之路 :这个问题的研究还是颇费心思的。一开始由 Parry 研究,后来 Rex 参与了一段时间的研究,然后是 Roy Parry 一起来研究。我研究的结果也只知道在回车的前后会出现问题,但更深的原因却不知道,看着纷繁复杂的 FTPC 代码逻辑,以及 JavaSwing 的事件处理机制,有点难以下手的感觉。

主要困难 :不容易 Debug 因为是事件触发,不容易缩小范围,整个 DsGrid 写的异常复杂,很难抽丝剥茧

现有技巧 :通过在事件触发的地方使用 System.out.println(event.toString()) 可以打印事件的详细信息,对于 Debug 起到了作用,因为这样我们就可以看到哪些事件被触发了,被触发的到底是些什么事件

由于事件的丢失,所以怀疑是在 FTPC 事件处理的逻辑出现了问题,但始终没有找到真正的症结的所在。   最后在 Parry Roy 的合力作用下发现,由于在回车事件的处理中,有一段程序间 Control 不可见 , 这个 Control 就是嵌入到 DsGrid 中的 Edit ,它是事件触发的源头。把 control.setVisible(false); 注释掉,让 control 一直处于可见状态就解决了问题。而这段代码是换行事件最核心的运行代码: moveCell() à cellSelected

*        解决方案 :注释掉 control.setVisible(false) ,让 control 一直处于可见状态,因为不可见将无法接受触发的事件,就会导致事件的丢失,从而导致输入丢失或乱行。

*        总结 :鉴于这次问题的突破,针对这个 Case ,更加深入的追踪核心代码的逻辑尤为重要,彻底搞清楚核心逻辑所走的核心路线,是本次问题解决的关键。我想以后再出现一些难以解决的问题,不管逻辑有多复杂,我们都可以尝试抓住最核心的代码逻辑寻求突破。 另外团队的合作也只本次问题解决的要素之一。

 

关于这个问题的更详细的信息还在 Parry 的研究之中,因为有所体会,所以有了这次分享。

Regards,

Rex Wu

 

你可能感兴趣的:(关于解决键盘快速输入问题的(keyboard input issue)的突破的体会)