精华内容
下载资源
问答
  • 简述Handler机制原理
    2020-10-16 11:21:43

    1.Handler是什么?

    Handler是android提供用于更新UI的一套机制,也是消息处理机制。也可以说是消息传递机制。

    Handler的主要作用有2个

         1.在新启动的线程中发送消息

         2.在主线程即UI线程中获取并处理消息

    说明:启动一个APP时,首先会开启一个主线程(UI线程),主要管理UI界面,更新UI等操作

    a.如果进行一个耗时的操作,请求后台数据,并更新UI。这时候请求后台数据是需要时间的,不能放在主线程中进行。需要开启一个子线程来执行请求数据的操作

    b.请求完成后,需要更新UI,就需要用到Handler了,Handler运行在主线程中,通过Message对象来传递数据,子线程发送Message对象给主线程,接收消息并处理更新UI等操作。

    使用方法:

    private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                tv.setText("msg.arg1:"+msg.arg1+"--msg.arg2:"+msg.arg2);
            }
        };
       
     
       
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message msg = new Message();//消息对象
                    msg.arg1 = 99;//携带参数
                    msg.arg2 = 100;//携带参数
                    Object str = new Object();//实例化对象
                    msg.obj = str; //携带参数为实体类对象
                    handler.sendMessage(msg)
                }
            });
    

     

    2.Handler的主要方法

    handler可以分发Message对象和Runnable对象到主线程中,每个Handler实例,都会绑定到创建他的线程中(一般是位于主程),它有两个作用:

            (1)合理调度安排消息和runnable对象,使它们在将来的某个点被执行.

            (2)安排一个动作在不同的线程中执行

    Handler中开启线程和分发消息的一些方法:

          post(Runnable)直接开启Runnable线程

          postAtTime(Runnable,long)在指定的时间long,开始启动线程

          postDelayed(Runnable long)在延迟long时间后,启动Runnable线程

          sendEmptyMessage(int) 发送指定的消息,通过参数int来区分不同的消息

          sendMessage(Message)发送消息到UI线程中

          sendMessageAtTime(Message,long)     这个long代表的是系统时间,不推荐用

         sendMessageDelayed(Message,long)    此方法long代表调用后几秒后执行。

       sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.

    handler基本使用: 1)在主线程中,使用handler很简单,new一个Handler对象实现其handleMessage方法,在 handleMessage 中提供收到消息后相应的处理方法即可。(接收消息,并且更新UI)

                         2)在新启动的线程中发送消息

     

    a.Message  

    Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。使用Message的arg1和arg2便可携带int数据,使用obj便可携带Object类型数据。

    2.Handler
      Handler顾名思义就是处理者的意思,它只要用于在子线程发送消息对象Message,在UI线程处理消息对象Message,在子线程调用sendMessage方法发送消息对象Message,而发送的消息经过一系列地辗转之后最终会被传递到Handler的handleMessage方法中,最终在handleMessage方法中消息对象Message被处理。

    3.MessageQueue
      MessageQueue就是消息队列的意思,它只要用于存放所有通过Handler发送过来的消息。这部分消息会一直存放于消息队列当中,等待被处理。每个线程中只会有一个MessageQueue对象,请牢记这句话。其实从字面上就可以看出,MessageQueue底层数据结构是队列,而且这个队列只存放Message对象。

    4.Looper
      Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当MesssageQueue中存在一条消息,Looper就会将这条消息取出,并将它传递到Handler的handleMessage()方法中。每个线程只有一个Looper对象。

     

    理解以上成员后,理解就很简单了。

    子线程发送消息,主线程接收消息。

     

     

     

    更多相关内容
  • handler机制原理

    千次阅读 2021-02-02 19:27:40
    耗时操作只能在子线程中执行,Android是线程不安全的你不能在子线程中更新UI 所以Android引入了handler机制 handler通过发送和处理message和Runnable对象来关联相对应线程的MssagerQueue(消息队列) 1.可以让对应的...

    一,什么是handler
    handler是消息机制的一个上层接口  更新UI的操作 耗时完毕后发送消息给主线程更新UI
    耗时操作只能在子线程中执行,Android是线程不安全的你不能在子线程中更新UI 所以Android引入了handler机制
    handler通过发送和处理message和Runnable对象来关联相对应线程的MssagerQueue(消息队列)
    1.可以让对应的message(message是放置信息,可以传递一些参数)和runnable(Runnable则是直接给出处理的方法)在未来的某个时间进行相应处理
    2.让自己想要处理的耗时操作放在子线程,让更新UI操作放在主线程
    3.队列就是依次执行,Handler会处理完一个消息或者执行完某个处理在进行下一步,这样不会出现多个线程同时要求进行UI处理而引发的混乱现象
    二,handler的使用方法
    1.post(runnable)  Runnable则是直接给出处理的方法)在未来的某个时间进行相应处理源码底层还是调用了sendmassage(massage)方法
    2.sendmassage(massage)  放置信息,可以传递一些参数 ,可以定时处理更新UI
    三,handler机制的原理

    首先我们先看图


    looper 
    每一个线程所独有的,通过loop()方法读取MessageQueue当中的消息 读到消息后,把消息发送给Handler来进行处理
    messageQueue
    消息队列(先进先出的方式来管理的message),在创建looper的时候就创建了MESSagerQueue,已经关联到了一起
    mssage
    消息对象

    handler
    作用1.可以发送消息
    作用2.处理消息(处理looper发送过来的消息)

    1.1获取looper (mLooper = Looper.myLooper()),通过looper 获取massageQueue( mQueue = mLooper.mQueue;)

      /**
         * Use the {@link Looper} for the current thread with the specified callback interface
         * and set whether the handler should be asynchronous.
         *
         * Handlers are synchronous by default unless this constructor is used to make
         * one that is strictly asynchronous.
         *
         * Asynchronous messages represent interrupts or events that do not require global ordering
         * with respect to synchronous messages.  Asynchronous messages are not subject to
         * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
         *
         * @param callback The callback interface in which to handle messages, or null.
         * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
         * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
         *
         * @hide
         */
        public Handler(Callback callback, boolean async) {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
    
            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;
        }


    1.2  looper是怎么获取的呢?
     

     // sThreadLocal.get() will return null unless you've called prepare().
        static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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();
        }

    sThreadLocal.get()作用:通过不同的线程访问同一个ThreadLocal ,不管是get方法还是set方法对所做的读写操作仅限于各自线程内部,这就是为什么handler里边通过ThreadLocal 来保存looper 这样他就可以使每一个线程有单独唯一的looper

    1.3 looper作用
     

     /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the 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
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
    
                final long traceTag = me.mTraceTag;
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
                final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                final long end;
                try {
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                if (slowDispatchThresholdMs > 0) {
                    final long time = end - start;
                    if (time > slowDispatchThresholdMs) {
                        Slog.w(TAG, "Dispatch took " + time + "ms on "
                                + Thread.currentThread().getName() + ", h=" +
                                msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                    }
                }
    
                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();
            }
        }

    源码中loopr 就是创建了一个FOR的死循环,然后从消息队列中逐个的去获取消息,最后处理消息的过程 
    总结:looper 是通过prepare 来创建LOOPER    把他保存在ThreadLocal中 然后通过lopper.loop()开启循环来进行消息的分发

       /** 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));
        }

    源码中调用  msg.target.dispatchMessage(msg); target就是一个handler 是中转器   (/*package*/ Handler target;)

    handler总结: 通过handler将消息传递给了消息队列(massageQueue) 而消息队列又将消息分发给了handler 来处理,这就是handler的2个作用,一个是接收,发送消息,一个是处理消息

    四,handler引起的内存泄露及解决办法

    handler发送的消息在当前handler的消息队列中,如果此时activity finish掉了,那么消息队列的消息依旧会由handler进行处理,若此时handler声明为内部类(非静态内部类),我们知道内部类天然持有外部类的实例引用,这样在GC垃圾回收机制进行回收时发现这个Activity居然还有其他引用存在,因而就不会去回收这个Activity,进而导致activity泄露。

    解决方案:

    1.把handler设置成静态内部类,因为静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄露

    2.onDestroy生命周期中调用 handler.removeCallbacks();,进行释放

    3.handler内部类持有外部activity的弱引用

     


     

    展开全文
  • 首先,我将Handler相关的原理机制形象的描述为以下情景: Handler:快递员 Message:包裹 MessageQueue:快递仓储空间 Looper:配送车 某天,你想给朋友寄一件礼物,首先你会包裹好,下单加了某家的快递员上门...

    首先,我将Handler相关的原理机制形象的描述为以下情景:

    • Handler:快递员(属于某个快递公司的职员)
    • Message:包裹(可以放置很多东西的箱子)
    • MessageQueue:快递分拣中心(分拣快递的传送带)
    • Looper:快递公司(具有处理包裹去向的管理中心)

    情景分析:在子线程中更新主线程的UI

    其中的原理机制可以形象的理解为:

    某天,你想给朋友寄一件礼物,首先你拿个箱子装好礼物并包裹好,下单叫了某家的快递员上门取件,快递员揽收你的包裹后,会将包裹送往快递分拣中心,等待配送车送出你的包裹。等配送车来了,就按照你的包裹地址信息,送到指定地方站点,然后分派给相应的快递员,将你的包裹送到你的朋友手里。

    这整个邮寄包裹的过程可以形象的理解为Handler的工作机制原理,下面还原一下实际工作过程:

    某时,你想刷新主界面的TextView,无奈你不在主线程,此时你就会包装好Message,然后声明一个Handler,让Handler将你的Message送往主线程(Looper),Handler将你的Message送到主线程后,还需要排队等待,等轮到你的时候,主线程就会告诉Handler,这个Message可以处理了,你负责分发一下,于是,Handler将该Message分发到相应的回调或者handleMessage( ) 方法中,于是,你就在该方法中更新了UI。

    下面,我对这四位大佬一个个进行介绍。

    一、Message(消息)

    Message.class位于android.os.包中。Message的构造函数为无参构造方法,且只有一个构造方法;

    public Message() { }
    

    除了构造方法可以创建实例对象外,还可以通过内部的静态方法来创建:

    static Message obtain()
    static Message obtain(Message orig)
    static Message obtain(Handler h)
    static Message obtain(Handler h, Runnable callback)
    static Message obtain(Handler h, int what)
    static Message obtain(Handler h, int what, Object obj)
    static Message obtain(Handler h, int what, int arg1, int arg2)
    static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)
    

    以上几个静态的方法里面都会首先调用第一个方法来创建一个Message对象,我们来看看源码(API 28)

    	public static final Object sPoolSync = new Object();    //同步锁对象
        private static Message sPool;                           //全局池消息实例
        
    	/**
         * 从全局池返回一个新的消息实例,允许我们在许多情况下避免分配新对象。
         */
        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    //......
                    return m;
                }
            }
            return new Message();
        }
    

    如果当前全局池的Message实例不为空,则返回第一个消息实例。所以,大多数情况下,使用obtain()来获得一个Message对象,可以避免消耗更多的内存资源。

    对于其他 static obtain( ) 的重载方法,通过源码,可以发现,都是进行赋值操作,没有太多的可讨性。唯一得注意一下的是 obtain(Handler h, Runnable callback)这个静态方法:

    	/*package*/ Handler target;
    	/*package*/ Runnable callback;
    
    	public static Message obtain(Handler h, Runnable callback) {
            Message m = obtain();
            m.target = h;
            m.callback = callback;
            return m;
        }
    

    可以看到,也是赋值操作,target是保护级别的成员变量,即只有同包名空间可以访问,此变量意义重大。除了target,还有一个callback,这个callback也是配合着Handler来发挥作用的。后面讲到Handler会解释到,请稍安勿躁。

    此时,大家先要记住的就几点:

    • Message有8个静态方法可以创建Message实例
    • Message有两个重要的成员变量,分别为target 和callback,一个是Handler,一个是Runnable。
    • Message有4个公开变量what、arg1、arg2、obj 可以存储消息进行传递
    • Message还有一个包间成员变量next,它是Message类型,后面会使用到,知道有这个next就行

    以上就是Message的基本秘密了,很简单,没有什么复杂的东西(作为一个包裹箱,就是这么简单,能装一些东西,然后附带一些关键信息)。

    二、Handler(处理机)

    Handler.class也位于android.os包中。Handler英文意思为:处理者,管理者,处理机。它在消息传递过程中扮演着重要的角色,是消息的主要处理者,说白了,就是收消息,最终处理消息(它就像一个快递员,收快递,然后领快递单,派送快递)。

    1、Handler的构造方法(API 28)

    Handler()
    Handler(Callback callback)
    Handler(boolean async)
    Handler(Callback callback, boolean async)
    Handler(Looper looper)
    Handler(Looper looper, Callback callback)
    Handler(Looper looper, Callback callback, boolean async)
    

    通过源码可以发现,上面的构造方法都是上面一个个往下调用的,第一个调用第二个,第二个调用第三个…所以,我们首先把目光放在最后一个方法上:

    	public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;				
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    这是一个赋值的构造方法。再看另外一个构造方法:

    	public Handler(Callback callback, boolean async) {
            //......
            mLooper = Looper.myLooper();  //返回与当前线程关联的Looper对象,在后面Looper会讲到
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;  //返回Looper对象的消息队列,在后面MessageQueue会讲到
            mCallback = callback;  //接口回调
            mAsynchronous = async; //是否异步
        }
        
    	public interface Callback {
            public boolean handleMessage(Message msg); //这个函数大家都很熟悉了,暂不细说,总之都知道是用来回调消息的
        }
    

    整个构造方法的过程中会确立以下几件事:

    • 获取当前Handler实例所在线程的Looper对象:mLooper = Looper.myLooper()
    • 如果Looper不为空,则获取Looper的消息队列,赋值给Handler的成员变量mQueue:mQueue = mLooper.mQueue
    • 可以设置Callback 来处理消息回调:mCallback = callback

    Handler是消息的处理者,但是它并不是最终处理消息的那个大佬,它有且只能有一个上级个领导,就是Looper,Handler是将消息上报给Looper(领导),然后排队等待,等Looper(领导)处理完消息了,就会通知Handler去领取消息,给Handler分配任务,Handler拿到消息后在自行往下分发,Handler只能听命与Looper(领导)。

    举个实际运用中的情景:

    当你需要在子线程中更新主线程的UI时,你就会在当前的Activity下创建一个Handler对象,然后在它的handleMessage() 中更新UI。

    	private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //... 更新UI
            }
        };
    

    在你创建这个mHandler 实例的时候,底层做了以下几件事情:

    • 1、拿到mHandler所在线程的Looper,当前mHandler是在Activity中创建的,很明显,当前的线程就是主线程,所以 mHandler的成员变量mLooper = Looper.myLooper(),此处就已经将当前的主线程Looper赋值过去了。
    • 2、紧接着,判断mLooper 是否为空,明显不为空,所以又会将主线程的消息队列赋值给mQueue。告诉Handler,你要是有消息,就送到这个消息队列中来,我(Looper)会一个个按顺序处理,处理完后我就会告诉你,你再处理。
    由此我们可以得出结论:
    1、Handler有且只能绑定一个线程的Looper;
    2、Handler的消息是发送给Looper的消息队列MessageQueue,需要等待处理;

    所以,如果你在子线程中声明了一个Handler,是不能直接更新UI的,需要调用Handler相关的构造方法,传入主线程的Looper,这样创建的Handler实例,你才能进行UI的更新操作。另外的,需要注意的是,子线程默认是没有开启专属的Looper,所以,在子线程中创建Handler之前,你必须先开启子线程的Looper,否则就会爆出异常,然后GG。从上面贴出的构造方法中的部分就可以知道:

    	if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
        }
    

    以上就是创建Handler的过程,有了Handler实例了,怎样传递消息呢?

    2、Handler sendMessage()相关的方法(API 28)

    首先上一幅图来表明sendXXXMessageXXX()的相互调用关系:

    可以看出,当我们调用Handler进行发送消息时,最终都会调用sendMessageAtTime()方法,最后调用enqueueMessage( ) 发送到消息队列。

    	public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;  //获得当前的消息队列
            if (queue == null) {   //若是在创建Handler时没有指定Looper,就不会有对应的消息队列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); 
        }
        
    	private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;   //这个target就是前面我们说到过的
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
    • msg.target = this
      在发送消息到消息队列之前,明确的指定了消息的target为当前的Handler,以便于在后面Looper分发消息时用到。
    • queue.enqueueMessage(msg, uptimeMillis)
      然后调用了消息队列的enqueueMessage()方法,并传递了两个参数,一个Message,一个是long型的时间。

    以上就是Handler的创建和发送消息的过程。

    3、Handler dispatchMessage()方法(API 28)

    前面说了消息的发送,交给Looper等待处理,处理完后会重新通知Handler处理,那么,是怎样通知Handler处理消息的呢?秘密就在dispatchMessage()这个方法中:

    	/**
         * 在这里处理系统消息
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
    	/**
         * 子类必须实现这个来接收消息
         */
        public void handleMessage(Message msg) {
        }
    

    当Looper处理完Message后,会使用到Message的target,即上面说到的target,即发送消息的那个Handler,Looper会调用Handler的dispatchMessage()方法分发消息,所以前面在enqueueMessage()发送消息的时候,为什么非得指明Message的target就是这个道理。

    回到dispatchMessage()这个方法:
    1、首先会判断Message的callback是否为空,此处的callback就是前面我们在Message中说到的,在静态方法创建Message时,可以指定的callback,若不为空,则将结果回调到callback中;
    2、若Handler的mCallback 不为空,也一样的道理。
    3、平时我们都没有传入这个callback,而是直接实现handleMessage()这个方法,在这个方法中处理更新UI任务。

    以上就是Handler发送和接收消息的基本过程:把消息发送到队列—>然后喝茶等待—>接收消息—>分发消息—>在回调中处理。

    三、MessageQueue

    前面我们知道,Handler发送消息会调用MessageQueue的enqueueMessage()方法,直接上源码(API 28):

    	boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {  //判断msg的所属Handler
                throw new IllegalArgumentException("Message must have a target.");
            }
            //......
            synchronized (this) {  //因为是队列,有先后之分,所以用了同步机制
                //......
                msg.when = when;
                Message p = mMessages;  //对列中排在最后的那个Message 
               //......
                if (p == null || when == 0 || when < p.when) {    
               	    //若队列为空,或者等待时间为0,或者比前面那位的等待时间要短,就插队
                    msg.next = p;  //此处的next就是前面我们在Message提到的,指向队列的下一个结点
                    mMessages = msg;
                    //......
                } else { 
                    //......
                    Message prev;
                    for (;;) {     
                        //此处for循环是为了取出一个空的或者when比当前Message长的一个消息,然后进行插入
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }
                        //......
                    }
                    msg.next = p;       // 置换插入
                    prev.next = msg;  // 置换插入
                }
    		    //......
            }
            return true;
        }
    

    以上就是消息队列插入消息的过程原理,通过单向链表的数据结构来存储消息。既然有了插入消息的方法供Handler插入消息,那么应该有对应的取出消息的方法,供Looper调用取出消息处理,它就是Message.next这个变量(结点),代码就不贴了,自行前往查看,过程还是挺简单的。

    四、Looper

    Looper在Handler机制中扮演着关键的一环,他是循环处理消息的发动机,永不停息(永动鸡),它不断的从消息队列中取出的消息,处理,然后分发处理事件。每个线程都可以且只能绑定一个Looper。主线程之所以能处理消息,也是因为在APP启动时,在ActivityThread中的main()方法中就已经启动了Looper循环。
    点击前往查看Looper在main()方法中的启动流程

    下面直接上Looper关键方法loop( )的源码(API 28)

    	public static void loop() {
            final Looper me = myLooper();   //获得当前的Looper
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            
            final MessageQueue queue = me.mQueue;  //获取当前Looper的消息队列
            //......
    
            for (;;) {
                Message msg = queue.next();  //取出队头的消息
                if (msg == null) {
                    // 如果消息为空,则跳过,继续执行下一个message
                    return;
                }
                //......
                try {
                    msg.target.dispatchMessage(msg);
                    //......
                } finally {
                   //......
                }
               //......
                msg.recycleUnchecked();  //回收可能正在使用的消息
            }
        }
    

    由此可见,Looper的处理消息的循环还是挺简单的,就是拿出消息,然后分发,然后回收 … …

    总结

    Handler机制可以简述为:
    Handler将Message发送到Looper的消息队列中,即MessageQueue,等待Looper的循环读取Message,处理Message,然后调用Message的target,即附属的Handler的dispatchMessage()方法,将该消息回调到handleMessage()方法中,然后完成更新UI操作。
    展开全文
  • Handler机制

    2017-12-15 22:54:40
    Handler机制
  • 为了避免ANR,我们通常会把一些耗时操作(比如:网络请求、I/O操作、复杂计算等)放到子线程中去执行,而当子线程需要修改UI时则子线程需要通知主线程去完成修改UI的操作,则此时就需要我们使用Handler机制来完成子...

    Handler是Android中提供的一种异步回调机制,也可以理解为线程间的消息机制。为了避免ANR,我们通常会把一些耗时操作(比如:网络请求、I/O操作、复杂计算等)放到子线程中去执行,而当子线程需要修改UI时则子线程需要通知主线程去完成修改UI的操作,则此时就需要我们使用Handler机制来完成子线程与主线程之间的通信。

    1. Handler的一般使用步骤
      在明确了Android中只有主线程能修改UI界面、子线程执行耗时操作的前提后,下面一起来学习下Handler的使用步骤。

    在主线程中创建Handler实例,并且重写handlerMessage方法。

     private Handler handler = new Handler(){
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case 1:
                //执行相关修改UI的操作
                 break;
            }
        }
     }; 
    

    2.子线程中获取Handler对象,在需要执行更新UI操作的地方使用handler发送消息

     Message msg = Message.obtain();
     msg.obj = "content";
     msg.what = 1;
     //发送消息给Handler
     handler.sendMessage(msg); 
    

    在handlerMessage方法中的Switch里面,根据case下不同的常量执行相关操作

    以上只是Handler的一种使用方式,由于本文的重点是探究Handlerde原理,故其他使用方式这里不重点介绍。

    1. 重要类的职责
      在深入了解Handler机制原理之前,我们应该明确在Handler机制中几个重要类的职责。

    Handler:负责发送处理消息
    MessageQueue:消息队列,负责存储消息
    Message: 具体发送的消息
    Looper: 负责循环取出消息给Handler处理
    ThreadLocal: 用于线程间的数据隔离,在每个线程中存放各自对应的Looper

    1. Handler机制原理

    每个Handler都会关联一个消息队列,消息队列又是封装在Looper对象中,而每个Looper又会关联一个线程。这样Handler、消息队列、线程三者就关联上了。

    Handler是一个消息处理器,将消息发送给消息队列,然后再由对应的线程从消息队列中逐个取出,并执行。

    默认情况下,消息队列只有一个,也就是主线程的消息队列,该消息队列通过Looper.prepareMainLooper()方法创建,最后执行Looper.loop()来循环启动消息。

    以上叙述可能有点空洞,下面我们结合源码一起来理解:

    3.1 Handler是如何关联消息队列和线程的?
    我们首先看Handler默认的构造函数:

     public Handler(Callback callback, boolean async) {
        //代码省略
        mLooper = Looper.myLooper();//获取Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
            mQueue = mLooper.mQueue;//通过Looper对象获取消息队列
            mCallback = callback;
            mAsynchronous = async;
        }
        
        //获取Looper对象
        public final Looper getLooper() {
            return mLooper;
         } 
    

    从Handler的构造函数中我们可以发现,Handler在初始化的同时会通过Looper.getLooper()获取一个Looper对象,并与Looper进行关联,然后通过Looper对象获取消息队列。那我们继续深入到Looper源码中去看Looper.getLooper()是如何实现的。

     //初始化当前线程Looper
        public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
            }
         }
    
        //为当前线程设置一个Looper
        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));
         } 
    

    从上面的程序可以看出通过prepareMainLooper(),然后调用 prepare(boolean quitAllowed)方法创建了一个Looper对象,并通过sThreadLocal.set(new Looper(quitAllowed))方法将该对象设置给了sThreadLocal。

     //通过ThreadLocal获取Looper
        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
         } 
    

    通过Looper中的预备工作,sThreadLocal中已经存储了一个Looper对象,然后myLooper()方法通过sThreadLocal.get()方法获取到了Looper。那么消息队列就与线程关联上了,所以各个线程只能访问自己的消息队列。

    综上所述,我们可以发现消息队列通过Looper与线程关联上了,而Looper又与Handler是关联的,所以Handler就跟线程、线程的消息队列关联上了。

    3.2 如何执行消息循环?
    在创建Looper对象后,通过Handler发来的消息放在消息队列中后是如何被处理的呢?这就涉及到了消息循环,消息循环是通过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;//获取消息队列
        //代码省略
        for (;;) {//死循环
            Message msg = queue.next(); // 获取消息
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //代码省略
            try {
                msg.target.dispatchMessage(msg);//分发消息
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //代码省略
            msg.recycleUnchecked();//消息回收
             }
        } 
    

    从源码中我们可以看出,loop()方法实质上就是通过一个死循环不断的从消息队列中获取消息,然后又不断的处理消息的过程。

    3.3 消息处理机制

    msg.target.dispatchMessage(msg);//分发消息

    我们从loop中的 dispatchMessage()方法入手,看看谁是该方法的调用者,深入Message源码中看看target的具体类型:

     public final class Message implements Parcelable {
            //代码省略
    
            /*package*/ int flags;
            /*package*/ long when;
            /*package*/ Bundle data;
            /*package*/ Handler target;
            /*package*/ Runnable callback;
            /*package*/ Message next;
    
            //代码省略
        } 
    

    从源码中我们可以看到其实target就是Handler类型。所以Handler是将消息发送到消息队列暂时存储下,然后又将消息发送给Handler自身去处理。那我们继续到Handler源码中去看看Handler是如何处理消息的:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    } 
    
    private static void handleCallback(Message message) {
        message.callback.run();
    } 
    
    /**
     * Subclasses must implement this to receive messages.
     * 消息处理方法为一个空方法,由子类去实现
     */
    public void handleMessage(Message msg) {
    } 
    

    从上面的源码中可以看出,dispatchMessage(Message msg)只负责分发Message。从Message源码中我么可以知道callback为Runnable类型,如果callback不为空,则执行 handleCallback方法来处理,而该方法又会调用callback.run();如果如果callback为空,则调用handleMessage来处理消息,而该方法又为空,所以我们会在子类中重写该方法,并将修改UI的代码写在里面。之所以会出现这两种情况,是因为Handler发送消息有两种形式:

    在本文的一般使用步骤中,我使用的是sendMessage(msg)发送消息,此时callback就为空。
    当使用post发送消息时,callback就不为空。
    以上就是Handler机制的原理,大致可以总结为:在子线程中Handler将消息发送到MessageQueue中,然后Looper不断的从MessageQueue中读取消息,并调用Handler的dispatchMessage发送消息,最后再Handler来处理消息。为了更好的帮助大家一起理解,我画了一个Handler机制的原理图:

    73a9f2d42040a363d62cf1b572cfcc6a_看图王.png

    关于Handler机制补充如下几点:

    Handler创建消息时用到了消息池,在创建消息时会先从消息池中去查询是否有消息对象,如果有,则直接使用消息池中的对象,如果没有,则创建一个新的消息对象。
    使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。

    最后:后续会持续更新哦,喜欢的话就点赞关注一下吧。

    相关视频
    【Android源码解析】Handler源码解析及大厂面试重点问题

    析】Handler源码解析及大厂面试重点问题](https://link.juejin.cn/?target=https%3A%2F%2Fwww.bilibili.com%2Fvideo%2FBV1t34y1X7En%3Fspm_id_from%3D333.999.0.0 “https://www.bilibili.com/video/BV1t34y1X7En?spm_id_from=333.999.0.0”)

    最后

    按照国际惯例,给大家分享一套十分好用的Android进阶资料:《全网最全Android开发笔记》。

    整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。

    图片

    图片

    因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。

    如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。

    相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。

    (一)架构师必备Java基础

    1、深入理解Java泛型

    2、注解深入浅出

    3、并发编程

    4、数据传输与序列化

    5、Java虚拟机原理

    6、高效IO

    ……

    图片

    (二)设计思想解读开源框架

    1、热修复设计

    2、插件化框架设计

    3、组件化框架设计

    4、图片加载框架

    5、网络访问框架设计

    6、RXJava响应式编程框架设计

    ……

    图片

    (三)360°全方位性能优化

    1、设计思想与代码质量优化

    2、程序性能优化

    • 启动速度与执行效率优化
    • 布局检测与优化
    • 内存优化
    • 耗电优化
    • 网络传输与数据储存优化
    • APK大小优化

    3、开发效率优化

    • 分布式版本控制系统Git
    • 自动化构建系统Gradle

    ……

    图片

    (四)Android框架体系架构

    1、高级UI晋升

    2、Android内核组件

    3、大型项目必备IPC

    4、数据持久与序列化

    5、Framework内核解析

    ……

    图片

    (五)NDK模块开发

    1、NDK开发之C/C++入门

    2、JNI模块开发

    3、Linux编程

    4、底层图片处理

    5、音视频开发

    6、机器学习

    ……

    图片

    (六)Flutter学习进阶

    1、Flutter跨平台开发概述

    2、Windows中Flutter开发环境搭建

    3、编写你的第一个Flutter APP

    4、Flutter Dart语言系统入门

    ……

    图片

    (七)微信小程序开发

    1、小程序概述及入门

    2、小程序UI开发

    3、API操作

    4、购物商场项目实战

    ……

    图片

    (八)kotlin从入门到精通

    1、准备开始

    2、基础

    3、类和对象

    4、函数和lambda表达式

    5、其他

    ……

    图片

    好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码免费领取哈~

    图片

    展开全文
  • 先上图,让大家好理解下handler机制handler机制示例图上面一共出现了几种类,ActivityThread,Handler,MessageQueue,Looper,msg(Message),对这些类作简要介绍:ActivityThread:程序的启动入口,为什么要介绍...
  • handler在安卓开发中是必须掌握的技术,但是很多人都是停留在使用阶段。使用起来很简单,就两个...先上图,让大家好理解下handler机制handler机制示例图上面一共出现了几种类,ActivityThread,Handler,MessageQ...
  • 一文搞懂Handler机制原理

    千次阅读 2021-12-01 14:06:31
    一文搞懂Handler机制原理
  • Handler使用简单功能强大,常被用作线程间传递消息的组件,而且还可以用于跨进程。 消息机制背后有包括Looper ,MessageQueue管理和分发消息的实现,...这里从Java层开始深入探究下Handler和消息机制背后实现的原理
  • Handler机制原理

    千次阅读 2016-11-16 01:08:01
    Handler、Looper、Message是Android线程间通信的重要概念,我们在项目中会经常用到,最常用的写法,创建一个Handler对象,在线程中通过Handler发送消息来更新UI,这是很常用的写法,那么有时候我们把Handler的创建写...
  • 虽然网上已经有很多Handler相关的文章了,而且Handler机制的上层原理也并不难,还是决定写一下,因为我想构建自己的知识体系。也希望给看我博客的朋友们一个无缝衔接的阅读体验。 Handler机制涉及到的类主要有...
  • 1、handler的作用handler是android线程之间的消息机制,主要的作用是将一个任务切换到指定的线程中去执行,(准确的说是切换到构成handler的looper所在的线程中去出处理)android系统中的一个例子就是主线程中的所有...
  • 探索其背后的原理:子线程 handler 主线程 其实构成了线程模型中的经典问题 生产者消费者模型。 生产者消费者模型:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加数据,消费者从存储...
  • Android Handler消息机制原理最全解读(持续补充中)

    万次阅读 多人点赞 2018-05-13 19:22:57
    Handler  在Android开发的过程中,我们常常会将耗时的一些操作放在子线程(work thread)中去执行,然后将执行的结果告诉 UI线程(main thread),熟悉Android的朋友都知道,UI的更新只能通过Main thread来...
  • Handler机制原理探究

    千次阅读 2017-06-10 14:46:50
    结合代码和实验,探究Handler的实现原理
  • Handler机制原理解析(二)prepare,loop,post上一篇已经介绍了Handler机制的原理,如果不熟悉可以看Handler机制原理解析(一)。这一篇,介绍下Handler周边的知识点。我们已经知道,要获得looper对象,必须要先...
  • Android Handler 机制原理

    2017-10-30 11:36:44
    在下面介绍handler机制前,首先得了解以下几个概念: 1. Message 消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。 2. Message Queue 消息...
  • Handler的理解、用法以及运行机制原理

    万次阅读 多人点赞 2018-08-19 00:10:17
    Handler是android提供用于更新UI的一套机制,也是消息处理机制Handler的主要作用有两个: (1).在新启动的线程中发送消息 (2).在主线程中获取,处理消息。 解释:(1) 当应用程序启动时...
  • 安卓中handler机制原理

    2017-02-06 13:56:14
    Handler机制
  • android handler机制原理

    2016-04-01 11:13:58
    andriod提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。 1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里...
  • 有关Handler机制原理的总结

    千次阅读 2017-03-31 15:30:50
    Handler是线程与线程间进行通信的一套机制。  Handler是常被开发者拿来更新UI的一种消息处理机制,它的运行机制需要底层的Looper和MessageQueue的支撑。  一个Android应用程序被创建时就会创建一个进程,该...
  • Handler机制是用来进行线程间通讯的2、为什么?主要解决异步线程刷新UI界面3、怎么实现的?主线程创建时默认调用Loop.prepare()静态方法来建立一个消息队列和loop对象,新建handler对象时该对象与此loop和队列绑定,...
  • 创建流程: ...创建Handler的时候,我们会取出当前线程的Looper,不断的去轮询MessageQueue中的Message。 整体流程: Handler在子线程中发送的消息,实际上就是在MessageQueue中添加一条Message...
  • Handler的消息机制原理

    千次阅读 2016-05-27 21:51:37
    ARN异常  ANR 即ApplicationNot Response,表示应用无响应,这里是指主线程无响应(UI线程) 如果在主线程中进行了耗时操作,就会发生ARN异常 ...消息机制可以用来处理这种更新UI的情况,也就是说,用来处理UI在
  • Android异步通信:图文详解Handler工作原理

    万次阅读 多人点赞 2018-05-03 10:07:41
    今天,我将图文详解 Handler机制 的工作原理,希望你们会喜欢 目录 1. 定义 一套 Android 消息传递机制 2. 作用 在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 85,669
精华内容 34,267
关键字:

handler机制的原理