Android源码——Handler机制(一)

news2024/12/23 18:38:22

Android源码——Handler机制(一)

    • Handler机制概述
      • 介绍
      • Handler机制模型
      • Handler机制架构
    • Handler机制源码解析
      • ActivityThread
      • Looper
      • Handler

Handler机制概述

介绍

Handler是Android消息机制的上层接口。Handler可以将一个任务切换到Handler所在的线程中去执行。通常情况下,Handler的使用场景就是更新UI。

如下就是使用消息机制的一个简单实例:

public class BasicActivity extends AppCompatActivity {

    private static final String TAG = "BasicActivity";

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.i(TAG, msg.what);
        }
    };

    @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() {
                
                // 耗时操作.....
                
                Message message = Message.obtain();
                message.what = 1;
                mHandler.sendMessage(message);
            }
        }).start();
    }
}

在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。这便是消息机制的典型应用场景。我们通常只会接触到Handler和Message来完成消息机制,其实内部还有两大助手来共同完成消息传递。

Handler机制模型

消息机制主要包含:MessageQueueHandlerLooper这三大部分,以及Message

  • Message: 需要传递的消息,可以传递数据;
  • MessageQueue: 消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息MessageQueue.enqueueMessage和取走消息池的消息MessageQueue.next
  • Handler: 消息辅助类,主要功能向消息池发送各种消息事件Handler.sendMessage和处理相应消息事件Handler.handleMessage
  • Looper: 不断循环执行Looper.loop,从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

Handler机制架构

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

每个线程只能存在一个LooperLooper保存在ThreadLocal线程中。主线程(UI线程)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper。每个线程可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler

在这里插入图片描述
Looper有一个MessageQueue消息队列,MessageQueue有一组待处理的MessageMessage中记录发送和处理消息的HandlerHandler中有LooperMessageQueue

Handler机制源码解析

ActivityThread

ActivityThread类是用来启动Android的,在main()函数中准备相关环境,创建并启动Looper循环。

主线程中不需要自己创建Looper,这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

    // Install selective syscall interception
    AndroidOs.install();

    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    CloseGuard.setEnabled(false);

    Environment.initForCurrentUser();

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    // Call per-process mainline module initialization.
    initializeMainlineModules();

    Process.setArgV0("<pre-initialized>");

    // 1. 把主线程和消息队列放进Looper中,同时把Looper放进ThreadLocal(每个线程绑定一个Looper)
    Looper.prepareMainLooper();

    // ....

    // 启动Looper循环,不断循环取出消息
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper

Looper不断循环执行loop(),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

  1. 初始化Looper
public final class Looper {

    // sThreadLocal.get() will return null unless you've called prepare().
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    @UnsupportedAppUsage
    private static Looper sMainLooper;  // guarded by Looper.class

    @UnsupportedAppUsage
    final MessageQueue mQueue;
    final Thread mThread;

    /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    /**
     * 创建Looper,并保存在ThreadLocal,不能重复创建Looper,只能创建一个。 
     */
    private static void prepare(boolean quitAllowed) {
        // 不能重复创建否则报错
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 4. 实例化自己,封装mQueue和主线程,把自己放入ThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. See also: {@link #prepare()}
     *
     * @deprecated The main looper for your application is created by the Android environment,
     *   so you should never need to call this function yourself.
     */
    @Deprecated
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    private Looper(boolean quitAllowed) {
        // 2. 实例化MessageQueue
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

}
  1. 开启Looper
    /**
     * 5. 调用Looper.loop()方法
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    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.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;
        // 获取Looper对象中的消息队列
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        
        // 进入loop()主循环
        for (;;) {
            // 消息队列取出一条消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // 默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能
            // 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);
            }

            // .....

            try {
                // 获取msg的目标Handler,然后用于分发Message
                msg.target.dispatchMessage(msg);
                
                // ....
            } catch (Exception exception) {
                // ...
            } finally {
                // ...
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();

            // 回收可能正在使用的消息
            msg.recycleUnchecked();
        }
    }

loop()进入循环模式,不断重复下面的操作,直到消息为空时退出循环:读取MessageQueue的下一条Message;把Message分发给相应的target

next()取出下一条消息时,队列中已经没有消息时,next()会无限循环,产生阻塞。等待MessageQueue中加入消息,然后重新唤醒。

Handler

消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage)。

  1. 创建Handler
public class Handler {

    @UnsupportedAppUsage
    final Looper mLooper;
    // 这里并没有实例化,指向Lopper的消息队列
    final MessageQueue mQueue;
    @UnsupportedAppUsage
    final Callback mCallback;
    final boolean mAsynchronous;
    @UnsupportedAppUsage
    IMessenger mMessenger;

    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }
    
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(@NonNull Message msg) {
    }

}

构造函数:

    @Deprecated
    public Handler() {
        this(null, false);
    }

    @Deprecated
    public Handler(@Nullable Callback callback) {
        this(callback, false);
    }

    @UnsupportedAppUsage
    public Handler(boolean async) {
        this(null, async);
    }

    /**
     * 第一种构造方法:给主线程调用,因为主线程已经调用Looper.prepareMainLooper()
     */
    public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        // 获取主线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }

        // Handler的mQueue指向Looper的mQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }

    public Handler(@NonNull Looper looper, @Nullable Callback callback) {
        this(looper, callback, false);
    }

    /**
     * 第二种构造方法,给在子线程中创建Handler时使用,子线程要自主调用Looper.prepare()
     */
    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

  1. 发送消息

发送消息有几种方式,但是归根结底都是调用了sendMessageAtTime()方法。在子线程中通过Handlerpost()方式或send()方式发送消息,最终都是调用了sendMessageAtTime()方法。

post方法:

    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

    public final boolean postAtTime(
            @NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

    public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

    public final boolean postDelayed(Runnable r, int what, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
    }

    public final boolean postDelayed(
            @NonNull Runnable r, @Nullable Object token, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r, token), delayMillis);
    }

send方法:

    public final boolean sendMessage(@NonNull 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(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

就连子线程中调用Activity中的runOnUiThread()更新UI,其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。

/**
 * 如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,最终会调用sendMessageAtTime()方法。
 * 否则就直接调用Runnable对象的run()方法。
 */
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

sendMessageAtTime()

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        // 其中mQueue是消息队列,从Looper中获取的
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        // 调用enqueueMessage方法
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        // 把自己(Handler)传入消息一并发送,因为Looper需要用Handler来执行dispatchMessage()
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        // 调用MessageQueue的enqueueMessage方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看到sendMessageAtTime()方法的作用很简单,就是调用MessageQueueenqueueMessage()方法,往消息队列中添加一个消息。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1872957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高考填报志愿,是选就业前景?还是选自己的兴趣爱好?

一、 当前的就业形式 受yi情影响&#xff0c;全国的就业处于下滑趋势&#xff0c;互联网和实体企业呈现疲软势态&#xff0c;很多企业不得不裁员。大学毕业生人数几乎每年都会上涨&#xff0c;带来的是僧多粥少的就业状态。 考得好不如报得好 就业环境如此严峻的形势下&#…

领夹麦克风哪个品牌好?揭秘无线领夹麦克风哪个品牌性价比高?

随着短视频、直播等行业的兴起&#xff0c;越来越多小伙伴开始加入自媒体创作之中&#xff0c;虽说目前手机的拍摄效果已经能满足个人创作视频的需求了&#xff0c;但是声音效果依旧是目前拍摄的瓶颈之一&#xff0c;因为手机的麦克风受限于体积&#xff0c;物理位置以及技术等…

Spring Session将HttpSession保存到Redis中,实现重启应用会话不丢失

这篇文章介绍一下在springboot项目中整合Spring Session&#xff0c;将session会话信息保存到Redis中&#xff0c;防止重启应用导致会话丢失。 第一步 创建一个springboot项目&#xff0c;添加spring-session-redis的依赖&#xff0c;因为要用到reids&#xff0c;所以要把redi…

推荐金顺心贸易一款爆款产品——越南进口高蛋白莲城鱼露

今日推荐金顺心贸易有限公司买的很火爆的一款越南高蛋白莲城鱼露&#xff0c;蛋白质含量高达30℃,天然发酵,非调制鱼露可比的。越南原装进口&#xff0c;品质纯正。

vxe-vxeTable使用vxe-colgroup分组合并表头,基础上合并

1.例 vxe-colgroup分组完成&#xff0c;需要实现两个合并合并成一行 基础合并完成 2.实现思路 由于表头字段固定&#xff0c;在进行vxe-colgroup分组合并&#xff0c;实现基础的表头合并&#xff1b;组件库官网对于表头合并并没有方法&#xff1b; 1.官网API知可以给对应的表…

炫云云渲染c4d使用教程

c4d是一款功能强大的三维建模、动画和渲染软件&#xff0c;在电影、电视和广告行业中被广泛应用于视觉效果制作&#xff0c;是一款非常受欢迎的三维设计软件&#xff0c;而后期的渲染往往又非常费时间。本文为大家介绍炫云云渲染的使用方法&#xff0c;能极大提升你的渲染效率和…

为什么ISO 45001职业健康安全管理体系是企业发展的基石

ISO 45001源自OHSAS 18001职业健康和安全管理体系&#xff0c;是全球第一个国际职业健康和安全管理标准。ISO&#xff08;国际标准化组织&#xff09;于2018年发布了这一标准&#xff0c;旨在帮助各类组织为员工提供一个更安全、更健康的工作环境。与OHSAS 18001相比&#xff0…

亚马逊测评干货分享:跨境卖家店铺测评技巧

测评在亚马逊、etsy、temu、速卖通、vinted、ebay、allegro、Jumia、Fruugo、敦煌、shopee、ozon、阿里国际站、沃尔玛、newegg等跨境平台中扮起着重要的方式&#xff0c;卖家们了解到测评可以快速增加产品的销量、评论数量&#xff0c;提升排名&#xff0c;从而打造爆款产品。…

python办公自动化之excel

用到的库&#xff1a;openpyxl 实现效果&#xff1a;读取单元格的值&#xff0c;写入单元格 代码&#xff1a; import openpyxl # 打开现有工作簿 workbookopenpyxl.load_workbook(现有工作簿.xlsx) # 选择一个工作表 sheetworkbook[交易表] # 读取单元格的值 cell_valueshe…

用数组模拟栈实现递归函数模拟

做算法课设时候看到题目要求模拟函数递归时候栈的入栈出栈过程。本来想着直接调用系统递归函数即可&#xff0c;可是发现系统函数栈的空间非常小大约只有3000层&#xff0c;很容易爆栈。于是便有了用栈去模拟递归函数的想法&#xff0c;但是上网查了下貌似相关代码比较少&#…

【网络安全学习】漏洞利用:BurpSuite的使用-02-常用的标签

下面介绍一下BurpSuite各个标签的用法&#x1f447; 1️⃣ Dashboard标签 Dashboard&#xff0c;顾名思义就是BurpSuite的仪表盘&#xff0c;可以通过Dashboard进行漏洞扫描&#xff0c;不过该功能需要升级到pro版本&#xff0c;也就是得交钱&#x1f62d;。 2️⃣ Target标签…

STM32要学到什么程度才算合格?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; STM32 这玩意儿要学到啥…

CDN入门

在腾讯云上使用CDN 1、参考 内容分发网络 CDN 从零开始配置 CDN-快速入门-文档中心-腾讯云 2、验证 访问&#xff1a; 登录 - 腾讯云 Window10本地电脑使用命令验证 nslookup -qt-cname hmblogs.com.cn Ubuntu下验证 dig hmblogs.com.cn

【等保小知识】政府机构哪些系统需要过等保?怎么过?

随着等保政策的严格落地执行&#xff0c;政府机构率先做榜样&#xff0c;积极办理等保。那你知道政府机构到底哪些系统需要过等保吗&#xff1f;这里我们小编就来给大家简单回答一下&#xff0c;仅供参考哈&#xff01; 政府机构哪些系统需要过等保&#xff1f; 【回答】&…

docker部署EKF

1.检查版本 检查当前系统的docker版本 [rootnode1 ~]# docker version Client: Docker Engine - CommunityVersion: 20.10.12API version: 1.41Go version: go1.16.12Git commit: e91ed57Built: Mon Dec 13 11:45:41 2021OS/Arch: …

基于SpringBoot养老院管理系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

SpringBoot集成道历(实现道历日期查询)

官网地址&#xff1a;官网地址https://6tail.cn/calendar/api.html 1、导入依赖 <dependency><groupId>cn.6tail</groupId><artifactId>lunar</artifactId><version>1.3.9</version></dependency><dependency><group…

未来先行!MWC 2024 世界移动通信大会盛大开幕!!!

2024MWC上海世界移动通信大会&#xff0c;在上海新国际博览中心&#xff08;SNIEC&#xff09;盛大开幕。 今年&#xff0c;MWC的主办方GSMA&#xff08;全球移动通信系统协会&#xff09;为这届MWC定下了一个主题——“Future First&#xff08;未来先行&#xff09;”。各大…

定制聚四氟乙烯架子离心管架子消解管样品架

南京瑞尼克定制离心管架子、消解管架子、微波罐架子等&#xff0c;各种实验室所需器皿样品架。 我司聚四氟乙烯架子优势&#xff1a; 1.耐强腐蚀&#xff08;耐各种强酸强碱和有机溶剂&#xff09; 2.耐高低温&#xff1a;-200至250℃ 3.工艺精制&#xff0c;尺寸&#xff0…

【代码随想录】【算法训练营】【第52天】 [647]回文子串 [516]最长回文子序列

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 52&#xff0c;周五&#xff0c;开始补作业了~ 题目详情 [647] 回文子串 题目描述 647 回文子串 解题思路 前提&#xff1a; 思路&#xff1a; 重点&#xff1a; 代码实现 C语言 [516] 最…