在纠结了一番了之后,还是决定整理一下事件处理机制相关的笔记,结合菜鸟教程。 原因在于今天抽空将以前的笔记给分了个类,发现自己的分类清单中竟然没有设计模式!!! 这简直就比较滑稽了,对于一个面向对象语言的学习者,竟然连设计模式都了解的不仔细,以及它具体用法不了解,确实说不过去。 但是,这个又是一个需要经验的积累,以及实战的锻炼才能体会总结的,所以,发现android 的事件处理机制是一个非常不错的demo。
言归正传,android 的事件处理机制主要有两种: 基于 回调的事件处理机制; 基于监听的处理机制。
基于监听的事件处理机制:
使用最频繁的一种;
通过大牛给出的事件监听机制的整个流程,我发现这样一些疑问以及有可能的答案:
1.基于监听的事件处理机制,有一个关键就是事件注册。 但是我们在实践的时候并没有自己手动的为某个视图控件注册监听器。
解答: 我们会经常用到 诸如 setOnclickListener(),OnTouchListener()方法等。 从字面意义理解,它为设置...监听器。 但是,它 跟注册还是颇有一些区别的。 我想注册实践监听器,就是将它挂在在一个线程上,也就是说有一个事件监听线程,那么,有事件的视图,就至少是双线程的程序了。 不过很可惜,在去看set..Listener的源码的时候,是看不到它在java源码方面的具体实现的。 也就是说,要么它依赖操作系统实现,要么它依赖jni实现,并且,事件线程由jni管理。 换言之,实现注册监听是由ni实现的。
2.事件源的触发流程:
解答: 学习过操作系统朋友应该知道,操作系统的很多操作都是通过中断来完成。 同理,比如一个点击事件,android手机硬件中,包括了一个触摸屏的硬件,它分为内屏和外屏。 其中负责触发屏幕点击和触摸中断的为内屏。 内屏大概由五个层次构成,具体有什么用不知道,反正我拆过~~~ 从内屏上,当有电容屏感应的时候,会接收到你触摸的位置信息,甚至触摸力度!!! 这个消息经由系统中断(具有最高优先级,应该是由最高优先级的进程通知)发送给cpu,经由cpu通过进程间的消息机制传递给这个进程(当前正在用户界面运行的进程,这时候只有一个),也就是这个程序运行的内存空间的某个点。(或者说通过广播机制,将这个事件发送给所有的app也是有可能的)。
3.事件监听的核心在于事件的传递。
解答: 事件的传递,我们能够理解的地方应该是从jni进入java的时候(或者说虚拟机的时候), 这时,一个代表事件的实例对象已然生成。 这个实例对线在类间的传递或者说通信,主要是通过参数对象的传递。 学习java基础的时候,我们知道,java八大基本数据类型传递的是拷贝的副本,符合数据类型传递的是引用,或者说指针。。。 在64位机器上面,可以通过一个long型数据代表任意一个地址。 也就是说,他始终传递一个long型数据,开销还是非常小的。 此外对于监听器的设置有三种,匿名内部类,内部类,外部类。 其中外部类我们一般不用,但是理解的关键却恰恰在于外部类这里。 匿名内部类,内部类都能够引用到该View控件的相关实例。 这样的话,整个过程看上去就像这样的: 我们在至少定义好了一个activity内部,实力好了相关的属性,退出开发程序打包跑起来,通过触摸或者滑动等事件,导致程序 在非 人工的情况下自己发生了改变。。。 嗯,没错就是这样。。 监听器事件的核心就在于此。 实在是阐述的比较形象,我自己都佩服自己。
此外,对于外部类实现的监听器,要取得内部的属性,那么就需要通过对象传递的方式给将地址给传递过去。 因此又不由得开始思考,为什么内部类能够共享公共类的资源,外部类却不行呢?
外部类不能共享另一个类的资源,这个比较容易理解。 我们知道类必需经过加载才能使用。 那么如果一个类被加载过,它要去访问另外一个未被加载的类的资源自然是不行的。 此外,就算两个类都被加载后,要去访问另一个类对象的资源,那也是要满足一个前提就是必需是public的属性。。 这个是针对java语言本身机制的安全性考虑,而非设计上不能实现的原因所致。。 因此它是属于编译期的限定,而非运行期。
内部类能够共享公共类的资源,我想这应该是由于java内存划分的关系。 我们知道java运行时内存分未堆区与栈区。 栈区中放一些类的数据,静态属性和方法,以及常量。 堆区中放类的实例对象,以及实例对象内部的实例属性以及方法。至于堆栈所代表的物理意义与逻辑意义,我想之前的c算法练习已经理解的比较透彻。 于是就存在一种可能性,内部类是位于堆区的!! 不是说位于堆区的数据找不到位于栈区的数据,只是可能为了程序的健壮性,他们要考虑这两块地区的逻辑独立性以及性能的原因,不那样实现。
说到类与对象的关系,不得不提一提: 控制反转(IOC)与 类中属性方法的关系。
首先摆出基础知识: IOC,Inverse Of Control,控制反转,注意,这个“控制” 是名词。是spring框架的一个概念,它是基于jdk中的反射实现的。 控制反转包括依赖注入 和 依赖查找。
接触面向对象语言以来,况且又是接触最具代表性的,面向对象语言的佼佼者java,我们在思维惯性上,很自然的将类,属性,方法,构造方法给贴上标签。 但是这个标签是针对java世界的游戏规则而言的,也就是针对java的编译期而言的。 而整个虚拟世界的游戏规则,是由机器语言定义的。 那么到底是怎样的一种固化呢? 比如很自然的觉得,类中包含属性,方法,构造方法等等,好像在物理方面,类之间也是相互隔离的。 就像两个房子相邻,但是两个屋子内的人确实彼此隔离的,面向对象的本来意义也就是说要抽象物理世界,是的。 它成功了。 但是我们作为技术人,有必要透过现象看本质:是否位于这两个屋子的人就真的无法通信了? 类中的属性是否确确实实不能脱离类而存在?
我们摆脱java语言的限制,换到另外一门具有代表性的面向对象语言: c++。 我们发现他有一个有趣的数据类型: 结构体。 嗯呢,这就跟java中的类很像了;况且它本身也是具有类数据的类型的,只是感觉结构体更能说明问题。 结构体通常由结构名称定义,结构内容组成。 它没有实例化的概念,所以,它就是一块地址,一块只具有大小的地址。 这个地址可以被复用。 这个地址的内部的内容跟它是相对定位的关系; 这个地址以及地址内部的内容跟内存是绝对定位的关系。。。 类推之: 一个类代表一个地址块,类属性代表这个地址块中的某一部分内容,它们是相对定位的关系; 类 与类属性相对于内存 是绝对定位的关系。。 最终可以提炼一下: 当一个类被加载好,且实例化完成后,它,以及它内部的属性,或者说方法 相对于内存都是唯一的。 通过一个地址,可以唯一定位到它。 至于静态类,静态成员,静态方法,常量,不需要实例化就可以唯一的通过一个地址定位到它们。。 只不过定位后的地址一个在堆区,一个在栈区。。
中和以上观点,理解IOC容器: 考虑这样一种情况: 一个类的定义中,引用了另一个类作为它的属性。 我们常见的做法是,手动实例化一个对象,通过写入器,或者说构造方法给引用类提供支持。 为了解耦的需求,引入IOC容器。 IOC容器做了这样的事情: 将被引用的类给实例化了将它在内存中的绝对地址给记住,然后保存它的容器中。 引用类在加载实例化的时候,由于编译阶段的注解触发,实例对象的时候,去到IOC容器中找到该依赖,通过反射注入,实现实例化的效果。 这就是所谓的依赖注入。 至于依赖查找,我认为有两种可能性: 1,指的是在注入之前的依赖查找; 2,指的是后面由于项目的需求需要寻找某个类的实例。
总结,理论上来说,java的任何一个方法或者说属性都应该是可以独立出来的,可以用面向对象的思路抽象问题,但是需要用面向过程的思维解决问题。。。。
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
二. 基于回调的事件处理机制
可以实现接口和实现类的分离。
菜鸟没有给出整个流程,那么就只能推测一下了:
事件源--> 中断-->jni(调用接口)-->进程-->接口类实现-->jni-->操作系统
这个时候,我认为它的核心是接口对象的调用,调用的发起者为 jni,调用的接收者为最终的实现类。
我们之前学习的时候知道,所有的View控件,都支持回调方式的事件处理,因为View 是实现了键鼠的回调,同时所有的ui控件都是View的子类,因此理论上来说,所有的ui控件都支持基于回调的事件处理机制。。若未重写事件回调的处理,则默认应该是被拦截了,但是这样多少对性能还是有些影响的。 此外,性能与功能素来就是一个相矛盾的功能。。
包括四大组件除了BroadCastRecevier外,它们都实现了容器回调。 而这个容器回调,恰恰是提供它生命周期支持的一个功能!!! unbelievable! ! interesting!! 被我发现惊天大秘密!!!
关于最后的一点畅想就是:
既然所有的这些都是基于内存,那么,有个概念叫RPC,remote process control ,远程进程调用。 这比较有趣的地方在于,它既要通过tcp/ip协议定位物理主机,又要通过通过端口号进行虚拟主机定位,最后需要在操纵该物理主机对该进程做相关处理。。 不知道这样理解对不。。 不早了,还是睡吧!!