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

欢迎关注微信公众号:程序员Android
公众号ID:ProgramAndroid
获取更多信息

微信公众号:ProgramAndroid

微信公众号:ProgramAndroid

我们不是牛逼的程序员,我们只是程序开发中的垫脚石。
我们不发送红包,我们只是红包的搬运工。

通过本章学习你将掌握以下知识点

  1. Handler 消息处理机制原理
  2. Handler 机制处理的4个关键对象
  3. Handler常用方法
  4. 子线程更新UI 异常处理
  5. 主线程给子线程发送消息的方法
  6. 子线程给主线程发送消息的方法
  7. 主、子 线程 互发消息方法
  8. 子线程方法中调用主线程更新UI的方法

继承关系如下:

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 发送的Message,Looper则是不断的从消息队列中取消,如果有消息就取出发送给Handler 处理,没有则阻塞。

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

Handler 使用方法详解_第1张图片

主线程、子线程间通信简单流程

3. Handler常用方法

1.Runnable对象

  • post(Runnable)

使用方法举例:

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)
  1. Message 对象
  • sendEmptyMessage(int)

使用方法举例:

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);            }        }    }   //1.Message  对象        new MessageHandlerThreadMethod("子线程不能更新UI").start();
  • sendMessage(Message),
    使用方法举例:
  • 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
CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

解决方法:
子线程通过Handler 发送消息给主线程,让主线程处理消息,进而更新UI。

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

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

    1. 准备Looper 轮询器
      Looper.prepare();
    1. Handler 处理遍历消息
      Handler mHandler = new Handler()
    1. 遍历消息队列
      Looper.loop();

Looper 使用方法如下:

 class LooperThread extends Thread {      public Handler mHandler;      public void run() {          Looper.prepare();          mHandler = new Handler() {              public void handleMessage(Message msg) {                  // process incoming messages here              }          };          Looper.loop();      }  }

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

    public void BtnMainMessageMethod(View view) {        Message msg = new Message();        msg.obj = "主线程:这是我携带的信息";        if (mMainHandler != null) {            mMainHandler.sendMessage(msg);        } else {            Toast.makeText(getApplicationContext(), "开启子线程轮询消息,请再次点击发送消息", Toast.LENGTH_SHORT).show();            //开启轮询线程,不断等待接收主线成消息            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", "子线程:从主线程中接受的消息为:\n" + mMainMessage);                    // 使用 runOnUiThread  在主线程中更新UI                    runOnUiThread(new Runnable() {                        @Override                        public void run() {                            ((Button) findViewById(R.id.btn_main_message)).setText(mMainMessage);                        }                    });                }            };            // 3.Loop循环方法            Looper.loop();        }    }

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

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

    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();    }    //定义一个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如下:

Handler 使用方法详解_第2张图片

主、子线程通信log信息

  • 实现方法如下:
    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();    }    //定义一个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;            }        }    };    private Handler mChildHandler;    //创建主线程    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);        }    };    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);    }

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() {                            ((Button) findViewById(R.id.btn_runnable)).setText("Runnable");                        }                    });
  • Handler.sendMessage(Message)

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

如有侵权,请联系小编,小编对此深感抱歉,届时小编会删除文章,立即停止侵权行为,请您多多包涵。

既然都看到这里,领两个红包在走吧!
以下两个红包每天都可以领取

1.支付宝搜索 522398497,或扫码支付宝红包海报。

支付宝扫一扫,每天领取大红包

2.微信红包,微信扫一扫即可领取红包

 

微信扫一扫,每天领取微信红包

小礼物走一走,来简书关注我

更多相关文章

  1. android异步任务完成后再执行主线程任务
  2. Android应用程序键盘(Keyboard)消息处理机制分析(27)
  3. Android消息推送接收后,通知栏的显示
  4. Android应用程序键盘(Keyboard)消息处理机制分析(17)
  5. Android使用线程获取网络图片的方法
  6. android中使用多线程——HandlerThread举例
  7. android 多线程断点下载,listview 模式 开始 暂停等功能

随机推荐

  1. android 监听电话去电接通瞬间[非来电]
  2. 2013年4月19日--佳都新太Android笔试题+
  3. Android几种消息推送方案总结
  4. Android中滑屏初探 - scrollTo 以及 scro
  5. Android 调用第三方so中方法记录
  6. android 的Android Media Scanner多媒体
  7. [Android] Android自定义对话框(Dialog)
  8. Android socket 编程 实现消息推送(二)
  9. 【实习周记】Android中ProtoBuf的使用与
  10. android之ImageView