资讯专栏INFORMATION COLUMN

Handler 使用详解

sihai / 1094人阅读

摘要:机制处理的个关键对象线程之间传递的消息,可以携带一些简单的数据供子线程与主线程进行交换数据。解决方法子线程通过发送消息给主线程,让主线程处理消息,进而更新。

极力推荐文章:欢迎收藏
Android 干货分享

阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android

本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:

Handler 消息处理机制原理

Handler 机制处理的4个关键对象

Handler常用方法

子线程更新UI 异常处理

主线程给子线程发送消息的方法

子线程给主线程发送消息的方法

主、子 线程 互发消息方法

子线程方法中调用主线程更新UI的方法

HandlerAndroid中用来更新UI 的一套消息处理机制。Handler 允许线程间发送MessageRunnable对象进行通信。在Android中UI修改只能通过UI Thread,子线程不能更新UI。如果子线程想更新UI,需要通过 Handler 发送消息给主线程,进而达到更新UI的目的。

Handler 简介 继承关系如下:
java.lang.Object
   ↳    android.os.Handler
1. Handler 消息处理机制原理

Android 应用程序创建的时候,系统会给每一个进程提供一个Looper Looper 是一个死循环,它内部维护一个消息队列,Looper 不停的从消息队列中取Message,取到的消息就发送给handler,最后Handler 根据接收的消息去修改UI等。

2. Handler 机制处理的4个关键对象 1.Message

线程之间传递的消息,可以携带一些简单的数据供子线程与主线程进行交换数据。

2.Message Queue

存放通过Handler 发送的 Message 的消息队列,每一个线程只有一个消息队列。

3.Handler

消息处理者,主要用于发送跟处理消息。

主要功能:
发送消息SendMessage()
处理消息 HandleMessage()

4.Looper

内部包含一个死循环的MessageQueue,用于存储handler 发送的MessageLooper则是不断的从消息队列中取消,如果有消息就取出发送给Handler 处理,没有则阻塞。

总结:

Handler 负责发送MessageMessage QueueLooper负责从Message Queue 遍历Message ,然后直接把遍历的消息回传给Handler 自己,通过Handler 自身的handleMessage处理更新UI等操作。

3. Handler常用方法 1.Runnable对象

post(Runnable)

使用方法举例:

    public void BtnRunnableMethod(View view) {
        // 1.Runnable 对象
        RunnableHandlderMethod();
    }

    /**
     * Runnable 对象更新 UI 
     * **/
    private Handler mRunnableHandler = new Handler();
    public void RunnableHandlderMethod() {
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);

                    mRunnableHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            ((Button) findViewById(R.id.btn_runnable))
                                    .setText("Runnable");
                        }
                    });

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }.start();
    }

postAtTime(Runnable, long)

postDelayed(Runnable, long)

2. Message 对象

sendEmptyMessage(int)

使用方法举例:

    public void BtnMessageThreadMethod(View view) {
        // 2.Message 对象
        new MessageHandlerThreadMethod("子线程不能更新UI").start();
    }
    /**
     * Message 对象举例
     * ***/
    private int mCount = 0;
    private Handler mMessageHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            ((Button) findViewById(R.id.btn_thread)).setText("" + mCount);
        }
    };

    class MessageHandlerThreadMethod extends Thread {

        String mString;

        public MessageHandlerThreadMethod(String str) {
            mString = str;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {

                }

                mCount++;
                mMessageHandler.sendEmptyMessage(0);
            }

        }
    }

sendMessage(Message)

使用方法举例:

public void BtnMessageObjMethod(View view) {
        HandlerMessageObjMethods();
    }

    /***
     * handler sendmessage 处理方法
     * **/
    private Handler mHandlerMessageObj = new Handler() {

        @Override
        public void handleMessage(Message msg) {

            ((Button) findViewById(R.id.btn_message)).setText("arg1:"
                    + msg.arg1 + "
" + msg.obj);
        }
    };

    private void HandlerMessageObjMethods() {
        new Thread() {
            @Override
            public void run() {

                try {
                    Thread.sleep(1000);
                    // Message message = new Message();
                    Message message = mHandlerMessageObj.obtainMessage();

                    message.arg1 = 100;

                    Person person = new Person();
                    person.name = "Lucy";
                    person.age = 12;

                    message.obj = person;
                    mHandlerMessageObj.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    class Person {

        public int age;
        public String name;

        public String toString() {
            return "Name=" + name + "
 Age=" + age;
        }

    }

sendMessageAtTime(Message, long),

sendMessageDelayed(Message, long)

3.接收、处理Message

handleMessage(Message)

使用方法举例:

    private Handler mMessageHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            ((Button) findViewById(R.id.btn_thread)).setText("" + mCount);
        }
    };
4. 子线程更新UI 异常处理

子线程不能更新UI,如果在子线程中更新UI,会出现CalledFromWrongThreadException 异常。

CalledFromWrongThreadException

解决方法:

子线程通过Handler 发送消息给主线程,让主线程处理消息,进而更新UI

5. 主线程给子线程发送消息的方法

此例子中子线程通过Looper不断遍历主线程发送的消息,Looper 使用方法如下:

准备Looper 轮询器

Looper.prepare();

Handler 处理遍历消息

Handler mHandler = new Handler()

遍历消息队列

Looper.loop();

Looper 使用方法如下:

    // 自定义 Loop 线程 ---> 不停的处理主线程发的消息
    class ChildLooperThread extends Thread {
        @Override
        public void run() {
            // 1.准备成为loop线程
            Looper.prepare();
            // 2.处理消息
            mMainHandler = new Handler() {
                // 处理消息
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                            ... ...
                        }
                    });
                }
            };
            // 3.Loop循环方法
            Looper.loop();
        }

    }

主线程发送消息给子线程 的使用例子如下:

启动 子线程,并再启动后发送消息

    public void BtnMainMessageMethod(View view) {
        // 点击主线程 按钮,启动子线程,并在子线程启动后发送消息
        Message msg = new Message();
        msg.obj = "主线程:这是我携带的信息";
        if (mMainHandler != null) {
            // 2.主线程发送消息
            mMainHandler.sendMessage(msg);
        } else {
            Toast.makeText(getApplicationContext(), "开启子线程轮询消息,请再次点击发送消息",
                    Toast.LENGTH_SHORT).show();
            // 1.开启轮询线程,不断等待接收主线成消息
            new ChildLooperThread().start();
        }
    }

子线程启动,不停的变量主线程发送的消息

    private Handler mMainHandler;
    String mMainMessage;

    // 自定义 Loop 线程 ---> 不停的处理主线程发的消息
    class ChildLooperThread extends Thread {
        @Override
        public void run() {
            // 1.准备成为loop线程
            Looper.prepare();
            // 2.处理消息
            mMainHandler = new Handler() {
                // 处理消息
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    mMainMessage = (String) msg.obj;
                    Log.i("TAG", "子线程:从主线程中接受的消息为:
" + mMainMessage);
                    // 使用 runOnUiThread 在主线程中更新UI
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ((Button) findViewById(R.id.btn_main_message))
                                    .setText(mMainMessage);
                        }
                    });
                }
            };
            // 3.Loop循环方法
            Looper.loop();
        }

    }
6. 子线程给主线程发送消息的方法

1.子线程发送消息给主线程方法

    public void BtnChildMessageMethod(View view) {

        new Thread() {
            public void run() {
                while (mCount < 100) {
                    mCount++;
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    /**
                     * 利用handler 对象发送消息 Message msg=Message.obtain(); Message
                     * msg=new Message(); 获取一个消息对象message
                     * */
                    Message msg = Message.obtain();
                    // 消息标记
                    msg.what = 1;
                    // 传递整型值msg.obj="传递object数据"
                    msg.arg1 = mCount;
                    Log.i("TAG", "count 值=" + mCount);
                    if (mhandler != null) {
                        mhandler.sendMessage(msg);
                    }
                }
            }
        }.start();
    }

2.主线程接收并处理消息的方法

// 定义一个handler 主线程 接收子线程发来的信息
    private Handler mhandler = new Handler() {
        // 處理消息的方法
        public void handleMessage(android.os.Message msg) {

            switch (msg.what) {
            case 1:
                int value = msg.arg1;
                Log.i("TAG", "value值=" + value);
                ((Button) findViewById(R.id.btn_child_message)).setText("当前值="
                        + value);
                break;

            default:
                break;
            }
        }

    };
7. 主、子 线程 互发消息方法

主要实现主、子线程每隔1s中通信一次

实现打印Log如下:

实现方法如下:

启动子线程并发送给主线程消息

    public void BtnMainChildMessageMethod(View view) {

        // 创建 名称为currentThread 子线程
        HandlerThread mChildThread = new HandlerThread("ChildThread");
        mChildThread.start();
        mChildHandler = new Handler(mChildThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {

                Log.i("TAG", "主线程对我说:" + msg.obj);

                // 子线程携带的消息
                Message message = new Message();
                message.obj = Thread.currentThread() + "我是子线程,小样,让我听你的没门";
                // 向主线程发送消息
                mainhandler.sendMessageDelayed(message, 1000);
            }
        };
        // 主线成发送空消息,开启通信
        mainhandler.sendEmptyMessage(1);
    }

2.主线程接收并处理子线程发送的消息

    // 创建主线程
    private Handler mainhandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.i("TAG", "子线程对我说:" + msg.obj);

            // 主线成携带的消息内容
            Message message = new Message();
            message.obj = Thread.currentThread() + "我是主线程:小子你得听我的。";

            // 向子线程发送消息
            mChildHandler.sendMessageDelayed(message, 1000);
        }
    };
8.子线程方法中调用主线程更新UI的方法 Activity 中 可以使用 runOnUiThread(Runnable)
                    // 使用 runOnUiThread 在主线程中更新UI
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ((Button) findViewById(R.id.btn_main_message))
                                    .setText(mMainMessage);
                        }
                    });
子线程使用 Handler.post(Runnable)
                    mRunnableHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            ((Button) findViewById(R.id.btn_runnable))
                                    .setText("Runnable");
                        }
                    });
View.post()
                            ((Button) findViewById(R.id.btn_runnable)).post(new Runnable() {
                                
                                @Override
                                public void run() {
                                    // TODO Auto-generated method stub
                                    ((Button) findViewById(R.id.btn_runnable)).setText("View.post()方法使用");
                                }
                            });
Handler.sendMessage(Message)
    public void BtnMainMessageMethod(View view) {
        // 点击主线程 按钮,启动子线程,并在子线程启动后发送消息
        Message msg = new Message();
        msg.obj = "主线程:这是我携带的信息";
        if (mMainHandler != null) {
            // 2.主线程发送消息
            mMainHandler.sendMessage(msg);
            }
}
9.移除Handler 发送的消息方法

1.移除 handler 发送的所有消息

private Handler mChildHandler;
mChildHandler.removeCallbacksAndMessages(null);

2.移除 指定消息

private Handler mainhandler;
mainhandler.removeMessages(what);

至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75832.html

相关文章

  • js事件详解二:鼠标和滚轮事件

    摘要:在级事件中定义了个鼠标事件,分别是。取消鼠标事件的默认行为还会影响其他事件,因为鼠标事件与其他事件是密不可分的关系。同样的,和支持这个事件。兼容各个浏览器的事件监听对象该对象封装了和级事件的常用事件函数。 概述 鼠标事件是web开发中最常用的一类事件,毕竟鼠标还是最主要的定位设备。在DOM3级事件中定义了9个鼠标事件,分别是:click,dbclick,mousedown,mousee...

    Lucky_Boy 评论0 收藏0
  • Handler详解

    我们在new Handler()时候,实际上调用的是两个参数的构造方法,我们看下 public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { fina...

    Caizhenhao 评论0 收藏0
  • js事件详解

    摘要:事件流事件流描述的是从页面中接收事件的顺序。其次,必须事先指定所有事件处理程序而导致的访问次数,会延迟整个页面的交互就绪时间。 1.事件流 事件流描述的是从页面中接收事件的顺序。 1.1 事件冒泡 IE中的事件流叫做冒泡,即时间最开始由最具体的元素接收,然后逐级向上传播到较为不具体的节点,直到传播到document对象。例: Event Exampple ...

    BDEEFE 评论0 收藏0

发表评论

0条评论

sihai

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<