草庐IT

手写Android-Handler

h2coder 2023-03-28 原文

Handler机制,是Android系统的消息队列,一般用于处理主线程的页面绘制等消息处理。

前言

Handler除了Java层外,还有Native的部分,但我能力有限,只能仿写一个Java层的Handler,并且支持延时消息。项目地址:MiniHandler

注:由于消息机制需要使用一个while死循环来让线程不能结束,所以直接将MiniHandler在Android主线程中使用会卡住主线程,所以需要用一个子线程来运行MiniHandler,一般使用HandlerThread,对应到MiniHandlerMiniHandlerThread

简单介绍

  • 延时消息功能:使用DelayQueue实现,它需要队列中的消息实体实现一个Delayed接口,它继承于Comparable接口,它要求复写一个getDelay()方法,返回延时多长毫秒。以及需要复写compareTo()方法,用于比较2个消息谁先谁后,来决定队列中的消息排序。

类结构介绍

  • Looper,轮训器,内部包含一个MessageQueue消息队列,并且使用ThreadLocal将每个线程绑定一个Looper。所以一个线程只有一个Looper,一个Looper也只有一个MessageQueue消息队列
  • MessageQueue,消息队列,使用DelayQueue实现队列功能,支持延时消息
  • Message,消息实体类,使用享元模式支持消息对象复用
  • MiniHandler,消息处理器以及消息发送器
  • MiniHandlerThread,原自Android源码中的HandlerThread,改用MiniHandler实现,就是一个带有Handler事件循环的子线程

原理

  • Handler,发送Message消息到MessageQueue,会被Looper轮训器取出
  • Looper轮训器中,有一个while循环,不断从MessageQueue消息队列中取出消息进行处理,如果队列中没有消息,MessageQueue会阻塞当前线程
  • Handler处理完消息,Message消息回收到池中,复用消息能提高效率,以及减少对象创建

基本使用

MiniHandler的API和Handler的一致,2者的Java层API只是类名不同。

/**
 * Toast任务
 */
public static final int ACTION_TOAST = 1;
/**
 * 延时任务
 */
public static final int ACTION_DELAY = 2;

////事件处理线程
MiniHandlerThread handlerThread = new MiniHandlerThread("handler-thread");
handlerThread.start();

//创建MiniHandler实例,并绑定到MiniHandlerThread
MiniHandler  eventHandler = new MiniHandler(mHandlerThread.getLooper()) {
    @Override
    public void handleMessage(Message message) {
        super.handleMessage(message);
        long action = message.what;
        if (action == ACTION_TOAST) {
            String msg = message.obj.toString();
            ToastUtil.toast(getApplicationContext(), msg);
        } else if (action == ACTION_DELAY) {
            String delayMsg = message.obj.toString();
            ToastUtil.toast(getApplicationContext(), delayMsg);
        }
    }
};

//发消息到立即执行事件线程
eventHandler.sendMessage(Message.obtain(ACTION_TOAST, "Toast~"));
//或者
eventHandler.post(new Runnable() {
    @Override
    public void run() {
      ToastUtil.toast(getApplicationContext(), 延时消息~);
    }
});

//发送延时消息到事件线程
eventHandler.sendMessageDelayed(Message.obtain(ACTION_DELAY, "延时消息~"), 2000);
//或者
eventHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
      ToastUtil.toast(getApplicationContext(), 延时消息~);
    }
}, 1000);

//退出MiniHnadlerThread
handlerThread.quitSafely();

源码

Message消息实体

消息实体类,使用享元模式支持消息对象复用。为了支持延时消息,消息实体,需要实现一个Delayed接口,它继承于Comparable接口,它要求复写一个getDelay()方法,返回延时多长毫秒。以及需要复写compareTo()方法,用于比较2个消息谁先谁后,来决定队列中的消息排序。

/**
 * 消息事件实体
 */
public class Message implements Delayed {
    /**
     * 使用中的标志位
     */
    static final int FLAG_IN_USE = 1;

    int flags;
    /**
     * 为了形成消息链表
     */
    Message next;
    /**
     * 对象锁
     */
    public static final Object sPoolSync = new Object();
    /**
     * 消息链表的头节点
     */
    private static Message sPool;
    /**
     * 当前链表中数据的个数
     */
    private static int sPoolSize = 0;
    /**
     * 总共可使用的消息链表大小
     */
    private static final int MAX_POOL_SIZE = 50;
    /**
     * 消息的标识
     */
    public long what;
    /**
     * 消息的附件
     */
    public Object obj;
    /**
     * 消息的处理器
     */
    public MiniHandler target;
    /**
     * 要执行的任务,可为null
     */
    public Runnable callback;
    /**
     * 指定的执行时间,毫秒值
     */
    public long workTimeMillis;

    /**
     * 回收Message
     */
    void recycleUnchecked() {
        //把标记设置为使用中
        flags = FLAG_IN_USE;
        //清理所有字段
        what = 0;
        obj = null;
        target = null;
        callback = null;
        workTimeMillis = 0;
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

    /**
     * 创建一个Message对象
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            //判断头节点是否null
            if (sPool != null) {
                //取出头节点
                Message m = sPool;
                //将头节点的下一个作为最新的头节点
                sPool = m.next;
                //设置需要返回的消息的next为空
                m.next = null;
                //清除使用中的标志位
                m.flags = 0;
                sPoolSize--;
                return m;
            }
        }
        //如果消息链表为空 创建新的message
        return new Message();
    }

    /**
     * 创建一个Message对象,并绑定处理它的Handler
     */
    public static Message obtain(MiniHandler handler) {
        return obtain(handler, 0);
    }

    /**
     * 创建一个Message对象,并绑定处理它的Handler、消息标识what
     */
    public static Message obtain(MiniHandler handler, long what) {
        return obtain(handler, what, null);
    }

    /**
     * 创建一个Message对象,绑定消息标识what
     */
    public static Message obtain(long what) {
        return obtain(what, null);
    }

    /**
     * 创建一个Message对象,绑定消息标识what、消息附件obj
     */
    public static Message obtain(long what, Object obj) {
        return obtain(null, what, obj);
    }

    /**
     * 创建一个Message对象,并绑定处理它的Handler、消息标识what、消息附件obj
     */
    public static Message obtain(MiniHandler handler, long what, Object obj) {
        Message message = obtain();
        message.target = handler;
        message.what = what;
        message.obj = obj;
        return message;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(workTimeMillis
                - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }
}

MessageQueue消息队列

消息队列,使用DelayQueue实现队列功能,支持延时消息

public class MessageQueue {
    /**
     * 是否退出
     */
    volatile boolean isQuit;
    /**
     * 消息队列
     */
    private final BlockingQueue<Message> mMessageQueue = new DelayQueue<>();

    /**
     * 消息入队
     */
    public boolean enqueueMessage(Message message, long uptimeMillis) {
        try {
            message.workTimeMillis = uptimeMillis;
            mMessageQueue.put(message);
            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 拿出一条消息
     */
    public Message next() {
        try {
            //如果队列中没有消息,就会阻塞在这里
            return mMessageQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 移除队列中指定Handler的未执行的回调和消息
     */
    public final void removeCallbacksAndMessages(MiniHandler handler) {
        if (handler == null) {
            return;
        }
        List<Message> targetMessages = new ArrayList<>();
        for (Message message : mMessageQueue) {
            if (message.target == handler) {
                targetMessages.add(message);
            }
        }
        mMessageQueue.removeAll(targetMessages);
    }

    public void quit() {
        mMessageQueue.clear();
        isQuit = true;
    }
}

Looper轮训器

轮训器,内部包含一个MessageQueue消息队列,并且使用ThreadLocal将每个线程绑定一个Looper。所以一个线程只有一个Looper,一个Looper也只有一个MessageQueue消息队列

public class Looper {
    private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
    /**
     * 消息队列
     */
    final MessageQueue mMessageQueue;

    private Looper() {
        //一个线程,只有一个Looper,一个Looper也只有一个消息队列
        mMessageQueue = new MessageQueue();
    }

    /**
     * 把当前线程和Looper进行绑定
     */
    public static void prepare() {
        Looper looper = sThreadLocal.get();
        if (looper != null) {
            throw new RuntimeException("一个线程只能绑定一个Looper,请确保prepare方法在一个线程中只调用一次");
        }
        sThreadLocal.set(new Looper());
    }

    /**
     * 获取当前线程的Looper
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

    /**
     * 开始循环从队列中取出消息
     */
    public static void loop() {
        //获取当前线程的轮询器
        Looper looper = myLooper();
        MessageQueue queue = looper.mMessageQueue;
        while (!queue.isQuit) {
            Message message = queue.next();
            try {
                message.target.dispatchMessage(message);
            } finally {
                //回收Message对象
                message.recycleUnchecked();
            }
        }
    }

    /**
     * 安全退出,会等所有事件都执行完,再关闭
     */
    public void quitSafely() {
        mMessageQueue.quit();
    }
}

MiniHandler消息处理器

消息处理器以及消息发送器

public class MiniHandler {
    /**
     * 消息队列
     */
    private final MessageQueue mMessageQueue;
    /**
     * 事件回调,可以选择构造时传入一个Callback,就可以不需要复写handleMessage()方法进行处理
     */
    private MiniHandler.Callback mCallback;

    public MiniHandler() {
        this(Looper.myLooper());
    }

    public MiniHandler(Callback callback) {
        Looper looper = Looper.myLooper();
        //Looper没有绑定
        if (looper == null) {
            throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
        }
        mMessageQueue = looper.mMessageQueue;
        mCallback = callback;
    }

    public MiniHandler(Looper looper) {
        //Looper没有绑定
        if (looper == null) {
            throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
        }
        mMessageQueue = looper.mMessageQueue;
    }

    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(Message msg);
    }

    /**
     * 移除队列中指定Handler的未执行的回调和消息
     */
    public final void removeCallbacksAndMessages() {
        mMessageQueue.removeCallbacksAndMessages(this);
    }

    /**
     * 发送消息到消息队列中
     */
    public boolean sendMessage(Message message) {
        return sendMessageDelayed(message, 0);
    }

    /**
     * 在主线程执行一个Runnable
     */
    public final boolean post(Runnable task) {
        return sendMessageDelayed(getPostMessage(task), 0);
    }

    /**
     * 延迟一段时间后,在主线程执行一个Runnable
     *
     * @param delayMillis 延时时间,毫秒值
     */
    public final boolean postDelayed(Runnable task, long delayMillis) {
        return sendMessageDelayed(getPostMessage(task), delayMillis);
    }

    /**
     * 获取一个带Runnable任务的Message
     */
    private static Message getPostMessage(Runnable task) {
        Message m = Message.obtain();
        m.callback = task;
        return m;
    }

    /**
     * 发送延时消息
     *
     * @param message     消息
     * @param delayMillis 延时时间
     */
    public final boolean sendMessageDelayed(Message message, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(message, System.currentTimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(Message message, long uptimeMillis) {
        MessageQueue queue = this.mMessageQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, message, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message message, long uptimeMillis) {
        message.target = this;
        return queue.enqueueMessage(message, uptimeMillis);
    }

    /**
     * 分发消息给Handler进行处理
     */
    void dispatchMessage(Message message) {
        //如果是post()或postDelayed()发出的任务,则执行这个任务
        if (message.callback != null) {
            handleCallback(message);
        } else {
            //如果在Handler构造时传入了Callback,则回调这个Callback
            if (mCallback != null) {
                if (mCallback.handleMessage(message)) {
                    return;
                }
            }
            //都没有,则调用handleMessage(),Handler的子类可以复写该方法进行事件处理
            handleMessage(message);
        }
    }

    /**
     * 执行Message绑定的任务
     */
    private static void handleCallback(Message message) {
        message.callback.run();
    }

    /**
     * 子类重写该方法进行处理消息
     */
    public void handleMessage(Message message) {
    }
}

MiniHandlerThread

原自Android源码中的HandlerThread,改用MiniHandler实现,就是一个带有Handler事件循环的子线程

/**
 * A {@link Thread} that has a {@link Looper}.
 * The {@link Looper} can then be used to create {@link MiniHandler}s.
 * <p>
 * Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
 */
public class MiniHandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private MiniHandler mHandler;

    public MiniHandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
     * Constructs a HandlerThread.
     *
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from
     *                 {@link android.os.Process} and not from java.lang.Thread.
     */
    public MiniHandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason isAlive() returns false, this method will return null. If this thread
     * has been started, this method will block until the looper has been initialized.
     *
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * @return a shared {@link MiniHandler} associated with this thread
     * @hide
     */
    public MiniHandler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new MiniHandler(getLooper());
        }
        return mHandler;
    }

    /**
     * Quits the handler thread's looper safely.
     * <p>
     * Causes the handler thread's looper to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * Pending delayed messages with due times in the future will not be delivered.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link MiniHandler#sendMessage(Message)} method will return false.
     * </p><p>
     * If the thread has not been started or has finished (that is if
     * {@link #getLooper} returns null), then false is returned.
     * Otherwise the looper is asked to quit and true is returned.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

有关手写Android-Handler的更多相关文章

  1. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  2. Handler在UI线程与子线程中的使用 - 2

    Handler在UI线程与子线程中的使用一、Handler基本概念Handler:其作用是在新启动的线程中发送消息,在主线程中获取和处理消息。Message:Handler接收和处理的消息对象。MessageQueue:消息队列,采用先进先出的方式来管理Message。由Looper对象进行管理,在程序创建Looper对象时,会在它的构造方法中创建MessageQueue对象,两者是相互依存的。Looper:每个线程只能拥有一个Looper对象。它在loop()方法会不断在MessageQueue中读取Message消息,分给对应的Handler来进行处理。二、Handler使用步骤在主线程中

  3. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  4. Android 10.0 设置默认launcher后安装另外launcher后默认Launcher失效的功能修复 - 2

    1.前言 在10.0的系统rom定制化开发中,在系统中有多个launcher的时候,会在开机进入launcher的时候弹窗launcher列表,让用户选择进入哪个launcher,这样显得特别的不方便所以产品开发中,要求用RoleManager的相关api来设置默认Launcher,但是在设置完默认Launcher以后,在安装一款Launcher的时候,默认Launcher就会失效,在系统设置的默认应用中Launcher选项就为空,点击home键的时候会弹出默认Launcher列表,让选择进入哪个默认Launcher.所以需要从安装Launcher的流程来分析相关的设置。来解决问题设置默认La

  5. AiBote 2022 新研发的自动化框架,支持 Android 和 Windows 系统。速度非常快 - 2

    Ai-Bot基于流行的Node.js和JavaScript语言的一款新自动化框架,支持Windows和Android自动化。1、Windowsxpath元素定位算法支持支持Windows应用、.NET、WPF、Qt、Java和Electron客户端程序和ie、edgechrome浏览器2、Android支持原生APP和H5界面,元素定位速度是appium十倍,无线远程自动化操作多台安卓设备3、基于opencv图色算法,支持找图和多点找色,1080*2340全分辨率找图50MS以内4、内置免费OCR人工智能技术,无限制获取图片文字和找字功能。5、框架协议开源,除官方node.jsSDK外,用户可

  6. Android Gradle 7.1+新版本依赖变化 - 2

    前一段时间由于工作需要把可爱的小雪狐舍弃了,找到了小蜜蜂。但是新版本的小蜜蜂出现了很多和旧版本不一样的位置。1.功能位置迁移,原来在工程build.gradle的buildscript和allprojects移动至setting.gradle并改名为pluginManagement和dependencyResolutionManagement。里面的东西依旧可以按照原来的copy过来。pluginManagement{repositories{gradlePluginPortal()google()mavenCentral()}}dependencyResolutionManagement{r

  7. ruby - Ruboto 的最佳教程(适用于 Android 的 ruby​​)? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion我几乎用完了Ruby,但现在想试试Ruboto,android上的ruby​​。谷歌未能给我足够的(几乎没有结果)。所以任何人都可以分享一些关于Ruboto的教程。

  8. Android Studio 解决Could not resolve com.android.tools.build:gradle:7.4.2问题 - 2

    Aproblemoccurredconfiguringrootproject'MyApplication2'.>Couldnotresolveallfilesforconfiguration':classpath'.  >Couldnotresolvecom.android.tools.build:gradle:7.4.2.   Requiredby:     project:>com.android.application:com.android.application.gradle.plugin:7.4.2     project:>com.android.library:com.andr

  9. Android对话框的详细介绍(提示对话框,自定义对话框) - 2

    简介:我们都知道在Android开发中,当我们的程序在与用户交互时,用户会得到一定的反馈,其中以对话框的形式的反馈还是比较常见的,接下来我们来介绍几种常见的对话框的基本使用。前置准备:(文章最后附有所有代码)我们首先先写一个简单的页面用于测试这几种Dialog(对话框)代码如下,比较简单,就不做解释了一、提示对话框(即最普通的对话框)首先我们给普通对话框的按钮设置一个点击事件,然后通过AlertDialog.Builder来构造一个对象,为什么不直接Dialog一个对象,是因为Dialog是一个基类,我们尽量要使用它的子类来进行实例化对象,在实例化对象的时候,需要将当前的上下文传过去,因为我这

  10. android 多屏幕显示activity,副屏,无线投屏 - 2

    目录1.首先,需要一个副屏1.1可以通过代码的形式自己创建VirtualDispaly,创建副屏。1.2或者,在手机的开发者模式中直接开启模拟副屏,也是可以的。2.0怎么利用这个副屏幕?2.1 用作presentation演示ppt:2.2克隆主屏幕的内容,就是主屏幕显示什么,副屏显示同样的内容,镜像模式。2.3 将一个activity从第二个屏幕上启动,作为一个独立的屏幕首先说明一下这个多屏幕的概念,这里不是指分屏显示。分屏显示:是一个屏幕分出多个窗口,分别显示不同app.多屏支持:是一个设备有多个屏幕,怎么让不同的屏幕显示不同的app,或者是一个app同时用两个屏幕来显示不同的页面内容。多

随机推荐