Android的消息循环与Handler机制理解

Stella981
• 阅读 775

一、概念

1、事件驱动型

什么是事件驱动?就是有事了才去处理,没事就躺着不动。假如把用户点击按钮,滑动页面等这些都看作事件,事件产生后程序就执行相应的处理方法,就是属于事件驱动型。

2、消息循环

把需要处理的事件表示成一个消息,并且把这个消息放入一个队列。消息循环就是一循环,for或者while都一样。从消息队列里面取出未处理的消息,然后调用该消息的处理方法。

3、Handler

最开始碰到handler是需要在子线程里面更新UI的时候。android的UI更新只能在主线程中进行,但是在子线程中执行的逻辑又需要更新UI,例如文件下载,在子线程中访问网络下载之后,就是更新下载进度。这个时候就需要使用Hanlder,准确的说是要发送一个进度更新的消息。什么是Handler?我的理解是消息的处理者。create消息对应一个create的Handler,destroy消息对应一个destroy的Handler。

二、实现

只是说说概念太假了,下面就来实现一个简单的消息处理机制。

1、Msg

把产生的事件用消息来表示,数据用各个参数传递。

public class Msg implements Serializable{
    //序列化标识
    private static final long serialVersionUID = -2414053244664115328L;
    
    //该消息的处理者。
    private int handlerId;
    
    //参数。
    public Object arg1;
    public Object arg2;

    //大量参数
    public Object array[];
    
    public Msg(int handlerId) {
        this.handlerId=handlerId;
    }
    
    public void setHandlerId(int handlerId) {
        this.handlerId=handlerId;
    }
    public int getHandleId() {
        return handlerId;
    }
}

2、Handler

事件的处理者

public abstract class Handler {
    //唯一标识,由Looper分配
    private int id;
    
    //使用该Handler的Looper
    private Looper looper;
    
    public Handler(Looper looper) {
        this.looper=looper;
        id=looper.addMsgHandler(this);
    }
    
    //消息处理函数
    abstract public  boolean handleMsg(Msg msg);
    
    //添加一个未处理消息。
    public void sendMsg(Msg msg) {
        looper.addMsg(msg);
    }
    
    //返回该handler的信使。
    public Msg obtainMsg() {
        return new Msg(id);
    }
}

3、Looper

消息循环

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

public class Looper {
    private int handlerCount=0;
    //消息队列。
    private Queue<Msg> msgQueue=new LinkedList<Msg>();
    //消息的处理。
    private Map<Integer, Handler> msgHandler=new HashMap<>();
    
    //loop
    public void loop() {
        for(;true;)
            if(!msgQueue.isEmpty())
                if(!distributeMsg(msgQueue.poll()))
                //当消息处理返回false时。程序结束。
                    break;    
    }
    
    //添加处理消息的handler
    public int addMsgHandler(Handler handler) {
        handlerCount++;
        msgHandler.put(handlerCount,handler);
        return handlerCount;
    }
    
    //添加待处理的消息
    public void addMsg(Msg msg) {
        msgQueue.add(msg);
    }
    
    //消息分发
    private boolean distributeMsg(Msg msg) {
        Handler handler=msgHandler.get(msg.getHandleId());
        if(handler!=null) {
            handler.handleMsg(msg);
        }else {
            //出现未知消息,程序结束。
            System.out.println("exit");
            return false;
        }
        return true;    
    }
}

4、模拟生命周期

abstract class Basic{
    private Looper mainLooper=new Looper();
    private Handler sysHandler=new Handler(mainLooper) {
                @Override
                public boolean handleMsg(Msg msg) {
                    if(msg.arg1.equals("create")) {
                        onCreate();
                    }
                    if(msg.arg1.equals("destroy")) {
                        onDestroy();
                    }
                    return true;
                }
            };
    
    public Basic() {
        Msg m=sysHandler.obtainMsg();
        m.arg1="create";
        sysHandler.sendMsg(m);
        //新获取一个Msg,不能沿用上一个。
        m=sysHandler.obtainMsg();
        m.arg1="destroy";
        sysHandler.sendMsg(m);
    }
    
    public Looper getMainLooper() {
        return mainLooper;
    }
    
    /**
     * 生命周期
     */
    abstract public void onCreate();
    abstract public void onDestroy();
}

上面的代码创建了一个抽象类Basic,在里面注册了一个处理create和destroy两个消息的Handler。

5、子线程调用主线程的方法。

public class Main extends Basic{
    private final Handler handler
        =new Handler(getMainLooper()) {
        @Override
        public boolean handleMsg(Msg msg) {
            // TODO Auto-generated method stub
            System.out.println("msg.arg1="+msg.arg1+",Tid="+
            Thread.currentThread().getId());
            getMainLooper().addMsg(new Msg(-1));
            return true;
        }
    };
    public void onCreate() {
        System.out.println("..............onCreate");
        System.out.println("mainThread Tid="+Thread.currentThread().getId());
        new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                System.out.println("childThread Tid="+Thread.currentThread().getId());
                Msg msg=handler.obtainMsg();
                msg.arg1="childCall";
                handler.sendMsg(msg);
            }
        }).start();
    }
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        System.out.println(".............onDestroy");
        //getMainLooper().addMsg(new Msg(-1));
    }
    public static void main(String[] args) {
        new Main().getMainLooper().loop();
    }
}

6、结果

Android的消息循环与Handler机制理解

结果分析,首先是两个生命周期的方法被调用,其次是实现了子线程调用主线程的方法。这里子线程转到主线程的原因是因为Looper运行在主线程,消息由Looper分发处理。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
元旦 元旦
3年前
每日一问(一)Handler相关知识
1、Handler负责发送Message和处理Mesage2、Message就是消息载体,可用what区分,也可传递对象3、MessageQueue消息队列,存储Message4、Looper循环取出MessageQueue里的Message交给Handler处理。5、一个线程只有一个Looper和MessageQueue,子线程中使用Handler一
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
九路 九路
4年前
Android HandlerThread源码解析
在上一章Handler源码解析文章中,我们知道App的主线程通过Handler机制完成了一个线程的消息循环。那么我们自己也可以新建一个线程,在线程里面创建一个Looper,完成消息循环,可以做一些定时的任务或者写日志的功能。这就是HandlerThread的作用AndroidHandler消息机制源码解析(https://www.cnblogs.co
Stella981 Stella981
3年前
Handler更新UI的几种方式
Handler、loop、MessageQueue的工作原理Message:Handler接收和处理的消息对象Looper:每个线程只能拥有一个looper.它的loop方法负责读取MessageQueue中的消息,读到信息之后就把消息返回给handler处理MessageQueue:消息队列。程序创建Looper对象时,会在它的构造器中创建
Stella981 Stella981
3年前
Android消息循环分析
我们的常用的系统中,程序的工作通常是有事件驱动和消息驱动两种方式,在Android系统中,Java应用程序是靠消息驱动来工作的。消息驱动的原理就是:1\.有一个消息队列,可以往这个队列中投递消息;2\.有一个消息循环,不断从消息队列中取出消息,然后进行处理。在Android中通过Looper来封装消息循环,同时在其中封装了一个消息队
Stella981 Stella981
3年前
Android里面所说的Looper
Looper即:有消息循环的线程。在Android里线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新概念。主线程(UI线程)就是一个消息循环的线程。针对这种消息循环的机制,引入一个新的机制Handle,有消息循环,就要往消息循环里面发送相应的消息,自定义消息一般都会有对应的处理,消
Stella981 Stella981
3年前
Python的条件锁与事件共享
1:事件机制共享队列:利用消息机制在两个队列中,通过传递消息,实现可以控制的生产者消费者问题要求:readthread读时,writethread不能写;writethread写时,readthread不能读。基本方法时间类(Event)·set:设置事件。将标志位设为True。
Stella981 Stella981
3年前
Noark入门之协议映射
0x00消息控制器消息控制器,主要作用就是为每个模块提供消息处理的入口.这里的消息不仅仅是协议,还有内部指令,事件等等逻辑入口,这也是为了响应线程模型作出的一种支撑,只要入口在此消息控制器内,那必然走期望的线程调度。@Controller用于标识一个类为当前模块的消息控制器入口.@Controller(threadGroup
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这