Android 基础知识系列之 Handler

640?wx_fmt=gif

640?wx_fmt=gif

极力推荐Android 开发大总结文章:欢迎收藏程序员Android 力荐 ,Android 开发者需要的必备技能

640?wx_fmt=other

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

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

  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

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

4.Looper

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

总结:

640?wx_fmt=png

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

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),

 Message msg = Message.obtain();
                    // 消息标记
                    msg.what = 1;
                    // 传递整型值msg.obj="传递object数据"
                    msg.arg1 = mCount;
                    Log.i("TAG", "count 值=" + mCount);
                    if (mhandler != null) {
                        mhandler.sendMessage(msg);
                    }
  • 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.

解决方法:

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

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

  1. 准备Looper 轮询器

  1. Handler 处理遍历消息

  1. 遍历消息队列

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如下:

640?wx_fmt=png

主、子线程通信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)

至此,线程间通信已结束,

子线程发送消息

640?wx_fmt=jpeg

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

640?wx_fmt=jpeg

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

640?wx_fmt=jpeg

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

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

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

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

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

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

posted @ 2018-12-06 08:51  程序员Android的博客  阅读(124)  评论(0编辑  收藏  举报