Android--Handler使用应运及消息机制处理原理分析


最近开通了一个小微博,欢迎大家关注,每天分享一些上班路上看的小知识点

点击打开链接

一、Handler是什么 ?

handler是android给我们提供的一套用来更新UI的一套机制,也是一套消息处理机制,我们可以发送消息,也可以通过它处理消息

二、Handler的基本使用方法:

使用方法一 (普通更新UI方法)

//在子线程中使用,用来更新UI
 new Thread(){
            @Override
            public void run() {
                super.run();
                SystemClock.sleep(3000);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("三秒后更新文字");
                    }
                });
            }
        }.start();
这个可以在主线种中直接调用


使用方法二(循环消息更新UI)

private UpdateTextViewRuunable mRuunable = new UpdateTextViewRuunable();
    private int number = 1;
    private class UpdateTextViewRuunable implements  Runnable{
        @Override
        public void run() {
            number++;
            mTextView.setText("一秒更新一次"+number);
            mHandler.postDelayed(mRuunable,1000);
        }
    }

然后再在主线程中进行调用 :

mHandler.postDelayed(mRuunable,1000);

也就是开启循环



注:

 我们会发现当我们的应用程序退出的时候,这个线程还是在始终执行着,所以我们要在我们的应用程序退出的时候,停止循环操作
 也就是移除相关的消息发送

 @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(mRuunable);
    }




使用方法三(通过Message消息来传递数据)

创建保存数据的信息对象

class Person{
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

通过handler发送消息并传递相关数据

new Thread(){
            @Override
            public void run() {
                super.run();
                SystemClock.sleep(3000);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //   textView.setText("三秒后更新文字");
                        Message message = Message.obtain();
                        message.arg1 = 10;
                        message.arg2 = 12;
                        message.obj = new Person("zhaong san",102);
                        mHandler.sendMessage(message);
                    }
                });
            }
        }.start();

在handler中接收发送的消息和相关数据

private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            final int arg1 = msg.arg1;
            final int arg2 = msg.arg2;
            final Person obj = (Person) msg.obj;
            mTextView.setText("arg1 is "+arg1+"\n+arg2 is"+arg2+"\n person is "+obj.toString());
        }
    };



特别说明:

消息发送说明

在上面的发送消息的时候 ,我们使用的是mHandler的sendMessage方法,在这里发送消息,我们也可以使用

message.sendToTarget();

方法进行消息发送,message.sendToTarget方法简析

   /**
     * Sends this Message to the Handler specified by {@link #getTarget}.
     * Throws a null pointer exception if this field has not been set.
     */
    public void sendToTarget() {
        target.sendMessage(this);
    }
可以看到  message.sendToTarget(); 进行消息发送,实际上是调用了target的sendMessage方法进行消息发送
而这里使用到的target则是我们的一个Handler对象

创建消息对象说明

在这里我们是通过Message.obtain方法来获取一个消息 对象的,这里我们可以简单看一下其源码

 /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    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.obtain()在这里获取到一个Message对象,可以简单看一下其原理,实际上它是取出系统中存在的一个空的message对象,
如果这个对象为空,那么再进行创建

当然我们也可以直接使用new Message来创建一个新的消息对象


三、拦截Handler发送的消息


private  Handler mHandlerTwo = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return true;
        }
    }){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

在Handler接收发送过来的消息的时候会先回调callback中的handleMessage方法,然后再回调handler的handleMessage方法
当我们在callback的handleMessage方法返回为true时候,就会在callback中进行消息拦截,之后就不会再回调handleMessage方法了


四、Handler原理分析

* Handler主要是分析消息发送,默认情况下就是把消息发送给了自己

*Looper
    内部包含一个消息队列,也就是MessageQueue,所有的Handler发送的消息都走向这个消息队列

    Looper.Looper方法,就是一个死循环,不断的从MessageQueue中取消息,如有消息就进行消息处理,没有消息就阻塞

* 总结:
    Handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自已
    MessageQueue是一个存储消息的容器

* 在默认情况下,我们的应用程序是由ActivityThread来创建的,在ActivityThread中创建我们所有的Activity,并回调我们
Activity的构造方法,ActivytyThread也会默认去创建main线程(也就是主线程),同时也会默认创建一个Looper对象,而在
Looper中也会默认创建一个message对象

*源码分析:

* 我们查看ActivityThread类的main方法,可以看到在这里会首先调用方法  Looper.prepareMainLooper();

* 然后再查看Looper的prepareMainLooper方法

/**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

  可以看到 在这里首先调用了prepare方法
/** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

* 而在prepare方法中,使用到了sThreadLocal.get()方法
* sThreadLocal主要是用于在我们的线程运行中保存一些变量信息,其主要有set 方法 和get方法 ,set方法就是将我们  创建的变量放到ThreadLocal中
* 而get方法就是将变量拿出来

* 在这里我们默认的UI线程去调用它的get方法,默认情况下是等于null的,所以在这里 set了一个Looper对象

而我们再查看new Loopre的操作

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
    }

可以看到,在我们创建Looper对象的时候,会同时创建一个MessageQueue对象,而在这里的MessageQueue就是我们所有消息的一个消息队列

走到这里,在我们的UI线程中就创建了一个Looper对象和一个MessageQueue对象


然后当我们在应用程序中创建Handler的时候

/**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     */
    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;
    }

*在这里,无参数的构造会调用有参数的构造方法

*当执行到有两个参数的构造方法中后,首先会Looper.myLooper()方法获取一个Looper对象,

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

 

*可以看到这里调用了ThreadLocal.get方法,而上面我们分析到,主线程在进行创建应用程序的过程中会将一个Looper对象存放在ThreadLocal中,
在这里我们通过get方法将其取到,这样,Handler 与Looper 以及 MessageQuquene关联到一起了

*然后呢在我们的Handler的两个参数的构造方法中就可以通过方法  mQueue = mLooper.mQueue; 来拿到这里的MessageQueue消息队列

*当我们调用  mHandler.sendMessage(message);方法进行消息发送的时候

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

我们可以看到最终是调用的sendMessaeAtTime方法;在这里handler会通过当前线程拿到一个MessageQueue消息队列,然后调用下一个方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

在这里我们可以看到 msg.target是一个标识,是指消息要发送给谁,在这里,指向Handler对象本身,然后
调用了queue.euqueueMessage方法将消息放到这个消息队列中去



而在我们的Looper对象中loop方法中


public static void loop() {

        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

可以看到在这里首先是通过myLooper方法到获取到当前的Looper对象

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

然后再通过Looper对象来拿到我们创建的MessageQueue


然后就是在for循环中不断的取消息,如果取出的消息为null,直接return,如果取出的消息如果不为空,则调用msg.target.dispatchMessage(msg);处理消息


msg.target就是指向消息发送的方向,而在这里默认设置的是Handler本身
而在msg.target.dispatchMessage(msg);方法中


public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

可以看到在这里首先会去调用callback的handleMessage方法,当这个方法返回true的时候,这里会直接return,也就是说不再会执行本身的handleMessage方法
如果返回 的是false,那么会继续执行本身的handleMessage方法,这就是我们刚刚谈到的拦截handler接收的消息处理过程


Handler主要是封装了消息发送,默认情况下就是把消息发送给了自己

Looper
    内部包含一个消息队列,也就是MessageQueue,所有的Handler发送的消息都走向这个消息队列

    Looper.Looper方法,就是一个死循环,不断的从MessageQueue中取消息,如有消息就进行消息处理,没有消息就阻塞

总结:

    Handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自已
    MessageQueue是一个存储消息的容器

    在默认情况下,我们的应用程序是由ActivityThread来创建的,在ActivityThread中创建我们所有的Activity,并回调我们
    Activity的构造方法,ActivytyThread也会默认去创建main线程(也就是主线程),同时也会默认创建一个Looper对象,而在
    Looper中也会默认创建一个message对象



  

创建一个与子线程绑定的Handler

class  CustomThread extends  Thread{
    //定义一个Handler
    public Handler mHandler;
    @Override
    public void run() {
        super.run();
        Looper.prepare();
        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        Looper.loop();
    }
}

在使用的时候可直接创建CustomThread对象,然后通过对象来调用我们的线程Handler


创建一个子线程,并指定其中的一个Looper对象


class  CustomThread extends  Thread{
    //定义一个Handler
    public Handler mHandler;
    private Looper mLooper;
    @Override
    public void run() {
        super.run();
        Looper.prepare();
        mLooper = Looper.myLooper();
        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        Looper.loop();
    }
}

在主线程中进行调用

 final CustomThread customThread = new CustomThread();
        customThread.start();
        customThread.mHandler.sendEmptyMessage(0);
        final Handler handler = new Handler(customThread.mLooper){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        handler.sendEmptyMessage(0);

这样直接调用的时候,程序会抛出空指针异常,也就 是说当我们在主线程中调用 子线程(CustomThread)的Looper的时候,当我们的Looper对象还没有被创建出来的时候,然后我们主线程中的另一个Handler就使用到了,所以..


为了解决这样的问题,我们可以使用

private HandlerThread mHandlerThread;
private Handler mHandler;

mHandlerThread = new HandlerThread("custumThread");
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                System.out.println("Thread.currentThread name is "+Thread.currentThread());
            }
        };
        mHandler.sendEmptyMessage(1);

然后我们在主线程中执行

可以在控制台上看到

因为我们在new HandlerThread的时候就 定义了我们线程的名字

我们可以看一下在这里使用到的mHandlerThread.gtLooper方法

public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

 
调用这个方法的时候,首先会去判断下当前线程是否为空 

因为我们当前的HandlerThread其实就是一个线程

然后我们在getLooper方法中可以看到接下来就是判断当Looper对象为空的时候,使当前线程处于等待状态

在HandlerThread的run方法中

public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

可以看到这里创建了一个Looper对象,并且唤醒了我们的线程


主线程与子线程进行通信

public class ThreadMainToChildActivity extends Activity  {
    //创建主线程的Handler
    private Handler mMainHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            System.out.println("主线程中Handler执行的方法");
            final Message message = Message.obtain();
            //向子线程中发送消息
            mChildHandler.sendMessageDelayed(message, 1000);
        }
    };
    private HandlerThread mChildThread;
    private Handler mChildHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_thread_to_child);
        mChildThread = new HandlerThread("childThread");
        mChildThread.start();
        //创建子线程的Handler
        mChildHandler = new Handler(mChildThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                System.out.println("子线程中执行的方法");
                final Message message = Message.obtain();
                //主线程Handler发送消息
                mMainHandler.sendMessageDelayed(message,1000);
            }
        };

        mMainHandler.sendEmptyMessage(1);
    }
}

运行



Android自定义控件ImageViwe(一)——依据控件的大小来设置缩放图片显示
   点击查看分析文档
    
 Android自定义ImageView(二)——实现双击放大与缩小图片
    点击打开链接
    
 Android自定义控件ImageViwe(三)——随手指进行图片的缩放
   点击打开链接
    
 Android自定义控件ImageViwe(四)——多点触控实现图片的自由移动  
    点击打开链接
    
 Android ListView分组排序显示数据
    点击打开链接
    
 Android自定义下拉刷新功能的ListView
    点击打开链接
    
 Android音乐播放器高级开发
    点击打开链接
    
 Android自定义控件之流式布局
 点击打开链接

 Android自定义下拉刷新功能的ListView
 点击打开链接




早起的年轻人 CSDN认证博客专家 移动开发 项目管理 Java
只要用心去做,每一件事情还是有可能成功的,当然成功是没有界限的,只不过是达到自己心里的那个目标,公众号:我的大前端生涯,一个爱喝茶的程序员,通常会搞搞SpringBoot 、Herbinate、Mybatiys、Android、iOS、Flutter、Vue、小程序等.
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页