Android Handler 的基本使用_android handler weakreference-程序员宅基地

技术标签: Handler  QtAndroid  Android  

1.前言

https://developer.android.google.cn/reference/android/os/Handler.html

Handler 是 Android 中线程通信的常用方式,文档如是说:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.

Handler 允许你发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象。 每个 Handler 实例都与一个线程和该线程的消息队列 (MessageQueue) 相关联。 当你创建一个新的 Handler 时,它会绑定到 Looper。 它将向 Looper 的消息队列传递消息 (Message) 和可运行对象 (Runnable),并在 Looper 的线程上执行它们。

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Handler 有两个主要用途: (1) 安排消息和可运行对象在未来某个时刻执行; (2) 将要在与你自己的线程不同的线程上执行的操作排队。

Scheduling messages is accomplished with the post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

消息的调度是通过 post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) 等方法来完成的。post 系列的方法允许你将 Runnable 对象放入队列,当消息队列处理该消息时 Runnable 被调用;sendMessage 系列的方法允许你将可包含数据的 Message 对象放入队列,消息会被传递到 Handler 的 handleMessage(Message) 接口进行处理,你需要实现该接口。

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

当 post 或 sendMessage 到 Handler 时,可以在消息队列准备好后立即处理该消息,也可以指定延时时间 (Delayed) 或者指定时间点 (AtTime) 处理该消息。 后两者可以让你实现超时、定时和其他基于时间的行为。

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.

当为应用程序创建进程时,其主线程专用于运行一个消息队列,该消息队列负责管理顶层应用程序对象(Activity、BroadcastReceiver 等)及其创建的任何窗口。你可以创建自己的线程,并通过 Handler 与主应用程序线程进行通信。这只需要在新线程中调用 post 或 sendMessage 方法。 然后,给定的 Runnable 或 Message 将在 Handler 的消息队列中进行调度,并在适当的时候进行处理。

2.基本使用

2.1.基本流程

post 和 sendMessage 的基本使用,子线程向主线程发消息:

    // 创建 Handler 时关联一个 Looper 消息循环
    Handler handler = new Handler(Looper.getMainLooper()) {
        // 在 Looper 对应的线程处理该消息
        @Override
        public void handleMessage(Message msg) {
            // 根据 Message what 区分不同的消息
            switch (msg.what) {
                case 0: {
                    // 从 Message 中获取传递的参数数据进行处理
                    // do some ... ...
                }
                break;
            }
        }
    };

    void doing() {
        // 在线程中执行任务
        new Thread(new Runnable() {
            @Override
            public void run() {
                // do some ... ...
                // 任务完成 send 消息给 Handler 处理
                handler.sendEmptyMessage(0);
                // 或者 post 一个回调到 Handler 线程执行
                handler.post(new Runnable() {
                    // Run 会在 Handler Looper 线程执行
                    @Override
                    public void run() {
                        // do some ... ...
                    }
                });
            }
        }).start();
    }

2.2.Message 对象

创建 Message 的几种方式:

Message m1 = handler.obtainMessage(); //通过 Handler 实例获取
Message m2 = Message.obtain(); //通过 Message 获取
Message m3 = new Message(); //创建新的 Message 实例

其中,Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法:

    public final Message obtainMessage() {
        return Message.obtain(this); // this Handler 作为 target 参数
    }

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

obtain()从消息池拿一个 Message 对象,不足时 new 一个新的 Message 返回:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

Message 除了 what 属性可以用来区分消息类型,还可以携带参数:

Message msg = new Message();
// what:int 消息类型
msg.what = 1;
// arg1:int 和 arg2:int 两个 int 作为便捷参数,替代 obj
msg.arg1 = 0;
msg.arg2 = 0;
// obj:Object 存储对象
msg.obj = "Message";
// 存储复杂数据
// setData 设置内部 data,读取用 getData
Bundle bd = new Bundle();
bd.putString("name", "Bundle");
bd.putInt("code", 123);
msg.setData(bd);

handler.sendMessage(msg);

2.3.向子线程发消息

Handler 需要关联一个 Looper 对象,主线程直接用 Looper.getMainLooper() 获取,子线程需要用 Looper.prepare() 创建 Looper,再用 Looper.loop() 开启消息循环。

    Handler handler;

    void test() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e(LogTag, "Looper in");
                // 准备 Looper,不存在则创建
                Looper.prepare();
                // 获取当前线程 Looper
                handler = new Handler(Looper.myLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        // do some ... ...
                        Log.e(LogTag, "handleMessage " + msg.what);
                        // 退出消息循环
                        Looper.myLooper().quit();
                    }
                };
                // 开启消息循环
                Looper.loop();
                Log.e(LogTag, "Looper exit");
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000); // 延时,等 handler 初始化完
                    // 发送消息
                    Log.e(LogTag, "sendEmptyMessage");
                    handler.sendEmptyMessage(123);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

也可以用封装了 Looper 操作的 HandlerThread:

    Handler handler;
    HandlerThread thread;

    void test() {
        thread = new HandlerThread("test thread");
        thread.start();
        handler = new Handler(thread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                // do some ... ...
                Log.e(LogTag, "handleMessage " + msg.what);
                // 退出消息循环
                thread.quit();
                // 或者 thread.quitSafely();
            }
        };
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000); // 延时,等 handler 初始化完
                    // 发送消息
                    Log.e(LogTag, "sendEmptyMessage");
                    handler.sendEmptyMessage(123);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

3.内存泄漏 

界面 Activity finish 的时候,Handler 可能还被 MessageQueue 消息队列引用着。而前文 Handler 和 post 都用到了匿名内部类的写法:new Type(){},非静态的内部类和匿名内部类都会隐式持有外部类 Activity 的引用。Activity finish 不能立即回收,还要等 Handler 处理结束才能被回收,就造成了内存泄漏。

可以将 Handler 定义成静态内部类,静态内部类是不持有外部类的实例的。同时,为了能调用外部的实例方法,需要持有一个外部的弱引用。

public class MainActivity extends AppCompatActivity {

    private static class MyHandler extends Handler {

        // 弱引用持有 Activity, GC 回收时会被回收掉
        private WeakReference<MainActivity> weakReference;

        public MyHandler(MainActivity activity) {
            super(Looper.getMainLooper());
            this.weakReference = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = weakReference.get();
            super.handleMessage(msg);
            if (null == activity) {
                return;
            }
            // do some ... ...
        }
    }

    private MyHandler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // do some ... ...

        handler = new MyHandler(this);
        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.sendEmptyMessage(123);
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        // 移除所有回调及消息
        handler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
}

4.参考

安卓文档:Handler  |  Android Developers (google.cn)

参考博客:Android——Handler详解_android handler_Yawn__的博客-程序员宅基地

参考博客:Android - Handler_android handler looper_Jomurphys的博客-程序员宅基地

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/gongjianbo1992/article/details/132100484

智能推荐

蓝凌EIS智慧协同平台saveImg接口存在任意文件上传漏洞_蓝凌eis智慧协同平台文件上传漏洞-程序员宅基地

文章浏览阅读979次。蓝凌智慧协同平台eis集合了非常丰富的模块,满足组织企业在知识、协同、项目管理系统建设等需求。_蓝凌eis智慧协同平台文件上传漏洞

LLaVA-1.5-程序员宅基地

文章浏览阅读193次。与InstructBLIP或Qwen-VL在数亿甚至数十几亿的图像文本配对数据上训练的、专门设计的视觉重新采样器相比,LLaVA用的是最简单的LMM架构设计,只需要在600K个图像-文本对上,训练一个简单的完全连接映射层即可。结果表明,LLaVA-1.5不仅可以使用更少的预训练和指令微调数据,而且还可以利用最简单的架构、学术计算和公共数据集来实现最佳的性能——在12个基准中的11个上取得了SOTA。为了解决这个问题,研究人员建议在VQA问题的末尾,添加一个可以明确输出格式的提示,进而让模型生成简短回答。

ORACLE基本数据类型总结_oracle 数值类型最大值-程序员宅基地

文章浏览阅读442次。2013-08-17 21:04 by 潇湘隐者, 100246 阅读, 5 评论, 收藏, 编辑 ORACLE基本数据类型(亦叫内置数据类型 built-in datatypes)可以按类型分为:字符串类型、数字类型、日期类型、LOB类型、LONG RAW& RAW类型、ROWID & UROWID类型。在讲叙字符串类型前,先要讲一下编码。字符串类型的数据可依编码方式分成_oracle 数值类型最大值

10种机器学习算法_决策树和mlp-程序员宅基地

文章浏览阅读315次。作为数据科学家的实践者,我们必须了解一些通用机器学习的基础知识算法,这将帮助我们解决所遇到的新领域问题。本文对通用机器学习算法进行了简要的阐述,并列举了它们的相关资源,从而帮助你能够快速掌握其中的奥妙。▌1.主成分分析(PCA)/ SVDPCA是一种无监督的方法,用于对由向量组成的数据集的全局属性进行理解。本文分析了数据点的协方差矩阵,以了解哪些维度(大部分情况)/数据点(少数情况)更为重要,即它..._决策树和mlp

桥接模式的实现-程序员宅基地

文章浏览阅读148次。在这个示例中,我们使用std::shared_ptr来管理Implementor对象的生命周期,确保在不再需要时自动释放资源。通过智能指针的使用,我们避免了手动管理内存的复杂性,提高了代码的可靠性和可维护性。希望这个示例能帮助你理解如何使用智能指针来实现桥接模式。当使用智能指针来实现桥接模式时,我们可以利用std::shared_ptr或std::unique_ptr来管理对象的生命周期,确保资源的正确释放。

制造业敏感文件外发不安全?一招解锁更高效的加密方式!-程序员宅基地

文章浏览阅读440次,点赞11次,收藏8次。云盒子在制造业上有丰富的部署经验,在面向制造类企业的重要文件,可以通过审计、授权、文件加密进行多重保护,使得图纸文件、专利技术、采购订单等敏感数据等到有效保护,做到无处可泄,同时安全可靠,也不会对日常工作效率有影响 ,实现真正有效的企业文件保护的目的,达到既防止机密文件外泄和扩散,又支持内部知识积累和文件共享的目的。云盒子的加密方式是通过将本地文件数据上传到云盘进行统一加密存储,而不是对设备加密,通过【本地加密】+【云加密】双重组合下,不管用什么设备打开文件都受到管控,使管理者管理起来能够更高效。

随便推点

计算几何讲义——计算几何中的欧拉定理-程序员宅基地

文章浏览阅读188次。在处理计算几何的问题中,有时候我们会将其看成图论中的graph图,结合我们在图论中学习过的欧拉定理,我们可以通过图形的节点数(v)和边数(e)得到不是那么好求的面数f。 平面图中的欧拉定理: 定理:设G为任意的连通的平面图,则v-e+f=2,v是G的顶点数,e是G的边数,f是G的面数。证明:其实有点类似几何学中的欧拉公式的证明方法,这里采用归纳证明的方法。对m..._怎么证明平面图欧拉定理

c语言中各种括号的作用,C语言中各种类型指针的特性与用法介绍-程序员宅基地

文章浏览阅读750次。C语言中各种类型指针的特性与用法介绍本文主要介绍了C语言中各种类型指针的特性与用法,有需要的朋友可以参考一下!想了解更多相关信息请持续关注我们应届毕业生考试网!指针为什么要区分类型:在同一种编译器环境下,一个指针变量所占用的内存空间是固定的。比如,在16位编译器环境 下,任何一个指针变量都只占用8个字节,并不会随所指向变量的类型而改变。虽然所有的指针都只占8个字节,但不同类型的变量却占不同的字节数..._c语言带括号指针

缅甸文字库 缅甸语字库 缅甸字库算法_0x103c-程序员宅基地

文章浏览阅读9.5k次。字库交流 QQ:2229691219 缅甸语比较特殊、缅甸语有官方和民间之分,二者不同的是编码机制不同,因此这2种缅甸语的字串翻译、处理引擎、字库都是不同的。我们这里只讨论官方语言。 缅文、泰文等婆罗米系文字大多是元音附标文字,一般辅音字母自带默认元音可以发音,真正拼写词句时元音像标点符号一样附标在辅音上下左右的相应位置。由于每个元音位于辅音的具体位置是有自己的规则的,当只书写..._0x103c

Python+django+vue校园二手闲置物品拍卖系统pycharm毕业设计项目推荐_基于python+django+vue实现的校园二手交易平台-程序员宅基地

文章浏览阅读200次。在校园,随着学生数量的增多,存在许多生活和学习物品,许多学习用品经过一学期学习之后往往被闲置,一些出于一时喜欢而购买的物品使用机会少而被闲置,还有一些物品以低廉的价格卖给资源回收站,造成巨大的资源浪费。校园闲置物品拍卖系统使用python技术,MySQL数据库进行开发,系统后台使用django框架进行开发,具有低耦合、高内聚的特点,其中校园用户通过人脸识别的方法增加系统安全性,在闲置物品推荐中,使用协同过滤算法进行商品推荐。系统的开发,帮助高校有效的对闲置物品进行管理,提高了闲置物品销售的效率。_基于python+django+vue实现的校园二手交易平台

【推荐系统论文精读系列】(十)--Wide&Deep Learning for Recommender Systems_引用《wide & deep learning for recommender systems》-程序员宅基地

文章浏览阅读1.1k次,点赞3次,收藏3次。文章目录Wide & Deep Learning for Recommender Systems一、摘要二、介绍三、推荐系统综述四、Wide&Deep学习4.1 Wide部分4.2 Deep部分4.3 联合训练 Wide&Deep ModelPreferenceWide & Deep Learning for Recommender Systems一、摘要具有非线性特征转化能力的广义线性模型被广泛用于大规模的分类和回归问题,对于那些输入数据是极度稀疏的情况下。通过使用交_引用《wide & deep learning for recommender systems》

c++ sleep函数_Linux 多线程应用中如何编写安全的信号处理函数-程序员宅基地

文章浏览阅读171次。关于代码的可重入性,设计开发人员一般只考虑到线程安全,异步信号处理函数的安全却往往被忽略。本文首先介绍如何编写安全的异步信号处理函数;然后举例说明在多线程应用中如何构建模型让异步信号在指定的线程中以同步的方式处理。Linux 多线程应用中编写安全的信号处理函数在开发多线程应用时,开发人员一般都会考虑线程安全,会使用 pthread_mutex 去保护全局变量。如果应用中使用了信号,而且信号的产生不..._linux c++ sleep 不被中断

推荐文章

热门文章

相关标签