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

智能推荐

[AMBA]AXI原子操作(AXI Atomic Operation)-程序员宅基地

文章浏览阅读4.3k次,点赞2次,收藏25次。AXI原子操作(AXI Atomic Operation)。​原子操作就是不可中断的一个或者一系列操作, 也就是不会被线程调度机制打断的操作, 运行期间不会有任何的上下文切换(context switch)。我们为什么要关注原子操作呢?如果确定某个操作是原子的, 就不用为了去保护这个操作而加上会耗费昂贵性能开销的锁,借助原子操作可以实现互斥锁(mutex),借助互斥锁, 可以实现让更多的操作变成原子操作。 前言:一、原子操作的基本概念..._axi原子操作

Ps 滤镜:球面化-程序员宅基地

文章浏览阅读189次。Ps菜单:滤镜/扭曲/球面化Filter/Distort/Spherize球面化 Spherize滤镜可用于创造图像的球面或凹面效果。常用来制作图像的视觉弯曲,模拟三维效果,或者创造特殊的艺术效果。“球面化”滤镜通过数学算法调整图像的像素,使图像中心区域凸起或凹陷,从而在视觉上产生图像向外凸出或向内凹陷的效果。根据选择的“模式”,还可控制只沿水平方向或沿垂直方向进行拉伸或挤压。◆◆◆主要用途1...

MFC一一MFCPropertyGridCtrl控件使用_cmfcpropertygridctrl xml-程序员宅基地

文章浏览阅读6.3k次。MFC属性网格控件,控件样式如下所示: 通常其与Tree Control结合使用,如上图中的左边部分所述,点击左边树结点,对应右边插入相应的MFCPropertyGridCtrl控件,如下我们将介绍MFCPropertyGridCtrl控件public: CMFCPropertyGridCtrl m_wndPropList;一、在窗口创建前,创建属性表格并添加相应的属性设置int CDi..._cmfcpropertygridctrl xml

Layui在select下拉框中动态添加选项_layui select 动态加载-程序员宅基地

文章浏览阅读3.6k次。话不多说,直接上代码(注意项下方说明)HTML:<form class="layui-form" action=""> <div class="layui-input-inline> <select name="modules" lay-verify="required" lay-search="" lay-filter="test" id="test"> // 选项动态放置 </select>_layui select 动态加载

JS-正则表达式-程序员宅基地

文章浏览阅读98次。正则表达式是由一个字符序列形成的搜索模式。当你在文本中搜索数据时,你可以用搜索模式来描述你要查询的内容。正则表达式可以是一个简单的字符,或一个更复杂的模式。正则表达式可用于所有文本搜索和文本替换的操作。

Git速查手册_如何查看git手册-程序员宅基地

文章浏览阅读1.3k次。1024了,程序猿们节日快乐,简单整理个供小白使用的git速查手册,之后有时间再完善,其实一会一些简单的操作平时工作就够用了(只要你别闹!一般不会有什么大问题),哈哈哈文章目录Git的组成git--相关配置git--查看远程库信息git--初始化仓库git--将文件提交到暂存区git--将暂存区文件提交到本地仓库git--查看仓库当前状态git--比较文件异同git--查看历史记录git--代码..._如何查看git手册

随便推点

如何查看SAP表变更记录_sap ps 项目更改记录查询-程序员宅基地

文章浏览阅读2.2k次。在运维的过程中,我们有时需要去追踪一些表的变更记录,如采购订单的单价更新,采购订单的入库标识何时更新等,以便我们可以更快的追踪定位到问题所在,本章给大家介绍下如何追踪系统中表的变更记录。根据表名获取更改文档对象,表:TCDOB。_sap ps 项目更改记录查询

超链接打不开是什么原因html,超链接打不开是什么原因-程序员宅基地

文章浏览阅读2.4w次。演示工具:电脑型号:华硕adolbook14 2020系统版本:windows10具体原因及解决方法:1、如果是链接到本地文件的超链接无法打开,可能是相对路径和绝对路径的问题,绝对地址,是有完全的路径,如果超链接的路径写错了,就无法打开。2、检查一下被链接的文件是否被改变了存储位置,如果是,需重新设置一次。3、如果链接的是局域网上其它电脑中的文档,可能是被链接的电脑网络设置出了问题,先检查能否链接...

搭建网站服务基于openlab_那些可以在openlab网站获得支持-程序员宅基地

文章浏览阅读156次。1.基于域名www.openlab.com可以访问网站内容 welcome to openlab。#mount /dev/sr0 /mnt#yum install httpd -y2.给该公司创建三个子界面分别显示学生信息,教学资料和缴费网站,基于www.openlab.com/student网站访问学生信息,www.openlab.com/data网站访问教学资料,www.openlab.com/money网站访问缴费网站。(1)学生信息网站只能song和tian两个人访问,其他用户不_那些可以在openlab网站获得支持

idea常用插件_idea打印sql语句插件-程序员宅基地

文章浏览阅读1.9k次。当我们在写代码的时候,其实很多基础的代码都可以直接生成的,个人比较喜欢用easycode,该插件可以自己自定义生成基础的增删改查,有提供的模板,但个人建议最好还是自己写个模板,能符合自己的项目。定制性更强,而且该代码能直接生成到项目中选择连接的数据库,右击使用。_idea打印sql语句插件

win10 锁屏时间无法设置 解决方法_注册表修改锁屏时间win10-程序员宅基地

文章浏览阅读5.6k次。win10 锁屏时间无法设置 解决方法问题描述:解决方案:标题问题描述:win10 无法修改屏幕保护时间解决方案:标题将下段代码复制进txt,保存成reg,然后运行。dword:0000003C,为设置的时间16进制秒数Windows Registry Editor Version 5.00[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]“InactivityTimeoutSecs”=d_注册表修改锁屏时间win10

无人机基础知识:多旋翼无人机自动控制原理与算法_无人机协调行为算法怎么学习-程序员宅基地

文章浏览阅读5.4k次,点赞7次,收藏82次。无人机(Unmanned Aerial Vehicle),指的是一种由动力驱动的、无线遥控或自主飞行、机上无人驾驶并可重复使用的飞行器,飞机通过机载的计算机系统自动对飞行的平衡进行有效的控制,并通过预先设定或飞机自动生成的复杂航线进行飞行,并在飞行过程中自动执行相关任务和异常处理。在前面的文章中,我们分析了多旋翼无人机的飞行原理及飞行模式。链接在最后的飞行模式中, 除了纯手动模式外,简单解释了其它模式如姿态模式的原理,其中一笔带过就是形成了闭环控制,所以稳定。_无人机协调行为算法怎么学习

推荐文章

热门文章

相关标签