学而不思则罔,思而不学则殆

【Android】为啥事件传递这么频繁却不会造成内存抖动

  • 引言
  • 测试
    • 测试代码
    • 测试一 两次手指按下起来
    • 测试二 - 不同的页面测试
    • 测试三 同一个应用杀进程测试
    • 测试四 不同进程
    • 测试结果+总结
  • 源码分析

引言

一般在Android开发中我们被要求说在onDraw方法不要新建对象,那为什么整体事件传递的时候,MotionEvent对象一直被回调,却没有造成内存波动,是为什么呢?

测试

针对这个问题,我专门做了测试,在dispatchTouchEvent方法中打印MotionEvent 对象的hashCode和信息。

测试代码

public class DiyView extends View{...@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {Log.d("zhangyu20201217", "dispatchTouchEvent:" + System.identityHashCode(event) + " " + event.hashCode() + " " + event);return super.dispatchTouchEvent(event);}...

以下是测试结果:

测试一 两次手指按下起来

2020-12-17 08:01:21.457 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=197.96436, y[0]=174.92969, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=39665544, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.506 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=197.96436, y[0]=189.60889, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=4, eventTime=39665592, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.531 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=194.96509, y[0]=199.68668, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=5, eventTime=39665614, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.550 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=193.4578, y[0]=211.47052, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=4, eventTime=39665633, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.569 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=188.96655, y[0]=223.7197, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=5, eventTime=39665651, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.587 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=179.96875, y[0]=240.86429, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=7, eventTime=39665670, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.604 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=171.245, y[0]=250.92578, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=5, eventTime=39665688, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.622 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=165.96118, y[0]=260.61737, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=6, eventTime=39665707, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.641 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=162.96191, y[0]=267.41992, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=3, eventTime=39665722, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.689 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=162.96191, y[0]=268.97266, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=39665773, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:01:21.690 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=162.96191, y[0]=268.97266, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=39665779, downTime=39665544, deviceId=0, source=0x1002 }
2020-12-17 08:06:32.075 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=294.96265, y[0]=259.94922, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=39976162, downTime=39976162, deviceId=0, source=0x1002 }
2020-12-17 08:06:32.124 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=294.96265, y[0]=262.9375, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=39976199, downTime=39976162, deviceId=0, source=0x1002 }
2020-12-17 08:06:32.140 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=297.9619, y[0]=270.37024, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=3, eventTime=39976221, downTime=39976162, deviceId=0, source=0x1002 }
2020-12-17 08:06:32.153 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=297.9619, y[0]=277.14856, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=2, eventTime=39976237, downTime=39976162, deviceId=0, source=0x1002 }
2020-12-17 08:06:32.179 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=297.9619, y[0]=283.97266, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=2, eventTime=39976253, downTime=39976162, deviceId=0, source=0x1002 }
2020-12-17 08:06:32.180 18401-18401/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:154259924 hashCode:154259924 MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=297.9619, y[0]=283.97266, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=39976265, downTime=39976162, deviceId=0, source=0x1002 }

测试二 - 不同的页面测试

//AActivity.java
2020-12-17 08:24:16.497 19018-19018/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:267645040 hashCode:267645040 MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=172.96875, y[0]=289.98047, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41040583, downTime=41040583, deviceId=0, source=0x1002 }
2020-12-17 08:24:16.512 19018-19018/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:267645040 hashCode:267645040 MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=172.96875, y[0]=289.98047, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41040601, downTime=41040583, deviceId=0, source=0x1002 }//BActivity.java
2020-12-17 08:24:49.738 19018-19018/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:267645040 hashCode:267645040 MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=410.96558, y[0]=277.96875, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41073821, downTime=41073821, deviceId=0, source=0x1002 }
2020-12-17 08:24:49.792 19018-19018/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:267645040 hashCode:267645040 MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=410.96558, y[0]=277.96875, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41073879, downTime=41073821, deviceId=0, source=0x1002 }

测试三 同一个应用杀进程测试

//杀进程前
2020-12-17 08:25:40.127 19175-19175/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:267645040 hashCode:267645040 MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=366.96533, y[0]=380.97656, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41124208, downTime=41124208, deviceId=0, source=0x1002 }
2020-12-17 08:25:40.216 19175-19175/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:267645040 hashCode:267645040 MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=366.96533, y[0]=380.97656, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41124276, downTime=41124208, deviceId=0, source=0x1002 }//杀死进程后在启动测试
2020-12-17 08:26:55.298 19228-19228/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:267645040 hashCode:267645040 MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=193.96362, y[0]=432.94922, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41199384, downTime=41199384, deviceId=0, source=0x1002 }
2020-12-17 08:26:55.336 19228-19228/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:267645040 hashCode:267645040 MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=193.96362, y[0]=428.70605, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=1, eventTime=41199411, downTime=41199384, deviceId=0, source=0x1002 }
2020-12-17 08:26:55.360 19228-19228/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:267645040 hashCode:267645040 MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=193.96362, y[0]=429.96094, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41199450, downTime=41199384, deviceId=0, source=0x1002 }

测试四 不同进程

//com.example.viewtest进程
2020-12-17 08:39:09.508 19756-19756/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:149898054 hashCode:149898054 MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=202.96143, y[0]=362.9297, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41933595, downTime=41933595, deviceId=0, source=0x1002 }
2020-12-17 08:39:09.593 19756-19756/com.example.viewtest D/zhangyu20201217: dispatchTouchEvent identityHashCode:149898054 hashCode:149898054 MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=202.96143, y[0]=362.9297, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41933653, downTime=41933595, deviceId=0, source=0x1002 }//com.example.tw进程
2020-12-17 08:39:48.327 19847-19847/com.example.tw D/zhangyu20201217: dispatchTouchEvent identityHashCode:139330987 hashCode:139330987 MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=287.96265, y[0]=421.9336, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41972412, downTime=41972412, deviceId=0, source=0x1002 }
2020-12-17 08:39:48.400 19847-19847/com.example.tw D/zhangyu20201217: dispatchTouchEvent identityHashCode:139330987 hashCode:139330987 MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=287.96265, y[0]=421.9336, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=41972490, downTime=41972412, deviceId=0, source=0x1002 }

测试结果+总结

测试结果现象总结:

  1. 发现整个事件传递的流程中。【ACTION_DOWN】【ACTION_MOVE】【ACTION_UP】流中,MotionEvent 都是同一个对象。
  2. 就算不是同一个事件流,UP事件后再触发MotionEvent 跟之前还是同一个对象。
  3. 同一个进程不同页面间MotionEvent是同一个对象
  4. 同一进程后台杀死两次测试,MotionEvent是同一个对象
  5. 不同进程间的MotionEvent不是同一个对象

根据测试现象做结果分析:

  1. 同一个进程MotionEvent是同一个对象
  2. 不同进程MotionEvent不是同一个对象

源码分析

根据上面的测试,我们知道了在同一个进程中,MotionEvent是同一个对象。那么为什么?以及源码怎么实现的?
MotionEvent是同一个对象,我觉得,为了防止对象被重复创建,而且整个事件传递都是在主线程,不存在安全问题,只要每次事件源头同步好MotionEvent的状态就行。

根据源码:

    //MotionEvent.javaprivate static final Object gRecyclerLock = new Object();private static int gRecyclerUsed;private static MotionEvent gRecyclerTop;...private MotionEvent mNext;@UnsupportedAppUsagestatic private MotionEvent obtain() {final MotionEvent ev;synchronized (gRecyclerLock) {ev = gRecyclerTop;if (ev == null) {return new MotionEvent();}gRecyclerTop = ev.mNext;gRecyclerUsed -= 1;}ev.mNext = null;ev.prepareForReuse();return ev;}

MotionEvent.java中存在obtain方法(类似Handler的Message),该方法就是从缓存gRecyclerTop 中取出头部元素,没有就生成。然后gRecyclerTop 指向头部的下一个元素。等待下一次使用。而且gRecyclerTop是一个静态变量,在系统层面,是公用的。

    private static native long nativeInitialize(long nativePtr,int deviceId, int source, int displayId, int action, int flags, int edgeFlags,int metaState, int buttonState, @Classification int classification,float xOffset, float yOffset, float xPrecision, float yPrecision,long downTimeNanos, long eventTimeNanos,int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords);static public MotionEvent obtain(long downTime, long eventTime,int action, int pointerCount, PointerProperties[] pointerProperties,PointerCoords[] pointerCoords, int metaState, int buttonState,float xPrecision, float yPrecision, int deviceId,int edgeFlags, int source, int displayId, int flags) {MotionEvent ev = obtain();ev.mNativePtr = nativeInitialize(ev.mNativePtr,deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState,CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision,downTime * NS_PER_MS, eventTime * NS_PER_MS,pointerCount, pointerProperties, pointerCoords);...return ev;}

还提供了native方法nativeInitialize,复用时去native层把属性重新赋值。

以上就是我的分析。很多问题需要从源码中找到解决办法和一些处理问题的思路。

【Android】为啥事件传递这么频繁却不会造成内存抖动相关推荐

  1. Android Touch事件传递机制 二:单纯的(伪生命周期) 这个清楚一点

    转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...

  2. Android Touch事件传递机制 二:单纯的(伪生命周期)

    转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...

  3. Android触摸事件传递分析与实践

    设计UI时,亲爱的交互设计师们总会有一些天马行空的想法,大多数情况下原生的控件已不能支持这些"看似简单"的交互逻辑,需要继承ListView.ViewPager.ScrollVie ...

  4. Android Touch事件传递

    touch翻译为接触,触摸.我们今天要聊的就是摸的事件. 在Android中了解了Touch事件可以帮助我们解决,ScrollView嵌套ListView,GridView,viewPager滑动冲突 ...

  5. Android UI事件传递就是这么个事儿

    正文 ###聊聊UI事件传递 什么是UI事件? 触摸屏幕中UI控件的那一刻即为事件发生 MotionEvent对象包含了所有的触摸事件,如触摸的位置.多指触摸等 MotionEvent描述了当前的操作 ...

  6. Android之Android触摸事件传递机制

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/53431274 本文出自:[顾林海的博客] ##前言 关于Android ...

  7. Android Touch事件传递机制解析 (推荐)

    最近新闻列表里的下拉 down up  move 等等让我十分头疼 ,无意间看到了一篇非常不错的帖子,转载如下: 开篇语:最近程序在做一个小效果,要用到touch,结果整得云里面雾里的,干脆就好好把a ...

  8. android方向触摸事件,Android触摸事件传递机制,这一篇就够了

    整个触摸事件牵涉到的是,Activity,View,ViewGroup三者的传递机制. 这个触摸事件就是从外层往内层一层层的传递. 整个传递机制,分为3个步骤:分发,拦截,和消费. 1. 触摸事件的类 ...

  9. android imageview 事件传递,Android 事件传递机制TextView,ImageView等没有默认clickable属性的View单独设置onTouch事件注意事项...

    本文讲解TextView,ImageView等没有默认clickable属性的View单独设置onTouch事件 Android 事件传递机制:Android 事件传递机制初涉 我们知道 Button ...

最新文章

  1. 在node.js中,使用基于ORM架构的Sequelize,操作mysql数据库之增删改查
  2. python端午dragboat消消乐 美轮美奂的界面效果
  3. Hutool之集合工具——CollectionUtil
  4. 阿里程序员工作小技巧:理解CPU分支预测,提高代码效率
  5. 腾讯云,搭建Docker环境
  6. C#垃圾回收(GC)
  7. laravel artisan
  8. Lesson 021 —— python 循环语句
  9. go get 加 -u 和不加 -u 参数的区别
  10. MKL学习——线性代数概念相关
  11. 计蒜客——数位反转问题(代码+解析)
  12. Remoting學習(三)----Reomoting 基楚知識
  13. 程序员生存定律——打造属于自己的稀缺性(转载)
  14. okhttp返回内容乱码_okHttp 采坑指南-interceptor(乱码)
  15. 共享单车数据集_共享单车数据的数据可视化
  16. 夜神/逍遥/雷电/51/网易MUMU/腾讯手游助手等模拟器多开模拟器分配不同ip的办法
  17. 万字长文人脸识别深度研究:发展与市场、市场研究、流程及主要技术、行业应用、产品落地和个人看法
  18. Windows 桌面应用开发入门
  19. cannot be cast to
  20. 南京配眼镜避坑指南,轻松找到高性价比眼镜店

热门文章

  1. 广发银行李怀根:架构设计要有前瞻性,敢于对标敢于重构
  2. 一个排列中任意两个元素对换,排列改变奇偶性。
  3. foreach - gt 写法java_java map在JSTL EL中的小应用--lt;c:forEachgt;遍历Maplt;gt;泛型
  4. oracle 修改字段类型流程
  5. 提醒信用卡还款日的软件叫什么?
  6. 在智联招聘上用scrapy+selenium 进行简历自动投递
  7. 学习笔记(05):人工智能-必备数学基础视频教程-奇异值的分解
  8. 从仪表到能源统计到智慧能源系统建设
  9. VC中自定义消息ON_MESSAGE的用法
  10. 虚拟跳线软件干什么用的_这个是什么【虚拟实验软件】?