美文网首页Android进阶之路Android技术知识
Framework事件机制——手撕Android事件处理的三种方

Framework事件机制——手撕Android事件处理的三种方

作者: 码农的地中海 | 来源:发表于2022-06-17 21:33 被阅读0次

    一、背景

    Android的事件处理的三种方法:

    1、基于监听的事件处理机制

    setOnClickListener,setOnLongClickListener、setOnTouchListener

    注意:如果onTouchEvent方法return true,则单击事件和长摁事件不再执行;若onLongClick方法返回true,则单击事件不再处理。

    2、基于回调的事件处理机制

    需要定义继承组件的类,重写回调方法Touch方法执行时,先被Activity捕获,DispatchTouchEvent方法处理。return false,交给上层的onTouchEvent方法处理;return super.dispatchTouchEvent(ev),则传递给最外层的View。

    View用Dispatch方法处理,return false,由上层的onTouchEvent方法处理。如果返回super.dispatchTouchEvent(ev),则本层的onInterceptTouchEvent拦截,如果拦截true,则拦截,false不拦截,传递给子View的DispatchTouchEvent处理。

    常用的回调方法:onKeyDown,onKeyLongPress,onKeyUp,onTouchEvent,onTrackballEvent(轨迹球事件)监听和回调同时存在时,先调用监听。

    3、Handler消息处理

    二、Android基于监听

    1.基于监听的时间处理机制模型:

    流程模型图:

    image.png

    2.监听的三要素:

    Event source 事件源
    Event 事件
    Event Listener 事件监听器
    下面我们来看一下点击事件和触摸事件的监听三要素具体是那部分:

    1.点击事件

    由于点击事件比较简单,系统已经帮我们处理了,并没有找到具体事件是哪个。

    image.png

    2.触摸事件

    image.png

    归纳:

    事件监听机制是一种委派式的事件处理机制,事件源(组件)事件处理委托给事件监听器 当事件源发生指定事件时,就通知指定事件监听器,执行相应的操作


    常⽤监听接⼝

    View.OnClickListener 单击事件监听器必须实现的接⼝
    View.OnCreateContextMenuListener 创建上下⽂菜单事件
    View.OnFocusChangeListener 焦点改变事件
    View.OnKeyListener 按键事件监听器
    View.OnLongClickListener 长按事件监听器
    View.OnTouchListener 触摸屏事件监听器

    3.基于监听的事件处理机制

    ⾸先,事件监听机制中由事件源,事件,事件监听器三类对象组成。
    事件监听器处理流程:

    1. 为事件源(例如:button)设置⼀个监听器,⽤于监听⽤户的操作(点击操作等)
    2. ⽤户做出的操作触发事件源的监听器
    3. ⾃动⽣成对应的事件对象
    4. 将事件源对象作为参数传给事件监听器
    5. 事件监听器对事件对象进⾏判断,执⾏对应的事件处理器(处理⽅法)

    在此以OnClickListener单击事件为例使用intent来实现页面的跳转

    一、内部类形式实现监听

    <TextView
    //id值
            android:id="@+id/test2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="内部类"
            android:gravity="center"
            android:textSize="20dp"
            android:textColor="#fff"/>
    
    public class MainActivity extends AppCompatActivity{
    
    //定义一个TextView对象
        private TextView textView2;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    //获得事件源
            textView = findViewById(R.id.test2);
    //确定事件为点击事件,绑定监听器到事件源
            textView.setOnClickListener(new myListener());
        }
        //内部类实现页面跳转
        private class myListener implements View.OnClickListener {
            @Override
            public void onClick(View v) {
        //采用显示Intent启动第二个页面
                startActivity(new Intent(MainActivity.this,internalActivity.class));
            }
        }
    }
    

    二、匿名内部类实现

    <TextView
            android:id="@+id/test3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="匿名内部类"
            android:gravity="center"
            android:textSize="20dp"
            android:textColor="#fff"/>
    
    public class MainActivity extends AppCompatActivity {
    
        private TextView textView1;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    //获得事件源
            textView1 = findViewById(R.id.test3);
            //匿名内部类实现跳转 (实现监听器,绑定监听器到事件源要同步进行)
            textView1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivity(new Intent(MainActivity.this,anonymousActivity.class));
                }
            });
        }
    }
    

    三、类本身实现监听器

    <TextView
            android:id="@+id/test4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="类本身打开浏览器"
            android:gravity="center"
            android:textSize="20dp"
            android:textColor="@color/colorWhite"/>
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        private TextView textView2;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            //获得事件源
            textView2 = findViewById(R.id.test4);
            //绑定监听器到事件源
            textView2.setOnClickListener(this);
        }
    //类本身实现 浏览器跳转
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.test4:
                //采用隐式intent
                    Intent intent = new Intent();
                    intent.setAction(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse("http://www.baidu.com"));
                    startActivity(intent);
                    break;
            }
        }
    }
    

    本节小结

    本节给大家介绍了Android中的事件处理机制,例子中的是onClickListener点击事件,当然除了这个以外还有其他的事件,比如onItemClickListener,凡是需要通过setXxxListener这些,基本上都是基于事件监听的! 另外这五种方式用的比较多的是:1,2,3,5几种,看具体情况而定~

    本节给大家介绍了Android中的事件处理机制,例子中的是onClickListener点击事件,当然除了这个以外还有其他的事件,比如onItemClickListener,凡是需要通过setXxxListener这些,基本上都是基于事件监听的!

    三、Android基于回调

    1、回调事件处理原理

    监听事件处理是事件源与事件监听器分开的而基于回调的事件处理UI组件不但是事件源,而且还是事件监听器,通过组件的相关回调方法处理对应的事件。

    2、回调事件应用步骤

    Ⅰ. 自定义View类,继承自需要的View UI类。ex :自定义 MyButton按钮类 extends 基础Button类

    Ⅱ. 复写回调函数。ex:public boolean onTouchEvent(MotionEvent event)

    每一个事件回调方法都会返回一个boolean值,①.如果返回true:表示该事件已被处理,不再继续向外扩散,②.如果返回false:表示事件继续向外扩散

    而说到基于回调就离不开监听机制

    回调机制与监听机制的区别:

    • 如果说事件监听机制是⼀种委托式的事件处理,那么回调机制则恰好与之相反:对于基于回调机制的事件处理模型来说,事件源与事件监听器是统⼀的,或者说事件监听器完全消失了。

    • 当⽤户在GUI组件上激发某个事件时,组件⾃⼰特定的⽅法将会负责处理该事件。

    • 监听机制的事件源与事件监听是分开的。我们需要自己设置一个监听器,回调机制的事件源与事件监听是绑定在一起的。

    1、boolean类型

    几乎所有基于回调的事件处理方法都有一个boolean类型的返回值,该返回值用于表示该处理方法是否能完全处理该事件。
    如果处理事件的回调方法返回true,表明该处理方法已经完全处理改事件,该事件不会传播出去。
    如果处理事件的回调方法返回false,表明该处理方法并未完全处理该事件,该事件会传播出去。
    对于基于回调的时间传播而言,某组件上所发生的事件不仅会激发该组件上的回调方法,也会触发该组件所在Activity的回调方法——只要事件能传播到该Activity。

    实例

    1、MyButton 子类
    public class MyButton extends AppCompatButton {
        public MyButton(Context context , AttributeSet set)
        {
            super(context , set);
        }
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event)
        {
            super.onKeyDown(keyCode , event);
            Log.v("-MyButton-", "the onKeyDown in MyButton");
            // 返回false,表明并未完全处理该事件,该事件依然向外扩散
            return true;
        }
    }
    
    2、MainActivity
    public class MainActivity extends AppCompatActivity {
    
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button bn = (Button) findViewById(R.id.bn);
            Log.v("-Listener-", "the onKeyDown in Listener");
            // 为bn绑定事件监听器
            bn.setOnKeyListener(new OnKeyListener() {
                @Override
                public boolean onKey(View source
                        , int keyCode, KeyEvent event) {
                    // 只处理按下键的事件
                    if (event.getAction() == KeyEvent.ACTION_DOWN) {
                        Log.v("-Listener-", "the onKeyDown in Listener");
                    }
                    // 返回false,表明该事件会向外传播
                    return false; 
                }
            });
        }
        // 重写onKeyDown方法,该方法可监听它所包含的所有组件的按键被按下事件
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event)
        {
            super.onKeyDown(keyCode , event);
            Log.v("-Activity-" , "the onKeyDown in Activity");
            //返回false,表明并未完全处理该事件,该事件依然向外扩散
            return true;
        }
    }
    

    这里是在模拟器里进行的测试,这里按下键盘(而不是点击),会看到 logcat 中的输出,如下:

    V/-Listener-: the onKeyDown in Listener
    V/-MyButton-: the onKeyDown in MyButton
    V/-Activity-: the onKeyDown in Activity
    

    2、Override组件类的事件处理函数实现事件的处理。

    举例:

    View类实现了KeyEvent.Callback接口中的一系列回调函数,因此,基于回调的事件处理机制通过自定义View来实现,自定义View时重写这些事件处理方法即可。

    `public` `interface` `Callback { ` ` ``// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于 ` ` ``// 标识该处理函数是否能完全处理该事件 ` `// 返回true,表明该函数已完全处理该事件,该事件不会传播出去 ` `// 返回false,表明该函数未完全处理该事件,该事件会传播出去 ` `  ``boolean` `onKeyDown(``int` `keyCode, KeyEvent event); ` `  ``boolean` `onKeyLongPress(``int` `keyCode, KeyEvent event); ` `  ``boolean` `onKeyUp(``int` `keyCode, KeyEvent event); ` `  ``boolean` `onKeyMultiple(``int` `keyCode, ``int` `count, KeyEvent event); ` `} ` `public` `interface` `Callback {`` ``// 几乎所有基于回调的事件处理函数都会返回一个boolean类型值,该返回值用于`` ``// 标识该处理函数是否能完全处理该事件``// 返回true,表明该函数已完全处理该事件,该事件不会传播出去``// 返回false,表明该函数未完全处理该事件,该事件会传播出去``  ``boolean` `onKeyDown(``int` `keyCode, KeyEvent event);``  ``boolean` `onKeyLongPress(``int` `keyCode, KeyEvent event);``  ``boolean` `onKeyUp(``int` `keyCode, KeyEvent event);``  ``boolean` `onKeyMultiple(``int` `keyCode, ``int` `count, KeyEvent event);``}`
    

    比对

    • 基于监听器的事件模型符合单一职责原则,事件源和事件监听器分开实现。
    • Android的事件处理机制保证基于监听器的事件处理会优先于基于回调的事件处理被触发。
    • 某些特定情况下,基于回调的事件处理机制会更好的提高程序的内聚性。

    四、Handler消息处理

    一.什么是Handler

    Handler是一个消息分发对象。

    Handler是Android系统提供的一套用来更新UI的机制,也是一套消息处理机制,可以通过Handler发消息,也可以通过Handler处理消息。

    二、Handler的工作原理

    在下面介绍Handler机制前,首先得了解以下几个概念:

    1. Message 消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。
      Message Queue 消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
    2. Handler Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。
    3. Looper 循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。
      线程 UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue。每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。

    三、Handler的运行流程

    在子线程执行完耗时操作,当Handler发送消息时,将会调用 MessageQueue.enqueueMessage,向消息队列中添加消息。 当通过 Looper.loop开启循环后,会不断地从消息池中读取消息,即调用 MessageQueue.next, 然后调用目标Handler(即发送该消息的Handler)的 dispatchMessage方法传递消息, 然后返回到Handler所在线程,目标Handler收到消息,调用 handleMessage方法,接收消息,处理消息。

    image.png

    四、源码分析

    在子线程创建Handler

    class LooperThread extends Thread {
        public Handler mHandler;
        public void run() {
            Looper.prepare();
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            }
            ;
            Looper.loop();
        }
    }
    

    从上面可以看出,在子线程中创建Handler之前,要调用 Looper.prepare()方法,Handler创建后,还要调用 Looper.loop()方法。而前面我们在主线程创建Handler却不要这两个步骤,因为系统帮我们做了。

    主线程的Looper

    在ActivityThread的main方法,会调用 Looper.prepareMainLooper()来初始化Looper,并调用 Looper.loop()方法来开启循环。

    public final class ActivityThread extends ClientTransactionHandler {
        // ...    
        public static void main(String[] args) {
            // ...        
            Looper.prepareMainLooper();
            // ...        
            Looper.loop();
        }
    }
    

    五、Looper

    从上可知,要使用Handler,必须先创建一个Looper。

    初始化Looper

    public final class Looper {
        public static void prepare() {
            prepare(true);
        }
        private static void prepare(Boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }
        public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
        private Looper(Boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
        // ...
    }
    

    从上可以看出,不能重复创建Looper,每个线程只能创建一个。创建Looper,并保存在 ThreadLocal。其中ThreadLocal是线程本地存储区(Thread Local Storage,简称TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

    开启Looper

    public final class Looper {
        // ...    
        public static void loop() {
            // 获取TLS存储的Looper对象        
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;
            // 进入loop主循环方法        
            for (;;) {
                Message msg = queue.next();
                // 可能会阻塞,因为next()方法可能会无线循环            
                if (msg == null) {
                    // No message indicates that the message queue is quitting.                
                    return;
                }
                // This must be in a local variable, in case a UI event sets the logger            
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +                        
                                                    msg.callback + ": " + msg.what);
                }
                // ...            
                final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
                final long dispatchEnd;
                try {
                    // 获取msg的目标Handler,然后分发Message                
                    msg.target.dispatchMessage(msg);
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                }
                finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                // ...            
                msg.recycleUnchecked();
            }
        }
    }
    

    六、Handler

    创建Handler

    public class Handler {
        // ...    
        public Handler() {
            this(null, false);
        }
        public Handler(Callback callback, Boolean async) {
            // ...        
            // 必须先执行Looper.prepare(),才能获取Looper对象,否则为null        
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(                
                      "Can't create handler inside thread " + Thread.currentThread()                        
                      + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            // 消息队列,来自Looper对象        
            mCallback = callback;
            // 回调方法        
            mAsynchronous = async;
            // 设置消息是否为异步处理方式
        }
    }
    

    发送消息

    子线程通过Handler的post()方法或send()方法发送消息,最终都是调用 sendMessageAtTime()方法。

    post方法:

    public final Boolean post(Runnable r){
        return sendMessageDelayed(getPostMessage(r), 0);
    }
    public final Boolean postAtTime(Runnable r, long uptimeMillis){
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    public final Boolean postAtTime(Runnable r, Object token, long uptimeMillis){
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }
    public final Boolean postDelayed(Runnable r, long delayMillis){
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    

    send方法:

    public final Boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }
    public final Boolean sendEmptyMessage(int what){
        return sendEmptyMessageDelayed(what, 0);
    }
    public final Boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    public final Boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
    public final Boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    sendMessageAtTime()
    public Boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(            
                      this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    private Boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    七、分发消息

    在loop()方法中,获取到下一条消息后,执行 msg.target.dispatchMessage(msg),来分发消息到目标Handler。

    public class Handler {
        // ...    
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                // 当Message存在回调方法,调用该回调方法            
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    // 当Handler存在Callback成员变量时,回调其handleMessage()方法                
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                // Handler自身的回调方法            
                handleMessage(msg);
            }
        }
        private static void handleCallback(Message message) {
            message.callback.run();
        }
    }
    

    八、Handler的简答使用

    在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。

    public class Activity extends android.app.Activity {
        private Handler mHandler = new Handler(){
            @Override        
                    public void handleMessage(Message msg) {
                super.handleMessage(msg);
                // 更新UI
            }
        }
        ;
        @Override    
        public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
            super.onCreate(savedInstanceState, persistentState);
            setContentView(R.layout.activity_main);
            new Thread(new Runnable() {
                @Override            
                             public void run() {
                    // 执行耗时任务                ...                
                    // 任务执行完后,通知Handler更新UI                
                    Message message = Message.obtain();
                    message.what = 1;
                    mHandler.sendMessage(message);
                }
            }
            ).start();
        }
    }
    

    五、总结

    本文讲解了三个方面;Android事件机制;基于监听、基于回调以及Handler消息处理。还有许多没有讲解到的知识点,我总结在了整理的一套Android进阶笔记里面;需要学习进阶的同学可以前往获取:Frame Work源码解析手册Android核心技术进阶手册、实战笔记、面试题纲资料

    相关文章

      网友评论

        本文标题:Framework事件机制——手撕Android事件处理的三种方

        本文链接:https://www.haomeiwen.com/subject/blbhvrtx.html