handler 订阅
Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。 展开全文
Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。
信息
领    域
计算机编程
通常用途
处理相对耗时比较长的操作
应    用
异步消息处理
外文名
handler
handler特点
1. 传递Message。用于接受子线程发送的数据, 并用此数据配合主线程更新UI。在Android中,对于UI的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,那么就需要把数据消息作为一个Message对象发送到消息队列中,然后,由Handler中的handlerMessage方法处理传过来的数据信息,并操作UI。当然,Handler对象是在主线程中初始化的,因为它需要绑定在主线程的消息队列中。类sendMessage(Message msg)方法实现发送消息的操作。 在初始化Handler对象时重写的handleMessage方法来接收Message并进行相关操作。2. 传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。Handler对象在进行初始化的时候,会默认的自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法。 [1] 
收起全文
精华内容
下载资源
问答
  • handler
    千次阅读
    2021-03-18 00:30:21

    简单的说,Handler获取当前线程中的looper对象,looper用来从存放Message的MessageQueue中取出Message,再有Handler进行Message的分发和处理.

    Message Queue(消息队列):用来存放通过Handler发布的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列

    Handler:可以发布或者处理一个消息或者操作一个Runnable,通过Handler发布消息,消息将只会发送到与它关联的消息队列,然也只能处理该消息队列中的消息

    Looper:是Handler和消息队列之间通讯桥梁,程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的

    Handler:Handler接受到消息后调用handleMessage进行处理

    Message:消息的类型,在Handler类中的handleMessage方法中得到单个的消息进行处理

    在单线程模型下,为了线程通信问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:

    1. Message

    Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。

    2. Handler

    Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。

    3. Message Queue

    Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。

    每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。

    4. Looper

    Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。对于子线程使用Looper,API Doc提供了正确的使用方法:这个Message机制的大概流程:

    1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。

    2. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用 该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:

    1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;

    2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;

    3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。

    由此可见,我们实现的handleMessage方法是优先级最低的!

    3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!

    在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!

    1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper;

    2. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。

    现在我们再来看一下使用Handler的基本实现代码:

    // 主线程中新建一个handler

    normalHandler = new Handler() {

    public void handleMessage(android.os.Message msg) {

    btnSendMsg2NormalHandler.setText("normalHandler");

    Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--normalHandler handleMessage run...", Thread.currentThread()

    .getName()));

    }

    };

    ...

    //发送消息到hanlder

    myThreadHandler.sendEmptyMessage(0);

    你现在已经很清楚了sendEmptyMessage到handleMessage的过程,途中经过Looper.MessageQueue队列,转由Looper所在的线程去处理了,这是一个异步的过程,当然Looper所在的线程也可以是sendEmptyMessage所在的线程。

    看了上面你也许还是迷惑不解,那么什么要Looper了,跟我们要用的Handler又有啥鸟关系呢?

    我在前面一直强调在主线程中使用handler,为什么要这么说呢,因为你在自己new一个新线程中去像我前面那样简单建立一个Handler,程序执行是会报错的:

    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

    at android.os.Handler.(Handler.java:121)

    at com.cao.android.demos.handles.HandleTestActivity$MyThread$1.(HandleTestActivity.java:86)

    at com.cao.android.demos.handles.HandleTestActivity$MyThread.run(HandleTestActivity.java:86)

    为什么在主线程中不会报错,而在自己新见的线程中就会报这个错误呢?很简单,因为主线程它已经建立了Looper,你可以打开ActivityThread的源码看一下:

    public static final void main(String[] args) {

    SamplingProfilerIntegration.start();

    Process.setArgV0("");

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();

    thread.attach(false);

    Looper.loop();

    if (Process.supportsProcesses()) {

    throw new RuntimeException("Main thread loop unexpectedly exited");

    }

    thread.detach();

    String name = (thread.mInitialApplication != null)

    ? thread.mInitialApplication.getPackageName()

    : "";

    Slog.i(TAG, "Main thread of " + name + " is now exiting");

    }

    在main函数中它已经做了这个事情了,为什么要调用 Looper.prepareMainLooper(); Looper.loop();我们可以进去看一下,在prepareMainLooper方法中新建了一个looper对象,并与当前进程进行了绑定,而在Looper.loop方法中,线程建立消息循环机制,循环从MessageQueue获取Message对象,调用  msg.target.dispatchMessage(msg);进行处理msg.target在myThreadHandler.sendEmptyMessage(0)设置进去的,因为一个Thead中可以建立多个Hander,通过msg.target保证MessageQueue中的每个msg交由发送message的handler进行处理,那么Handler又是怎样与Looper建立联系的呢,在Handler构造函数中有这样一段代码:

    mLooper =Looper.myLooper();        if (mLooper == null) {

    throw new RuntimeException(

    "Can't create handler inside thread that has not called Looper.prepare()");

    }

    mQueue = mLooper.mQueue;

    在新建Handler时需要设置mLooper成员,Looper.myLooper是从当前线程中获取绑定的Looper对象:

    public static final Looper myLooper() {

    return(Looper)sThreadLocal.get();    }

    若Looper对象没有创建,就会抛异常"Can't create handler inside thread that has not called Looper.prepare()"这跟我前面讲的是一致的。所以我们在一个新线程中要创建一个Handler就需要这样写:

    class MyThread extends Thread {

    public void run() {

    Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]-- run...", Thread

    .currentThread().getName()));

    // 其它线程中新建一个handler

    Looper.prepare();//创建该线程的Looper对象,用于接收消息,在非主线程中是没有looper的所以在创建handler前一定要使用prepare()创建一个Looper                        myThreadHandler = new Handler() {

    public void handleMessage(android.os.Message msg) {

    Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler handleMessage run...", Thread

    .currentThread().getName()));

    }

    };Looper.myLooper().loop();//建立一个消息循环,该线程不会退出                }

    }

    现在,你应该对Handler的机制有所了解了吧,若有什么疑问,欢迎在评论中提出

    在其它线程中Handler使用主线程的Looper

    前面我说了在新线程中要新建一个Handler需要调用Looper.prepare();也有另一种方法就是使用主线程中的Looper,那就不必新建Looper对象了:

    threadMainLoopHandler =new Handler(Looper.getMainLooper()){                                public void handleMessage(android.os.Message msg) {

    Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--threadMainLoopHandler handleMessage run...", Thread

    .currentThread().getName()));

    }

    //该handleMessage方法将在mainthread中执行

    };

    这时候注意不要在handleMessage做太多的操作,因为它在主线程中执行,会影响主线程执行ui更新操作。

    使用Message.callback回调

    public void dispatchMessage(Message msg) {

    if (msg.callback != null) {

    handleCallback(msg);

    } else {

    if (mCallback != null) {

    if (mCallback.handleMessage(msg)) {

    return;

    }

    }

    handleMessage(msg);

    }

    }

    从dispatchMessage定义可以看出,如果Message对象自带callback对象,handler不会执行handleMessage方法而是执行message.callback中定义的run方法,当然callback还是在handler关联的looper所绑定的线程中执行的。实际上Handler.post(Runnable r)方法就是把r添加到一个msg.callback的,也就是说,下面两种写法,没有什么区别:

    1.使用Message.callback

    Message msg = Message.obtain(myThreadHandler,newRunnable() {

    @Override

    publicvoidrun() {

    Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler.Message.callback.run",

    Thread.currentThread().getName()));

    }

    });

    myThreadHandler.sendMessage(msg);

    2.使用Handler.post

    myThreadHandler.post(newRunnable() {

    @Override

    publicvoidrun() {

    Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler.Message.callback.run",

    Thread.currentThread().getName()));

    }

    });

    3.Handler对Activity finish影响。

    在开发的过程中碰到一个棘手的问题,调用Activity.finish函数Acitivity没有执行生命周期的ondestory函数,后面查找半天是因为有一个handler成员,因为它有一个delay消息没有处理,调用Activity.finish,Activity不会马上destory,所以记得在Ativity finish前清理一下handle中的未处理的消息,这样Activity才会顺利的destory

    更多相关内容
  • handler.post和handler.sendMessage本质上是没有区别的,都是发送一个消息到消息队列中,而且消息队列和handler都是依赖于同一个线程的。接下来通过本文给大家分享android handler.post和handler.sendMessage的区别...
  • Handler使用

    2017-02-14 15:49:04
    整理了Handler的各种使用方法
  • 资源包括如何在一个activity A里激活另一个activity B。 然后在两个activity之间用handler传输数据。在activity A里定义全局变量handler,用sendMessage来发数据,在handler里数据以消息队列的方式发送。
  • Handler 使用详解

    千次阅读 2021-02-27 20:50:00
    阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:Handler 消息处理机制原理Handler 机制处理的4个关键对象Handler...

    1c272b63e1e098ac69ab49e1a813b1d3.gif

    d85f2eccb2341c07ed074177a4c922dc.png

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

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

    Handler 消息处理机制原理

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

    Handler常用方法

    子线程更新UI 异常处理

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

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

    主、子 线程 互发消息方法

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

    Handler是 Android中用来更新UI 的一套消息处理机制。Handler 允许线程间发送Message或Runnable对象进行通信。在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发送的Message,Looper则是不断的从消息队列中取消,如果有消息就取出发送给Handler 处理,没有则阻塞。

    总结:

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

    41d69e91fe102c4d5e07d45585f71742.png

    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 + "\n" + 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 + "\n 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

    1a834156aa3af30aa4da17da679a6e5b.png

    解决方法:

    子线程通过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", "子线程:从主线程中接受的消息为:\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. 子线程给主线程发送消息的方法

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

    d4ba5fa720002b19ebd4d3ef251d6ffd.png

    实现方法如下:

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

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

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

    db590a4fd388ef925a54667c21de0447.png

    展开全文
  • 源码解析 Handler 面试宝典

    千次阅读 多人点赞 2022-05-18 13:56:49
    在Android 的中,高级面试中,我们经常会被问到Handler 相关的知识点,而且占重比例还比较大,这是什么呢?下面一起来看一张图:由上图我们可以看出,整个APP 的启动流程: ActivityThread.main() 就是我们APP 独有...

    前言

    在Android 的中,高级面试中,我们经常会被问到Handler 相关的知识点,而且占重比例还比较大,这是什么呢?下面一起来看一张图:

    其实一部手机,就是一个Handler,

    由上图我们可以看出,整个APP 的启动流程: Launcher(APP): zygote -> jvm -> ActivityThread.main() ActivityThread.main() 就是我们APP 独有的main 启动方法,如图所示,绿色的部分,就是Handler 为我们开辟的独有空间,启动主线程独有的Looper,将当前App 独立出来。

    由此我们可以得出一个结论:Handler 并不是只属于进程通讯,进程通讯只是Handler 的附属功能,而Handler 的真正功能是 所有的代码,都是在Handler 中运行的

    1、一个线程有几个Handler

    考点

    这里面试官其实想了解的是,你对Handler的认知,如果这个问题打不出来,那么面试官也不会再去问了。

    答案

    在说答案之前,先看一直 Handler 的执行流程图:
    在这里插入图片描述

    由上图,我们可以看出:

    • 一个线程里面,可以创建N个的Handler,如hander.sentXXX、 handler.postXX 都是在创建一个Handler。 每一个message,就是我们插入到消息队列 中的消息节点。

    2、一个线程有几个Looper?如何保证

    考点

    这里面试官其实想了解的是,你对Handler 流程及源码的理解。

    答案

    • 在回答这个问题之前,我们再看一下Handler 的执行流程图:
      handler -> sendMessage -> messageQueue.enqueueMessage -> looper.loop() -> messasgeQueue.next() -> handler.dispatchMessage() -> handler.handerMessage() ,handler 发送message,进入messageQueue.enqueueMessage 队列,进入looper中经过loop 死循环的不断遍历,驱动队列一直前进,经过handler.dispatchMessage() 分发给handler.handerMessage,这样我们就走完了整个的Handler 流程,也可以直接看错,一个线程中,只有一个Looper
    • 如何保证的呢?ThreadLocal 多线程,线程上下文的存储变量,其实ThreadLocal 并不能存储任何的东西,但是在ThreadLocal 中,有一个ThreadLocalMap 集合,里面存储的<this, value> ,this 就是上下文,唯一的ThreadLocal key,key 唯一了,那么value 也就是唯一的,ThreadLocal在创建的时候,会有一个判断,如果已经创建了,会报异常,所以一个线程有唯一的ThreadLocal 就有唯一的looper。如下图:

    ThreadLocal 线程隔离工具类

    在这里插入图片描述

    ThreadLocal 创建源码

    在这里插入图片描述

    3、Handler 内存泄漏原因?为什么其他的内部类没有说过这个问题

    考点

    应该是考官想知道,你对于GC回收 JVM 相关的东西吧。

    答案

    在这里,我我们先看一段代码:

        Handler handler = new Handler(){
            @SuppressLint("HandlerLeak")
            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.d("tiger", "handleMessage: ");
                View view = null;
                click(view);
                MainActivity2.this.click(view);
            }
        };
    
    
        public void click(View view) {
            
        }
    
    • 上面代码片段中的handler 代码,会标黄,并给予一个警告:这个处理程序类应该是静态的,否则可能发生内存泄漏。
    • 那么为什么其他类不会有呢?
      生命周期的问题。流程 sendMessage -> sendMessageAtTime -> enqueueMessage 在 enqueueMessage 中,有段代码 msg.target = this;,意思就是Message 会持有当前的handler,handler 已经成为了massage的一部分,假如我设置一个消息需要等待20分钟后执行,那么就意味,我的message会一直等待20分钟之后才会执行,message 持有 handler,handler 持有 (this)activity,这样就导致GC无法回收,JVM 通过可达性算法,告诉我们,没法到达,就无法回收。内部类的生命周期,一旦在外部类生命周期中被别的生命周期持有了,那么外部类也不能被释放。

    4、为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么?

    考点

    好烦啊,我也不知道啊,为什么还要要求这个?

    答案

    • 在APP 启动的时候,就在ActivityThread.main() 方法中,就创建了looper.loop() ,源码如下:
      在这里插入图片描述
    • 那么如何在子线程中new Handler呢?只需要在子线程中,手动添加 Looper.prepare();Looper.loop(); 就可以了。请看下面代码
        public void click (View view){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Looper.prepare();
                        mHandler = new Handler() {
                            public void handleMessage(Message msg) {
                                // do something()
                            }
                        };
                        Looper.loop();
                    }
                }).start();
            }
    

    5、子线程中维护的Looper,消息队列无消息的时候的处理方法是什么?有什么用?

    考点

    对于源码的掌握程度

    答案

    • 子线程中维护的Looper 在无消息的时候调用quit,可以结束循环。loop() 是一个死循环,想要退出,必须msg == null。请看下面源码:
    public static void loop() {
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                msg.recycleUnchecked();
            }
        }
    
    
    • 只有在调用quit 的时候,才会返回null。
    Message next() {
            for (;;) {
                synchronized (this) {
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
                }
            }
        }
    
    void quit(boolean safe) {
            synchronized (this) {
                if (mQuitting) {
                    return;
                }
                mQuitting = true;
            }
        }
    
    
    • 所以使用quit() 唤醒队列,执行loop() 退出循环,子线程looper 不在执行了。

    在这里插入图片描述

    • 消息入队:根据时间排序,当队列满的时候,阻塞,直到用户通过next() 取出消息。当next 方法被调用的时候,通知MassageQueue 可以消息入队。
    • 消息出队:由Looper.loop()进行循环, 对queue 进行轮询操作,当消息达到执行时间就取出来,当MessageQueue 为空的时候,队列阻塞,等消息调用queue massage 的时候,通知队列,可以取出消息,停止阻塞。
    • handler 没有使用多线程中的阻塞队列 BlockQueue,因为主线程(系统)也在使用,如果使用BlockQueue 设置上限的话,系统可能会出现卡顿等情况。

    在这里插入图片描述

    • 从上图中,我们看可以出,handler 是一个生产者 - 消费者的设计模式。下面我们来看一下,looper 中的两种循环阻塞方式:
    1. 执行时间阻塞(没有到执行时间),nativePollOnce(long ptr, int timeoutMillis) 执行阻塞操作,timeoutMillis 为 -1 表示无限等待,直到事件发生为止,如果为0,无需等待,立即执行。请看下面源码:
        Message next() {
            final long ptr = mPtr;
            if (ptr == 0) {
                return null;
            }
            int nextPollTimeoutMillis = 0;
            for (;;) {
                nativePollOnce(ptr, nextPollTimeoutMillis);// 循环进入阻塞状态,等待执行时间到达后唤醒
                synchronized (this) {
                    if (msg != null) {
                        if (now < msg.when) {
                            // 消息不为空,并且没有到执行时间,nextPollTimeoutMillis 不为-1
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        }
                    }
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
                }
            }
        }
    
    1. MessagaQueue 为空,执行阻塞,等待唤醒。当插入消息时,主动唤醒,请看下面源码:
    
        Message next() {
            if (ptr == 0) { // mPtr==0,表示中断循环,
                return null;
            }
    
            int pendingIdleHandlerCount = -1;
            int nextPollTimeoutMillis = 0;
            for (;;) {
                nativePollOnce(ptr, nextPollTimeoutMillis);
                synchronized (this) {
                    if (msg != null) {
                    } else {
                        // 无消息,timeoutMillis为-1表示无限等待,直到有事件发生为止
                        nextPollTimeoutMillis = -1;
                    }
                }
            }
        }
        // mPtr==0
        private void dispose() {
            if (mPtr != 0) {
                nativeDestroy(mPtr);
                mPtr = 0;
            }
        }
        // 唤醒
        boolean enqueueMessage(Message msg, long when) {
            synchronized (this) {
                boolean needWake;
                Message p = mMessages;
                if (p == null || when == 0 || when < p.when) {
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                }
                // mPtr != 0 循环没有中断,进行唤醒操作.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    
    
    
    

    6、既然可以存在多个Handler 往MessageQueue 中添加数据(发消息时各个Handler 可能处于不同线程),那它内部是如何确保线程安全的?

    考点

    线程锁,后续会更多 synchronized 相关的东西

    答案

    • synchronized锁: synchronized内置锁,它是由jvm自动完成的,插入和取都需要锁,因为取的时候,可能正在插入。它是锁的对象,因为MessageQueue 每个线程中只有一个Looper,每个Looper又只有一个MessageQueue.

    7、我们使用Message 时应该如何创建它?

    考点

    难道是想知道有没有使用过?

    答案

    • 在创建Message 对象时,有三种方法:
    1. Message message = new Message();
    2. Message message1 = Message.obtain(); 查看源码的时候,发现内部调用的也是obtain() 方法。
    3. Message message2 = handler.obtainMessage();

    8、Looper 死循环为什么不会导致应用卡死

    考点

    难道是 ANR 的机制?

    答案

    • 应用卡死也就是发生ANR,那什么是ANR?ANR是如何检测的,知道了ANR是如何检测的,我们就知道Looper死循环为什么不会导致应用卡死?

    什么是ANR?

    • ANR指的是应用无响应,ANR主体实现在系统层。所有与ANR相关的消息,都会经过系统进程(AMS)调度,然后派发到应用进程完成对消息的实际处理,同时,系统进程设计了不同的超时限制来跟踪消息的处理。 一旦应用程序处理消息不当,超时限制就起作用了, 它收集一些系统状态,比如CPU/IO使用情况、进程函数调用栈,并且报告用户有进程无响应了(ANR对话框)。

    总结

    🤩
    🎉 原 创 不 易 , 还 希 望 各 位 大 佬 支 持 一 下 \textcolor{blue}{原创不易,还希望各位大佬支持一下}

    👍 点 赞 , 你 的 认 可 是 我 创 作 的 动 力 ! \textcolor{green}{点赞,你的认可是我创作的动力!}

    🌟 收 藏 , 你 的 青 睐 是 我 努 力 的 方 向 ! \textcolor{green}{收藏,你的青睐是我努力的方向!}

    ✏️ 评 论 , 你 的 意 见 是 我 进 步 的 财 富 ! \textcolor{green}{评论,你的意见是我进步的财富!}

    展开全文
  • Android——Handler详解

    万次阅读 多人点赞 2021-05-27 10:58:53
    Handler是一套 Android 消息传递机制 可以说只要有异步线程与主线程通信的地方就一定会有 Handler。 在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,...

    1. 简介

    Handler是一套 Android 消息传递机制,主要用于线程间通信。

    用最简单的话描述: handler其实就是主线程在起了一个子线程,子线程运行并生成Message,Looper获取message并传递给Handler,Handler逐个获取子线程中的Message.

    Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信

    可以说只要有异步线程与主线程通信的地方就一定会有 Handler。

    在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理
    在这里插入图片描述
    使用Handler消息传递机制主要是为了多个线程并发更新UI的同时,保证线程安全
    在这里插入图片描述

    2. 相关概念解释

    Handler、Message、Message Queue、Looper

    • Message :代表一个行为what或者一串动作Runnable, 每一个消息在加入消息队列时,都有明确的目标Handler

    • ThreadLocal: 线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。ThreadLocal的作用是提供线程内的局部变量TLS,这种变量在线程的生命周期内起作用,每一个线程有他自己所属的值(线程隔离)

    • MessageQueue (C层与Java层都有实现) :以队列的形式对外提供插入和删除的工作, 其内部结构是以双向链表的形式存储消息的

    • Looper (C层与Java层都有实现) :Looper是循环的意思,它负责从消息队列中循环的取出消息然后把消息交给Handler处理

    • Handler :消息的真正处理者, 具备获取消息、发送消息、处理消息、移除消息等功能

    在这里插入图片描述

    Android消息机制:
    在这里插入图片描述

    • 以Handler的sendMessage方法为例,当发送一个消息后,会将此消息加入消息队列MessageQueue中。
    • Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。
    • 在Handler的handleMessage方法中处理该消息,这就完成了一个消息的发送和处理过程。

    Handler示意图:
    在这里插入图片描述
    消息机制的模型:

    • Message:需要传递的消息,可以传递数据;
    • MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
    • Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
    • Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

    消息机制的架构

    • 在子线程执行完耗时操作,当Handler发送消息时,将会调用 MessageQueue.enqueueMessage ,向消息队列中添加消息。
    • 当通过 Looper.loop 开启循环后,会不断地从线程池中读取消息,即调用 MessageQueue.next
    • 然后调用目标Handler(即发送该消息的Handler)的 dispatchMessage 方法传递消息,然后返回到Handler所在线程,目标Handler收到消息,调用 handleMessage 方法,接收消息,处理消息。

    3. Handler 的基本使用

    3.1 创建 Handler

    Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。

    这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。

    解决方案:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息。

    public class HandlerActivity extends AppCompatActivity {
    
        private Button bt_handler_send;
    
        private static class MyHandler extends Handler {
    
            //弱引用持有HandlerActivity , GC 回收时会被回收掉
            private WeakReference<HandlerActivity> weakReference;
    
            public MyHandler(HandlerActivity activity) {
                this.weakReference = new WeakReference(activity);
            }
    
            @Override
            public void handleMessage(Message msg) {
                HandlerActivity activity = weakReference.get();
                super.handleMessage(msg);
                if (null != activity) {
                    //执行业务逻辑
                    Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
                }
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.HandlerActivity);
    
            //创建 Handler
            final MyHandler handler = new MyHandler(this);
    
            bt_handler_send = findViewById(R.id.bt_handler_send);
            bt_handler_send.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            //使用 handler 发送空消息
                            handler.sendEmptyMessage(0);
    
                        }
                    }).start();
                }
            });
        }
        
        @Override
        protected void onDestroy() {
            //移除所有回调及消息
            myHandler.removeCallbacksAndMessages(null);
            super.onDestroy();
        }
    }
    
    

    3.2 Message 获取

    获取 Message 大概有如下几种方式:

    Message message = myHandler.obtainMessage(); 		   //通过 Handler 实例获取
    Message message1 = Message.obtain();   			      //通过 Message 获取
    Message message2 = new Message();      				 //直接创建新的 Message 实例
    

    通过查看源码可知,Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法

    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }
    
    

    通过查看 Message 的 obtain 方法

    public static Message obtain(Handler h) {
            //调用下面的方法获取 Message
            Message m = obtain();
            //将当前 Handler 指定给 message 的 target ,用来区分是哪个 Handler 的消息
            m.target = h;
    
            return m;
        }
        
    //从消息池中拿取 Message,如果有则返回,否则创建新的 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,使用前两种方式进行创建。

    3.3 Handler 发送消息

    Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列,post方法需要传入一个Runnalbe对象 ,我们来看看post方法源码

        public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    

    不过不管我们调用什么方法,最终都会走到 MessageQueue.enqueueMessage(Message,long) 方法。
    以 sendEmptyMessage(int) 方法为例:

    //Handler
    sendEmptyMessage(int)
      -> sendEmptyMessageDelayed(int,int)
        -> sendMessageAtTime(Message,long)
          -> enqueueMessage(MessageQueue,Message,long)
      			-> queue.enqueueMessage(Message, long);
    

    从中可以发现 MessageQueue 这个消息队列,负责消息的入队,出队。

    4. 两个实例(主线程-子线程)

    4.1 子线程向主线程

    首先我们在MainActivity中添加一个静态内部类,并重写其handleMessage方法。

    private static class MyHandler extends Handler {
            private final WeakReference<MainActivity> mTarget;
    
            public MyHandler(MainActivity activity) {
                mTarget = new WeakReference<MainActivity>(activity);
            }
    
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                HandlerActivity activity = weakReference.get();
                super.handleMessage(msg);
                if (null != activity) {
                    //执行业务逻辑
                    if (msg.what == 0) {
                    	Log.e("myhandler", "change textview");
                    	MainActivity ma = mTarget.get();
    	                ma.textView.setText("hahah");
                	}
                    Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
                }
            
            }
     }
    

    然后创建一个类型为MyHandler的私有属性:

      private Handler handler1 = new MyHandler(this);
    

    最后在onCreate回调中创建一个线程,用于接收发送消息:

    new Thread(new Runnable() {
                @Override
                public void run() {
                    handler1.sendEmptyMessage(0);
                }
            }).start();
    

    总结一下:

    • Handler的子类对象一般是在主线程中进行创建,以便在两个线程中都能访问。我们创建了Handler类的子类MyHandler,并重写了handlerMessage方法,这个方法是当使用接收处理发送的消息的。然后我们创建了一个子线程,在子线程中我们使用MyHandler的对象调用sendEmptyMessage方法发送了一个空的Message。然后我们就能在主线程中接收到这个数据。

    4.2 主线程向子线程

    首先创建一个MyHandler类。

    private static class MyHandler extends Handler {
    
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                if (msg.what == 0) {
                    Log.e("child thread", "receive msg from main thread");
                }
            }
        }
    

    声明一个Handler类型的私有变量,进行默认初始化为null。

     private Handler handler1;
    

    创建子线程,使handler指向新创建的MyHandler对象。

    new Thread(new Runnable() {
                @Override
                public void run() {
                    Looper.prepare();
                    handler1 = new MyHandler();
                    Looper.loop();
                    Log.e("child thread", "child thread end");
                }
            }).start();
    

    在主线程中向子线程中发送消息

    while (handler1 == null) {
    
            }
    
            handler1.sendEmptyMessage(0);
            handler1.getLooper().quitSafely();
    

    5. Handler机制原理

    5.1 Handler原理概述

    普通的线程是没有looper的,如果需要looper对象,那么必须要先调用Looper.prepare方法,而且一个线程只能有一个looper

    Handler是如何完成跨线程通信的?

    • Android中采用的是Linux中的 管道通信
      • 关于管道,简单来说,管道就是一个文件
      • 在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的
    • 消息队列创建时
      • 调用JNI函数,初始化NativeMessageQueue对象。NativeMessageQueue则会初始化Looper对象
      • Looper的作用就是,当Java层的消息队列中没有消息时,就使Android应用程序主线程进入等待状态,而当Java层的消息队列中来了新的消息后,就唤醒Android应用程序的主线程来处理这个消息

    在这里插入图片描述

    • Handler通过sendMessage()发送Message到MessageQueue队列
    • Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理
    • 经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理
    • 将Message加入MessageQueue时,处往管道写入字符,可以会唤醒loop线程;如果MessageQueue中- 没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,往往用于做一些清理性地工作

    5.2 Handler与管道通信

    管道,其本质是也是文件,但又和普通的文件会有所不同:管道缓冲区大小一般为1页,即4K字节。管道分为读端和写端,读端负责从管道拿数据,当数据为空时则阻塞;写端向管道写数据,当管道缓存区满时则阻塞。

    • 在Handler机制中,Looper.loop方法会不断循环处理Message,其中消息的获取是通过 Message msg = queue.next(); 方法获取下一条消息。该方法中会调用nativePollOnce()方法,这便是一个native方法,再通过JNI调用进入Native层,在Native层的代码中便采用了管道机制。

    • 我们知道线程之间内存共享,通过Handler通信,消息池的内容并不需要从一个线程拷贝到另一个线程,因为两线程可使用的内存时同一个区域,都有权直接访问,当然也存在线程私有区域ThreadLocal(这里不涉及)。即然不需要拷贝内存,那管道是何作用呢?

    • Handler机制中管道作用就是当一个线程A准备好Message,并放入消息池,这时需要通知另一个线程B去处理这个消息。线程A向管道的写端写入数据1(对于老的Android版本是写入字符W),管道有数据便会唤醒线程B去处理消息。管道主要工作是用于通知另一个线程的,这便是最核心的作用。
      在这里插入图片描述

    为什么采用管道而非Binder?

    • 从内存角度:通信过程中Binder还涉及一次内存拷贝,handler机制中的Message根本不需要拷贝,本身就是在同一个内存。Handler需要的仅仅是告诉另一个线程数据有了。
    • 从CPU角度,为了Binder通信底层驱动还需要为何一个binder线程池,每次通信涉及binder线程的创建和内存分配等比较浪费CPU资源。

    5.3 Handler举例详解

    Handler是Android消息机制的上层接口。Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。通常情况下,Handler的使用场景就是更新UI

    如下就是使用消息机制的一个简单实例:

    public class Activity extends android.app.Activity {
    	private Handler mHandler = new Handler(){
    		@Override
    		public void handleMessage(Message msg) {
    			super.handleMessage(msg);
    			System.out.println(msg.what);
    		}
    	};
    	@Override
    	public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
    		super.onCreate(savedInstanceState, persistentState);
    		setContentView(R.layout.activity_main);
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				...............耗时操作
    				Message message = Message.obtain();
    				message.what = 1;
    				mHandler.sendMessage(message);
    			}
    		}).start();
    	}
    }
    

    再举个例子:怎么从主线程发送消息到子线程?(虽然这种应用场景很少)

    Thread thread = new Thread(){
                @Override
                public void run() {
                    super.run();
                    //初始化Looper,一定要写在Handler初始化之前
                    Looper.prepare();
                    //在子线程内部初始化handler即可,发送消息的代码可在主线程任意地方发送
                    handler=new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                          //所有的事情处理完成后要退出looper,即终止Looper循环
                            //这两个方法都可以,有关这两个方法的区别自行寻找答案
                            handler.getLooper().quit();
                            handler.getLooper().quitSafely();
                        }
                    };
                  
                    //启动Looper循环,否则Handler无法收到消息
                    Looper.loop();
                }
            };
            thread.start();
        //在主线程中发送消息
        handler.sendMessage();
    

    先来解释一下第一行代码Looper.prepare();,先看看Handler构造方法

    //空参的构造方法,这个方法调用了两个参数的构造方法
      public Handler() {
            this(null, false);
        }
    
    //两个参数的构造方法
    public Handler(Callback callback, boolean async) {
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    • Handler的构造方法中会验证Looper,如果Looper为空,那么会抛出空指针异常
    • Handler在构造方法中还做了一件事,将自己的一个全局消息队列对象(mQueue)指向了Looper中的消息队列,即构造方法中的这行代码mQueue = mLooper.mQueue;

    第二行代码初始化了Hanlder并且重写HandleMessage()方法,没啥好讲的。

    然后调用了Looper.loop()方法,后面会解释。

    我们先来看看最后一行代码handler.sendMessage(message) 的主要作用:

    先看看这行代码之后的代码执行流程:
    在这里插入图片描述
    sendMessage()之后代码通过图中所示的几个方法,最终执行到了MessageQueue的enqueueMessage()方法,我们来就看看它:

    boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            synchronized (this) {
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    
    • MessageQueue是一个单向列表结构,而MessageQueue 的 enqueueMessage()方法主要做的事情就是将 Handler发送过来的 Message插入到列表中。
    • 调用handler.senMessage()方法的时候,最终的结果只是将这个消息插入到了消息队列中

    发送消息的工作已经完成,那么Looper是什么时候取的消息,来看看:

    public static void loop() {
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                try {
                    msg.target.dispatchMessage(msg);
                } 
            }
        }
    
    • 这是一个死循环
    • 这个循环的目的是从MessageQueue中取出消息
    • 取消息的方法是MessageQueue.next()方法
    • 取出消息后调用message.target对象的dispatchMessage()方法分发消息
    • 循环跳出的条件是MessageQueue.next()方法返回了null

    看到这里我们应该自然会想到,message.target.是哪个对象?dispatchMessage有什么作用?

    public final class Message implements Parcelable {
     /*package*/ int flags;
    
        /*package*/ long when;
    
        /*package*/ Bundle data;
    
        /*package*/ Handler target;
    
        /*package*/ Runnable callback;
    
        // sometimes we store linked lists of these things
        /*package*/ Message next;
    }
    
    • 在Looper从MessageQueue中取出Message之后,调用了Handler的dispatchMessage()方法
      这里我们不禁要问,这个target指向了哪个Handler,再来看看之前的enqueueMessage
    //Handler的方法
     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
    • 第一行代码就是,将message的target属性赋值为发送message的handler自身
    • Looper取出消息后,调用了发送消息的Handler的dispatchMessage()方法,并且将message本身作为参数传了回去。到此时,代码的执行逻辑又回到了Handler中。

    接着看handler的dispatchMessage()方法

    /**
        *handler的方法
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    看到这里面一个我们非常熟悉到方法了没有?—handleMessage()方法,也是我们处理消息时候的逻辑。

    最后再来一个系统的工作示意图:
    在这里插入图片描述

    6. Handler用法(android与java区别)

    6.1 刷新UI界面

    使用java:

    new Thread( new Runnable() {     
        public void run() {     
             myView.invalidate();    
         }            
    }).start();
    

    可以实现功能,刷新UI界面。但是这样是不行的,因为它违背了单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

    Thread+Handler
    Handler来根据接收的消息,处理UI更新。Thread线程发出Handler消息,通知更新UI。

    Handler myHandler = new Handler() {  
              public void handleMessage(Message msg) {   
                   switch (msg.what) {   
                        case TestHandler.GUIUPDATEIDENTIFIER:   
                             myBounceView.invalidate();  
                             break;   
                   }   
                   super.handleMessage(msg);   
              }   
         }; 
    
    class myThread implements Runnable {   
              public void run() {  
                   while (!Thread.currentThread().isInterrupted()) {    
                           
                        Message message = new Message();   
                        message.what = TestHandler.GUIUPDATEIDENTIFIER;   
                          
                        TestHandler.this.myHandler.sendMessage(message);   
                        try {   
                             Thread.sleep(100);    
                        } catch (InterruptedException e) {   
                             Thread.currentThread().interrupt();   
                        }   
                   }   
              }   
         }   
    

    6.2 定时器(延时操作)

    使用java:
    使用Java上自带的TimerTask类,TimerTask相对于Thread来说对于资源消耗的更低,引入import java.util.Timer; 和 import java.util.TimerTask;

    public class JavaTimer extends Activity {  
      
        Timer timer = new Timer();  
        TimerTask task = new TimerTask(){   
            public void run() {  
                setTitle("hear me?");  
            }            
        };  
    
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
           
             timer.schedule(task, 10000);   // 延迟delay毫秒后,执行一次task。
    
        }  
    }
    

    TimerTask + Handler

    public class TestTimer extends Activity {  
      
        Timer timer = new Timer();  
        Handler handler = new Handler(){   
            public void handleMessage(Message msg) {  
                switch (msg.what) {      
                case 1:      
                    setTitle("hear me?");  
                    break;      
                }      
                super.handleMessage(msg);  
            }  
              
        };  
    
        TimerTask task = new TimerTask(){    
            public void run() {  
                Message message = new Message();      
                message.what = 1;      
                handler.sendMessage(message);    
            }            
        };  
    
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
        
            timer.schedule(task, 10000);   // 延迟delay毫秒后,执行一次task。
        }  
    }  
    
    

    6.3 定时更新 UI

    Runnable + Handler.postDelayed(runnable,time)

       private Handler handler = new Handler();  
      
        private Runnable myRunnable= new Runnable() {    
            public void run() {  
                if (run) {  
                    handler.postDelayed(this, 1000);  
                    count++;  
                }  
                tvCounter.setText("Count: " + count);  
    
            }  
        }; 
    

    然后在其他地方调用

    handler.post(myRunnable);
    handler.post(myRunnable,time);
    

    6.4 Handler实现延迟执行

    Handler延迟2s执行一个runnable

    Handler handler=new Handler();
    Runnable runnable=new Runnable() {
     @Override
    	public void run() {
    		// TODO Auto-generated method stub
    		if(xsLayout.getVisibility()==View.VISIBLE){
    			xsLayout.setVisibility(View.GONE);
    		}
    	}
    };
    handler.postDelayed(runnable, 2000);
    

    在runnable被执行之前取消这个定时任务

    handler.removeCallbacks(runnable);
    

    7. 总结

    7.1 总结

    Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。

    • Looper :负责关联线程以及消息的分发在该线程下从 MessageQueue 获取 Message,分发给Handler ;
    • MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的Message;
    • Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。

    Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。

    线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决

    7.2 注意

    (1)一个线程有几个Handler?

    • 多个,通常我们开发过程中就会new出不止一个Handler。

    (2)一个线程有几个Looper?如何保证?

    • 仅有1个Looper
    • Looper的构造是私有的,只有通过其prepare()方法构建出来,当调用了Looper的prepare()方法后,会调用ThreadLocal中的get()方法检查ThreadLocalMap中是否已经set过Looper?
    • 如果有,则会抛出异常,提示每个线程只能有一个Looper,如果没有,则会往ThreadLocalMap中set一个new出来的Looper对象。
    • 这样可以保证ThreadLocalMap和Looper一一对应,即一个ThreadLocalMap只会对应一个Looper。而这里的ThreadLocalMap是在Thread中的一个全局变量,也只会有一个,所以就可以保证一个Thread中只有一个Looper。

    (3)Handler内存泄漏的原因?

    • 内部类持有外部的引用。
    • Handler原理:由于Handler可以发送延迟消息,所以为了保证消息执行完毕后,由同一个Handler接收到,所以发送出去的Message中会持有Handler的引用,这个引用存在Message的target字段中,是Handler所有的sendMessage()方法最后都会调用enqueueMessage(),而在enqueueMessage()中会给Message的target字段赋值this。
    • 因此Message持有Handler的引用,Handler又持有Activity的引用,所以在Message处理完之前,如果Activity被销毁了,就会造成内存泄漏。
    • 怎么解决?可以使用static修饰Handler对象。

    (4)为何主线程可以new Handler?如果想要在子线程中new Handler要做些什么准备?

    • 因为在ActivityThread中的main()已经对Looper进行了prepar()操作,所以可以直接在主线程new Handler。
      • android-28的SystemServer类中:
      • main方法是整个android应用的入口,在子线程中调用Looper.prepare()是为了创建一个Looper对象,并将该对象存储在当前线程的ThreadLocal中,每个线程都会有一个ThreadLocal,它为每个线程提供了一个本地的副本变量机制,实现了和其它线程隔离,并且这种变量只在本线程的生命周期内起作用,可以减少同一个线程内多个方法之间的公共变量传递的复杂度。Looper.loop()方法是为了取出消息队列中的消息并将消息发送给指定的handler,通过msg.target.dispatchMassage()方法
        /**
         * The main entry point from zygote.
         */
        public static void main(String[] args) {
            new SystemServer().run();
        }
    
    
        private void run() {
            ......
            Looper.prepareMainLooper();
            ......
        }
    
    
    • 如果想在子线程中new Handler,则需要先手动调用Looper的prepare()方法初始化Looper,再调用Looper的loop()方法使Looper运转。
          new Thread(new Runnable() {
                @Override
                public void run() {
                    Looper.prepare();			//初始化Looper
                    new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                        }
                    };
                    Looper.loop();
                }
            })
    
    

    (5)子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?

    • 如果不处理的话,会阻塞线程,处理方案是调用Looper的quitSafely();
    • quitSafely()会调用MessageQueue的quit()方法,清空所有的Message,并调用nativeWake()方法唤醒之前被阻塞的nativePollOnce(),使得方法next()方法中的for循环继续执行,接下来发现Message为null后就会结束循环,Looper结束。如此便可以释放内存和线程。

    (6)内部是如何确保线程安全的?

    • 可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程)
      添加消息的方法enqueueMessage()中有synchronize修饰,取消息的方法next()中也有synchronize修饰。
    • Handler的delay消息(延迟消息)时间准确吗?
      由于上述的加锁操作,所以时间不能保证完全准确。

    (7)使用Message时应该如何创建它?

    • 使用Message的obtain()方法创建,直接new出来容易造成内存抖动。
    • 内存抖动是由于频繁new对象,gc频繁回收导致,而且由于可能被别的地方持有导致无法及时回收所以会导致内存占用越来越高。
    • 使用obtain()对内存复用,可以避免内存抖动的发生。其内部维护了一个Message池,其是一个链表结构,当调用obtain()的时候会复用表头的Message,然后会指向下一个。如果表头没有可复用的message则会创建一个新的对象,这个对象池的最大长度是50。

    (8)使用Handler的postDelay后消息队列会有什么变化?

    • 如果此时消息队列为空,不会执行,会计算消息需要等待的时间,等待时间到了继续执行。

    (9)Looper死循环为什么不会导致应用卡死?

    • 卡死就是ANR,产生的原因有2个:
      • 1、在5s内没有响应输入的事件(例如按键,触摸等),
      • 2、BroadcastReceiver在10s内没有执行完毕。
    • 事实上我们所有的Activity和Service都是运行在loop()函数中,以消息的方式存在,所以在没有消息产生的时候,looper会被block(阻塞),主线程会进入休眠,一旦有输入事件或者Looper添加消息的操作后主线程就会被唤醒,从而对事件进行响应,所以不会导致ANR
    • 简单来说looper的阻塞表明没有事件输入,而ANR是由于有事件没响应导致,所以looper的死循环并不会导致应用卡死。

    6.3 接下来

    接下来,让我们看看HandlerThread

    展开全文
  • Android Handler 消息机制

    千次阅读 2022-04-04 11:50:43
    每一个搞 Android 的对 Handler 应该都不陌生,网上大神的文章也是数不尽的。这篇文章是个人在学习 Handler 过程中的一些疑问的记录。 1. 什么是 Handler,有什么作用 Handler 首先是一个 java 类,定义在/...
  • android中handler用法总结

    千次阅读 2021-06-05 09:12:40
    一、Handler的定义:Handler主要接收子线程发送的数据, 并用此数据配合主线程更新UI,用来跟UI主线程交互用。比如可以用handler发送一个message,然后在handler的线程中来接收、处理该消息,以避免直接在UI主线程中...
  • Handler

    千次阅读 2018-07-30 19:24:10
    Handler:作用就是发送与处理信息,如果希望Handle正常工作,在当前线程中要有一个Looper对象 Message:Handle接收与处理的消息对象 MessageQueue:消息队列,先进先出管理Message,在初始化Looper...
  • Fragment与Activity使用Handler进行交互

    千次下载 热门讨论 2014-06-27 12:26:19
    这里简单介绍了Fragment和Activity使用Handler方式进行交互。
  • mybatis解析-TypeHandler作用详解

    千次阅读 2021-02-16 20:07:07
    TypeHandler被称作类型处理器,MyBatis在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时,都会用类型处理器将Java对象转化为数据库支持的类型或者将获取到数据库值以合适的方式转换成 Java...
  • 通俗易懂聊springMVC中的handler是什么

    千次阅读 2021-11-22 22:59:50
    了解过SpingMVC流程的同学一定听说过handler,百度翻译过来是处理者,很多博客中称之为处理器.那就按照大部分人的说法称呼它为控制器.说道控制器,会不会联想到我们平常写业务代码中的各种controller,也是控制器,是不是...
  • Android全面解析之由浅及深Handler消息机制

    千次阅读 多人点赞 2020-10-08 21:17:45
    前言 ...我希望写一篇从入门到深入,系统地全面地讲解Handler的文章,帮助大家认识Handler。 这篇文章的讲解深度循序渐进,不同程序的读者可选择对应的部分查看: 第一部分是对于Handler的入门概述。
  • handler机制原理

    千次阅读 2021-02-02 19:27:40
    一,什么是handler handler是消息机制的一个上层接口 更新UI的操作 耗时完毕后发送消息给主线程更新UI 耗时操作只能在子线程中执行,Android是线程不安全的你不能在子线程中更新UI 所以Android引入了handler机制 ...
  • Handler完全解读——Handler的使用

    千次阅读 2018-09-18 15:52:57
    Handler完全解读——Handler的使用 Handler是什么 Handler是Android给我们提供用于更新UI的一套机制,也是一套消息处理机制。我们用它可以发送消息,也可以用它处理消息。在Android开发中有着非常重要的地位。 为...
  • Android之Handler简单介绍

    万次阅读 2020-07-15 08:40:56
    今天我们的主角:Android消息机制——Handler Handler是什么: Android系统中线程间传递消息的一种机制 Handler作为这么重要的一种机制,虽然我们在使用的时候基本上只用到这一个类,但是还是有必要了解一下与之...
  • [Android答答答]Handler是什么?

    千次阅读 2022-01-05 15:43:32
    Handler是什么?概念1. handler是什么2.什么是MessageQueque3.Message是什么4.Looper又是啥5.为什么要使用Handler用法1.handler怎么用?原理1.handler是如何发送消息的2.Looper死循环为什么不会卡死应用3.一个线程有...
  • Handler标准用法(推荐)

    千次阅读 2021-12-10 11:17:29
    Handler经常会出现泄露问题,原因是handler持有了activity的引用后导致activity无法被回收,如下是推荐使用的标准用法
  • Thread、Looper、Handler这三个是android消息驱动机制的核心对象,让我们先分析并了解一下一个线程拥有消息处理能力的过程: class LooperThread extends Thread { public Handler mHandler; public void run() ...
  • 1. 什么是 typeHandler typeHandler 是MyBatis框架集成的一个数据类型处理器,用于 java 数据类型 和 数据库类型 之间的互转,当然java对于数据库的操作需要jdbc,所以可以理解为 java数据类型 和 jdbc类型 之间的...
  • Handler总结

    千次阅读 2021-12-08 20:15:29
    Handler相关 一.谈一谈你对handler的认识,例如:机制和实现等。 handler机制图解: handler常见使用过程: ①.子线程向主线程发送消息: 在主线程中新建Handler实例,并重写handleMessage()方法用于处理子...
  • Android多线程(Handler篇)

    万次阅读 多人点赞 2018-06-13 23:09:01
    今天的主角是Handler,本文将从使用及源码来分析探索其奥秘。 使用 步骤: 创建Handler对象,实现handlMessage()方法 创建Runnable线程 此时产生一个Looper,并自动创建一个消息队列MessageQueue()...
  • # 添加图例,通过handler_map参数添加自定义映射 lg = plt.legend( [s1, s2], ["类型1", "类型2"], handler_map={s1: custom_handler1, s2: custom_handler2}, labelspacing=2, fontsize=10, frameon=False, ) # ...
  • Handler用法及解析

    万次阅读 多人点赞 2018-08-10 17:01:12
    1.handler作用:  1)传递消息Message 2)子线程通知主线程更新ui 2.常用api 3.handler使用避免内存泄露  1)handler怎么使用会产生内存泄露?  2)如何避免handler的内存泄露?  3) 雷区 4.handlerThread...
  • Handler内存泄漏原因及解决方案

    千次阅读 2022-03-31 08:23:52
    一、Handler造成内存泄露的原因 在Activity内将Handler声明成非静态内部类或者匿名内部类,这样Handle默认持有外部类Activity的引用。如果Activity在销毁时,Handler还有未执行完或者正在执行的Message,而Handler又...
  • AMessage-ALooper-AHandler消息机制流程分析
  • Android面试题:Handler

    千次阅读 2022-04-25 09:29:07
    1.一个线程有几个Handler? 答:一个线程可以有多个Handler。 2.一个线程有几个Looper?如何保证? 答:一个线程只能有1个Looper。Looper对象通过ThreadLocalMap保存,ThreadLocalMap保存键值对,该键值对的键为...
  • 读懂Android Handler中send方法与post方法的区别及各自的使用方法。
  • Mybatis优雅存取json字段的解决方案 - TypeHandler (一)

    万次阅读 多人点赞 2019-11-30 23:37:23
    register(TypeReference javaTypeReference, TypeHandler<? extends T> handler) Mybatis 并没有直接使用到,内部是从 javaTypeReference 获取到 rawType 之后,调用 register(javaType, typeHandler) 进行注册 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,100,607
精华内容 440,242
关键字:

handler

友情链接: 第4章.zip