精华内容
下载资源
问答
  • Handler原理

    2021-01-30 17:48:10
    Handler原理前言Handler介绍1、实现handler2、实现Looper3、实现MessageQueue相关内容: 前言 这篇文章打算讲handler原理,我相信handler原理已经被很多大佬写透过的东西,但是我想从一个不同的角度来写,从实践来...

    前言

    这篇文章打算讲handler原理,我相信handler原理已经被很多大佬写透过的东西,但是我想从一个不同的角度来写,从实践来了解handler原理。希望这篇文章能带给你来点收获。

    Handler介绍

    handler是Android一套消息传递机制,像在Andorid里AsyncTask、IntentService、activity生命周期控制等…都存在handler的身影,handler机制中有四个很重要的对象:

    Handler 负责消息的发送和处理
    MessageQueue 消息队列(虽然听名字数据结构像是队列,但是实际上是单向链表)
    Message 传递的消息
    Looper 负责消息的轮询,并且将消息发送给持有该消息的handler(一个线程只能有一个looper)

    1、实现handler

    首先我们看下handler源码的构造函数

        public interface Callback {
            /**
             * @param msg A {@link android.os.Message Message} object
             * @return True if no further handling is desired
             */
            public boolean handleMessage(Message msg);
        }
    
    
        public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
        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 " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    callback参数:回调handlerMessag ,我们将在handler所在的线程收到Message looper参数:消息泵,handler将会与这个looper绑定,mQueue是一个Messagequeue,Messagequeue是looper的成员变量 ,可以看的出来如果没有looper将会报错 。 async:是否是异步消息

    这里我们模仿源码实现简易handler类

    public class Handler {
        private final MessageQueue mQueue;
        private Looper mLooper;
    
        public Handler(){
            Looper looper=Looper.myLooper();
            mLooper=looper;
            mQueue=looper.mQueue;
        }
        //发送消息
        public void sendMessage(Message message){
           message.target=this;
           mQueue.enqueueMessage(message);
        }
       //处理消息
        public void handleMessage(Message message) {
        }
    }
    

    2、实现Looper

    我们以Looper.myLooper()作为入口看下Looper的源码

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

    可以看的出如果要获得looper最终是要调prepare(boolean quitAllowed)这个函数,这里再次证明一个线程只能创建一个looper,这里就会有小伙伴问我在主线程明明没有调用Looper.prepare啊,其实在主线程ActivityThread已经自动帮我创建了,所以在其他线程需要Looper的话均需要手动调用Looper.prepare();

    那Looper又是怎么作为消息泵,并且将消息传递给handler呢?我们来看Looper.loop的源码(有剪切)

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

    可以看得出来Looper的消息轮询是一个死循环,且不断的调用 Message msg = queue.next() 从MessageQueue取消息,然后调用 msg.target.dispatchMessage(msg);这里的target即handler,这样将消息传递给handler了;

    这里我们模仿源码实现简易Looper

    public class Looper {
        //用于存放在该线程中的looper,确保一条线程只有一个looper,
        //当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
        //所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本
       private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
         MessageQueue mQueue;
    
         private Looper(){
             mQueue =new MessageQueue();
         }
        public static Looper myLooper(){
            return sThreadLocal.get();
        }
        public static void prepare(){
             if (null!=sThreadLocal.get()){
                 throw new RuntimeException("Only one Looper may be created per thread");
             }
             sThreadLocal.set(new Looper());
        }
    
        public static void loop(){
             Looper looper=myLooper();
             if (looper==null){
                 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
             }
             MessageQueue queue=looper.mQueue;
             for (;;){
                 Message next=queue.next();
                 if (next==null||next.target==null){
                     continue;
                 }
                 next.target.handleMessage(next);
             }
        }
        public void quit(){
             mQueue.quit();
        }
    }
    

    3、实现MessageQueue

    我就以上述queue.nxet()为切入点进入源码看一下

      Message next() {
            // Return here if the message loop has already quit and been disposed.
            // This can happen if the application tries to restart a looper after quit
            // which is not supported.
            final long ptr = mPtr;
            if (ptr == 0) {
                return null;
            }
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
                        // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
    
                    // If first time idle, then get the number of idlers to run.
                    // Idle handles only run if the queue is empty or if the first message
                    // in the queue (possibly a barrier) is due to be handled in the future.
                    if (pendingIdleHandlerCount < 0
                            && (mMessages == null || now < mMessages.when)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
    
                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }
    
                // Run the idle handlers.
                // We only ever reach this code block during the first iteration.
                for (int i = 0; i < pendingIdleHandlerCount; i++) {
                    final IdleHandler idler = mPendingIdleHandlers[i];
                    mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                    boolean keep = false;
                    try {
                        keep = idler.queueIdle();
                    } catch (Throwable t) {
                        Log.wtf(TAG, "IdleHandler threw exception", t);
                    }
    
                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
    
                // Reset the idle handler count to 0 so we do not run them again.
                pendingIdleHandlerCount = 0;
    
                // While calling an idle handler, a new message could have been delivered
                // so go back and look again for a pending message without waiting.
                nextPollTimeoutMillis = 0;
            }
    

    这里看似很繁琐,其实可以大致流程为 nativePollOnce(ptr, nextPollTimeoutMillis);执行Native的消息机制,该方法会一直等待Native消息,其中 timeOutMillis参数为超时等待时间。如果为-1,则表示无限等待,直到有事件发生为止。如果值为0,则无需等待立即返回,所以主线程一直轮询是不会一直消耗cpu性能的,也不会造成卡顿,因为一有消息就会被唤醒。如果想进一步了解native消息机制可以看深入理解Java Binder和MessageQueue 接下来就是看 if (msg != null && msg.target == null) 是否有屏障消息,如果有就过滤掉同步消息直接执行就近的异步消息,代码再往下看就是时间是否到了消息等待的时候,到了则返回,接着往下看,这里涉及到空闲任务,当没有消息的时候,会执行一些空闲任务,例如GC,空闲任务执行完后nextPollTimeoutMillis又会重新置为0。

    那MessageQueue里是怎么存消息的呢,当调用Handler发送消息的时候,不管是调用sendMessage,sendEmptyMessage,sendMessageDelayed还是其他发送一系列方法。最终都会调用 sendMessageAtTime(Message msg, long uptimeMillis),然而这个方法最后会调用enqueueMessage(Message msg, long when)。

        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);
        }
    
     boolean enqueueMessage(Message msg, long when) {
                ...省略
            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 {
                    // Inserted within the middle of the queue.  Usually we don't have to wake
                    // up the event queue unless there is a barrier at the head of the queue
                    // and the message is the earliest asynchronous message in the queue.
                    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;
        }
    

    消息队列里没有消息或者无需等待、小于消息头的等待时间的时候直接将消息放在头部,否则将消息插入合适的位置(比较等待时间),如果触发needWake那么就会直接唤醒native的消息,这里的mptr是NativeMessageQueue对象然后返回的地址。

    这里我们模仿实现简易MessageQueue

    public class MessageQueue {
        //消息链表
        Message mMessage;
        private boolean isQuit;
        public void enqueueMessage(Message message){
              synchronized (this){
                  if (isQuit){
                      return;
                  }
                  Message p=mMessage;
                  if (null==p){
                      mMessage=message;
                  }else {
                      Message prev;
                      for (;;){
                          prev =p;
                          p=p.next;
                          if (null==p){
                              break;
                          }
                      }
                      prev.next=message;
                  }
                  notify();//唤醒,模拟 nativeWake(mPtr);
              }
        }
        public Message next(){
            synchronized (this){
                Message message;
                for (;;){
                    if (isQuit){
                        return null;
                    }
                    message=mMessage;
                    if (null!=message){
                        break;
                    }try {
                        wait(); //等待唤醒 模拟  nativePollOnce(ptr, nextPollTimeoutMillis);
                    }catch (InterruptedException e  ){
                        e.printStackTrace();
                    }
                }
                mMessage=mMessage.next;
                return message;
            }
        }
        public void  quit(){
            synchronized (this){
                isQuit=true;
                Message message=this.mMessage;
                while (null!=message){
                    Message next=message.next;
                    message.recycle();
                    message=next;
                }
                notify();
            }
        }
    }
    

    4、实现Message
    Message部分源码

        /*package*/ Handler target;
    
        /*package*/ Runnable callback;
    
        // sometimes we store linked lists of these things
        /*package*/ Message next;
      /**
         * 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();
        }
    

    这里的target就是handler这样就可以同过message.target.handlMessage() 去让handler处理消息了,这里还有一个next,这样他在messageQueue里就可以产生一个消息的单向链表。这里还有一个变量池,有兴趣的小伙伴可以自行去了解下message的复用

    这里我们模仿实现简易Message

    public class Message {
        int what;
        Object object;
        Message next;
        Handler  target;
        public void  recycle(){
            object=null;
            next=null;
             target=null;
        }
    }
    

    这次我们分析和模拟handler机制算是完成了,让我们来看一下效果吧。

       @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Looper.prepare(); //手动调用我们自己prepare方法
            final Handler handler=new Handler(){
                @Override
                public void handleMessage(Message message) { //处理消息
                    Log.i("info1", "handleMessage: "+message.what);
                }
            };
            new Thread() {
                @Override
                public void run() {
                    Message message=new Message();
                    message.what=45;
                    handler.sendMessage(message); //子线程里发送消息
                }
            }.start();
            Looper.loop(); //开启轮询
        }
    
    2020-01-02 16:40:38.862 22050-22050/? I/info1: handleMessage: 45
    

    来源:掘金.

    相关内容:

    图解Handler机制.

    【Android源码】Handler 机制源码分析.

    Handler基础运用及源码分析.

    handler消息机制源码解析.

    Android 源码分析 —— Handler、Looper 和 MessageQueue.

    展开全文
  • Handler原理简说

    2021-03-04 14:44:14
    有关Handler原理的文章已经烂大街了,有很多文章写的特别棒,佩服。这篇文章其实是我想自己简短总结一下自己的理解,和我个人当初在学习时,对自己疑惑的地方的探究。 Handler是干什么的? 个人理解:它是一个用来...

           有关Handler原理的文章已经烂大街了,有很多文章写的特别棒。这篇文章其实是我想自己简短总结一下自己的理解,和我个人当初在学习时,比较疑惑的地方。

    Handler是干什么的?
           个人理解:它是一个用来发送消息,并且自己处理这个消息的一个通信工具。(自己发自己处理?对!)关键是它可以在不同线程发送或处理消息
     
    使用场景:
           主要用于线程间的通信。
     
    原理:
           handler发出一个消息,放入到一个容器,外部启动一个死循环,不停的判断容器中是否有消息,有就拿出来,还给handler处理。

     
    怎么就实现在线程间通信了?
           只说关键流程:
    1、在线程A中要预先创建一个Looper, 同时在Looper中会创建一个MessageQueue对象。

    2、在线程A中创建Handler对象并重写了handleMessage方法,等待消息到来处理消息。也就是处理消息的线程是线程A。在Handler构造方法中,会去拿到上面这个Looper对象,和Looper中的MessageQueue对象

    3、在线程B发送消息,Handler会把消息放入到MessageQueue中,但在这之前,把自己赋值给了这个Message的target变量。 也就是说,在消息中会有一个target变量就是当初发送自己的handler对象

    4、调用Looper中的loop()方法。方法开启了一个死循环,判断当前Looper中的MessageQueue中是否有消息Message,如果有就调用该消息的target变量(handler对象)的dispatchMessage()方法。

    5、处理消息,在Handler中的dispatchMessage()方法中分发消息,判断具体把消息交给handler的那个回调方法处理。

    最后结果就是,线程B中发出的消息,在线程A中得到处理。。。注意第4步流程,可以直接在第1步之后做,总之是要开启这个死循环。

           其他文章还介绍了ThreadLocal, 这里也说下,ThreadLocal可以创建一个只属于线程自己的变量,这个变量与其他线程是隔离的,不会被共享。线程A中创建的Looper对象就是放到了的ThreadLocal中。Handler构造中也是通过ThreadLocal去拿到的Looper对象。

    就不贴代码了吧。其他文章都有,这里就是自己做个小总结。

    当初疑惑的地方
    为什么这么设计?
            我不知道还有其他的办法,来实现类似的功能。在看一些文章和听一些人讲解后,应该只有这种方式可以满足此类场景的的需求。就是在一个线程中及时处理其他线程发过来的消息。必须要有一个循环来不停的检查是否有消息传递。还有就是考虑到要想一个Android程序保持运行状态,就必须有代码一直在执行,不能写个阻塞在程序里吧,还要不停的刷新UI。这个死循环的设计正好可以满足。(从ActivityThread中的main()来思考)

    Looper.loop()死循环,不阻塞线程吗?不消耗cpu资源吗?
            首先上面也提到了Looper.loop()在ActivityThread中调用是等于是保持了程序的运行状态,每一次循环等于一次完整的刷新逻辑。只要loop()中每次循环过程是流畅的,展现给我们看到的就是流畅的操作。只有你在loop()中的某次循环中写了一个死循环,导致loop()循环卡主了,我们才会看到应用卡了。
    有消息正常处理,正常消耗cpu资源,当没有消息时候,线程会释放CPU资源进入休眠状态。具体涉及到Linux epoll的阻塞 唤醒机制。到这里,我就停止了。

    同一个线程中创建多个Handler,消息不会乱吗?
            target! 每个消息都有保存了自己的Handler对象,分发消息时候当然知道是哪个handler去处理(步骤3)。那handler放在MessageQueue中可以吗?这样就不用每个消息都存个handler了。不可以!同一个线程中,多个Handler拿到的MessageQueue是同一个。一个线程可以创建多个Handler,但只能有一个Looper,一个Looper中只能有一个MessageQueue(查看Looper.prepare()源码)。也就是说,同一个线程中Handler共享同一个Looper和MessageQueue。

    处理消息有优先级吗?
            可以说有,也可以说没有,看你怎么理解优先级这个概念了。Message中是没有设置优先级这种字段的。但有一个setAsynchronous(boolean async)方法,可以把消息标识成异步消息。Message消息本质上没有优先级,但它有3种类型,1、同步消息(平常我们发的都是),2、异步消息,3、屏障消息(没有target)。发送了一个屏障消息之后,会先处理异步消息,阻塞住同步消息,直到屏障消息移除。这么来看也可说通过同步屏障来控制处理消息的优先级了。

    文章所描述的观点问性问题,是出于自己的认知,不保证正确。欢迎指正文章错误。

    展开全文
  • TypeHandlerRegistry是一个TypeHandler容器,而TypeHandler定义着Java类型与数据库类型之间的映射关系。 构造函数中自定义了一些映射关系,作为myabtis的默认配置。 除了默认的处理器外,mybatis允许用户自定义...

     

    是什么

    上一篇说到Mybatis会把所有的配置放到一个类中——Configuration

    在这个Configuration里,有一个对象叫做 TypeHandlerRegistry。

    TypeHandlerRegistry是一个TypeHandler容器,而TypeHandler定义着Java类型与数据库类型之间的映射关系。

    构造函数中自定义了一些映射关系,作为myabtis的默认配置。

    除了默认的处理器外,mybatis允许用户自定义处理器,满足一些特殊场合的引用。

    用户需要继承接口TypeHandler

    public interface TypeHandler<T> {
    
      //设置参数
      void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
    
      //取得结果,供普通select用
      T getResult(ResultSet rs, String columnName) throws SQLException;
    
      //取得结果,供普通select用
      T getResult(ResultSet rs, int columnIndex) throws SQLException;
    
      //取得结果,供SP用
      T getResult(CallableStatement cs, int columnIndex) throws SQLException;
    
    }

     其实从接口中就可以估计出哪些场合用到这个处理器了。

    1、JDBC预编译设置参数时(PreparedStatement)

     2、从查询结果集中获取结果时

    也只有这两种情况涉及Java类型与数据库之间的转换。

    加载过程

    这一次debug源码的主要目的是:

    理解TypeHandler的加载过程。

    首先自定义一个TypeHandler

    @MappedTypes({String.class})
    @MappedJdbcTypes(JdbcType.VARCHAR)
    public class MyStringHandler implements TypeHandler<String> {
        private Logger log=Logger.getLogger(MyStringHandler.class);
    
        @Override
        public String getResult(ResultSet rs, String colName) throws SQLException {
            log.info("使用我的TypeHandler,ResultSet列名获取字符串");
            return rs.getString(colName);
        }
    
        @Override
        public String getResult(ResultSet rs, int index) throws SQLException {
            log.info("使用我的TypeHandler,ResultSet下标获取字符串");
            return rs.getString(index);
        }
    
        @Override
        public String getResult(CallableStatement cs, int index) throws SQLException {
            log.info("使用我的TypeHandler,CallableStatement下标获取字符串");
            return cs.getString(index);
        }
    
        @Override
        public void setParameter(PreparedStatement ps, int index, String value, JdbcType arg3) throws SQLException {
            log.info("使用我的TypeHandler");
            ps.setString(index, value);
        }
    
    }

    配置文件中定义映射关系

    <typeHandlers>
            <typeHandler jdbcType="VARCHAR" javaType="string" handler="org.apache.ibatis.customer.handler.MyStringHandler"/>
    </typeHandlers>

    从SqlSessionFactory构建之初开始

     第一步:解析xml配置文件

    可以看到在XMLConifgBuilder这个类中,解析到了类型处理器:

     解析到了我配置的映射关系

    第二步:加载字节码对象

     通过handlerTypeName以及resolveClass方法来加载字节码类,最后到了resolveAlias方法这里

     真正加载类的地方,实在ClassLoaderWrapper这个地方。

     从上图可以看出,实际上用的是AppClassLoader加载器。

     Class类加载完时候,注册到了typehandlerRegistry中,完成了加载过程。

     

    调用时机

    上面debug的是加载流程,

    现在我们再来debug一下TypeHandler究竟是在哪些场合被调用的:

    以如下sql做示例:

    public interface RoleMapper {
        public Role findRole(String roleName);
    }

    配置

        <select id="findRole" parameterType="long" resultMap="roleMap">
            select
            id,role_name,note from role where role_name like CONCAT('%',#{roleName
            javaType=string,
            jdbcType=VARCHAR,typeHandler=org.apache.ibatis.customer.handler.MyStringHandler},'%')
        </select>

    入口与上述加载过程一致,不赘述。

    断点打在DefaultSqlSession中的selectList中

     调用代理对象的查询

     缓存查不到时,会选择直接查询数据库

     SimpleExecutor中的doQuery调用prepareStatement准备sql语句,而这个方法内有一个参数处理的函数。

     跳过几个函数后,到达了DefaultParameterHandler这里

     ParameterMapping是对参数映射的一个封装,从中获取到了TypeHandler,并调用我们自定义的处理器设置参数。

    获取结果集的时候也用到了TypeHandler,代码位于DefaultResultSetHandler的getPropertyMappingValue中,debug过程由于篇幅原因就补贴出来了。

    项目应用

    举一个实际应用的例子

    对于枚举类型的映射,mybatis默认有两种处理器:EnumOrdinalTypeHandler和EnumTypeHandler,分别是根据枚举的Ordinal以及name做映射。

    但是实际项目中,我们可能会用枚举定义一下状态码,状态码通常比较灵活,并不会直接用枚举的Ordinal,这个时候如果也想直接与数据库做映射的话,需要自己写处理器了。

    例如角色表新增一个int类型(也可以是tinyint)的字段status用于表示状态,100状态正常,200表示该角色已被禁用。

    定义枚举类

    public enum RoleStatusEnum {
        NORMAL(100, "正常使用"),
        STOP(200, "禁用"),
        ;
        private final Integer code;
        private final String message;
    
    
        RoleStatusEnum(Integer code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    
    }

    Role

    package org.apache.ibatis.customer.entity;
    
    import org.apache.ibatis.customer.enums.RoleStatusEnum;
    
    public class Role {
        private long id;
        private String roleName;
        private String note;
        private RoleStatusEnum roleStatusEnum;
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getRoleName() {
            return roleName;
        }
    
        public void setRoleName(String roleName) {
            this.roleName = roleName;
        }
    
        public String getNote() {
            return note;
        }
    
        public void setNote(String note) {
            this.note = note;
        }
    
        public RoleStatusEnum getRoleStatusEnum() {
            return roleStatusEnum;
        }
    
        public void setRoleStatusEnum(RoleStatusEnum roleStatusEnum) {
            this.roleStatusEnum = roleStatusEnum;
        }
    
        @Override
        public String toString() {
            return "Role{" +
                    "id=" + id +
                    ", roleName='" + roleName + '\'' +
                    ", note='" + note + '\'' +
                    ", roleStatusEnum=" + roleStatusEnum +
                    '}';
        }
    }
    

    自定义处理器

    public class RoleStatusEnumHandler implements TypeHandler<RoleStatusEnum> {
        @Override
        public void setParameter(PreparedStatement ps, int i, RoleStatusEnum parameter, JdbcType jdbcType) throws SQLException {
            ps.setInt(i, parameter.getCode());
        }
    
        @Override
        public RoleStatusEnum getResult(ResultSet rs, String columnName) throws SQLException {
            //获取code
            int code = rs.getInt(columnName);
    
            return getByCode(code);
        }
    
        @Override
        public RoleStatusEnum getResult(ResultSet rs, int columnIndex) throws SQLException {
            int code = rs.getInt(columnIndex);
            return getByCode(code);
        }
    
        @Override
        public RoleStatusEnum getResult(CallableStatement cs, int columnIndex) throws SQLException {
            int code = cs.getInt(columnIndex);
            return getByCode(code);
        }
    
        private RoleStatusEnum getByCode(int code) {
            Class<RoleStatusEnum> roleStatusEnumClass = RoleStatusEnum.class;
            for (RoleStatusEnum enumConstant : roleStatusEnumClass.getEnumConstants()) {
                if( code == enumConstant.getCode()) {
                    return enumConstant;
                }
            }
            return null;
        }
    }
    

    配置文件中注册Handler

        <typeHandlers>
            <typeHandler jdbcType="INTEGER" javaType="org.apache.ibatis.customer.enums.RoleStatusEnum" handler="org.apache.ibatis.customer.handler.RoleStatusEnumHandler"/>
            <typeHandler jdbcType="VARCHAR" javaType="string" handler="org.apache.ibatis.customer.handler.MyStringHandler"/>
        </typeHandlers>

    Mapper配置文件中配置结果集映射

    sql语句

    <select id="findRole" parameterType="long" resultMap="roleMap">
            select
            id,role_name,note,status from role where role_name like CONCAT('%',#{roleName,
            javaType=string,
            jdbcType=VARCHAR,typeHandler=org.apache.ibatis.customer.handler.MyStringHandler},'%')
        </select>

     最后执行一个查询语句,并打印结果如下

     可以看到,mybatis把status封装成了我们定义的枚举对象。

    参考

    上述dubug的代码来自于注释版mybatis:https://github.com/tuguangquan/mybatis

    如有错误,欢迎批评指正!

    展开全文
  • 一文学透Handler原理

    2021-09-20 22:39:49
    本文带来的分享是对Handler原理的全面解析,相信对日常的开发和面试会有很大的帮助,下面我们一起看看它的原理。 正文 在讲解Handler原理之前,我们先通过一张图片看看它的整个流程: 当然,我们接下来所讲的...

    前言

    Handler可以说是老生常谈了,因为它在整个Android的系统中是非常重要的一部分,学习它的原理是很有必要的。本文带来的分享是对Handler原理的全面解析,相信对日常的开发和面试会有很大的帮助,下面我们一起看看它的原理。

    正文

    在讲解Handler原理之前,我们先通过一张图片看看它的整个流程:
    在这里插入图片描述
    当然,我们接下来所讲的Handler原理,不仅仅是Handler本身,还包括MessageQueue、Looper等类。

    Handler的作用

    在日常开发中,我们很多时候需要切换线程去做某件事情,比如在工作线程做好计算后,需要切换到主线程去更新ui,那么这个时候便可以使用Handler来进行线程的切换,又比如我们想做一个延迟任务,也可以使用Handler去开启一个延迟任务等等。

    Handler

    创建:

    Handler的构造函数最后会调用到下面的构造函数:

    public Handler(@Nullable Callback callback, boolean async) {
    
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    构造函数初始化Looper成员,会对Looper进行检查,如果之前还没初始化,则会抛异常,所以创建Handler之前,需要确保当前线程的Looper已经创建了,Looper创建后会与当前线程绑定存放起来,至于它得怎么创建,我们后面会讲到。

    但是我们在主线程中,则无需去创建Looper,因为在ActivityThread的main方法里边,系统已经帮我们创建好了:

    //只保留核心代码
    public static void main(String[] args) {
        Looper.prepareMainLooper();
    
        Looper.loop();
    
    }
    

    Looper.prepareMainLooper()里边会去创建一个Looper,并且这个Looper是在主线程中。

    发送消息:

    发送消息之前,需要创建Message,Message可以说是整个流程的载体,它承载着一些信息。它的创建方式如下:

    new Message()
    Message.obtain()
    handler.obtainMessage()
    //直接new跟obtain有什么区别呢?obtain的话,会优先到池子里拿,池子没有到话再创建一个,所以我们一般优先考虑obtain的方式去获取Message。
    
    //发送消息
    handler.sendMessage(msg);
    
    
    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        // 省略校验
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    

    发送消息的最后都会调用到sendMessageAtTime方法,它里边调用到是enqueueMessage方法:

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

    主要做了三件事:

    • 将msg的target设置为this,在消息分发时会用到,后面内容分析。
    • 如果当前Handler支持异步,那么会将msg设置为异步消息,用于异步屏障使用,后面分析
    • 调用MessageQueue的enqueueMessage方法。

    下面我们一起看看MessageQueue

    处理消息:

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

    从上面的代码我们可以看到分发的顺序是:

    • 如果Message有设置callback,则使用messaeg的callback进行回调,其实我们平时使用handler.post()/postDelay(),都会使用将Runnable赋值给Message的callback。
    • 如果Handler有设置callback,则会先回调Handler的callback。
    • 最后是Handler的handleMessage,这也是我们常见的一个方法,初始化Handler的时候去复写这个方法,并在里边进行消息的处理。

    MessageQueue

    内部时一个链表结构,用于存放Message和提供Message

    插入消息:

    上面讲到的enqueueMessage方法,就是用来存放Message的:

    //已精简了部分代码
    boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
            //省略部分代码
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
          //根据时间比较
            if (p == null || when == 0 || when < p.when) {
                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;
            }
            if (needWake) {
              //唤醒阻塞。(next方法里)
                nativeWake(mPtr);
            }
        }
        return true;
    }
    
    • 根据消息的时间寻找合适地方进行插入
    • 插入后尝试唤醒操作,因为在下边的取消息时可能会进入阻塞,调用唤醒的前提是这个消息不是同步屏障。

    既然我们MessageQueue提供了存消息的方法,同时也提供了取消息的方法,具体如下:

    取出消息:

    Message next() {
       //...
        int nextPollTimeoutMillis = 0;
        for (;;) {
            //...
          //尝试阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                  //同步屏障,拿到异步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                      //还没到分发的时间,计算需要阻塞的时长
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //可以分发,移除
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 没有消息,需要进入阻塞
                    nextPollTimeoutMillis = -1;
                }
        }
    }
    

    next方法会进入一个循环,然后分下列几步走:

    • 如果没有Message的话,会进入阻塞。如果拿到Message,会先判断是否为同步屏障(msg.target == null),如果是的话,则会去取后面的异步Message。同步屏障后面会进行分析。
    • 拿到Message后,先判断当前消息是否已经到达了分发的时间,则将它从链表中移除并且返回,否则的话会计算分发当前消息需要等多久,然后进行阻塞,阻塞时长为计算出来的时长。
    • 如果没有Message,则会进入阻塞,阻塞没有时长,会一直阻塞。等到有新的消息插入的话,才会通知打断阻塞。

    上面便是Message的两个核心方法存和取,那么又是哪个角色会从MessageQueue中进行去消息呢?下面介绍重要的类:Looper。它将负责取消息并进行分发。

    Looper

    创建:

    Looper的创建需要调用Looper.prepare()方法,最后会调用prepare(quitAllowed)方法:

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

    创建后,便放到了ThreadLocal里边,ThreadLocal可以保证将Looper与线程绑定起来。在创建之前,还会进行校验是否本线程是否已经创建过了。在这里我们顺便看一看Looper的构造函数:

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

    它的构造函数是私有的,也就是说外面无法直接new出来,并且构造函数里边会初始化MessageQueue。

    启动:

    上面我们讲过handler发送消息,然后插入到了MessageQueue,那么什么时候才会去处理放到队列里边的消息呢,这时Looper的一个工作,创建后,调用Looper.loop()方法,就会进入一个无限循环,不断去尝试获取Message:

    //精简了部分代码
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
    
        me.mInLoop = true;
        final MessageQueue queue = me.mQueue;
    
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
          
            try {
                msg.target.dispatchMessage(msg);
            } catch (Exception exception) {
                throw exception;
            }
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            msg.recycleUnchecked();
        }
    }
    

    loop方法主要做三件事:

    • 开启一起死循环
    • 不断地从MessageQueue里边拿Message
    • 拿到Message后,通过msg.target进行分发,其实target就是Handler,所以这里就能够将消息分发到Handler

    上面我们提过,主线程中系统在一开始就启动Looper了,所以我们无需手动启动。

    源码里的logging可以通过Looper的setMessageLogging进行设置。在这里我们同时也可以监控每个任务的耗时,通过对比logging的println方法参数值“>>>”和“<<<”进行配对。

    回到Handler,看看它是如何进行消息的分发:

    上面便是Message发送、取出、分发的整个过程,下面我们继续讲讲两个知识点:同步屏障和IdleHandler。

    同步屏障

    除了普通的Message以外,还有另外两种特殊的Message:屏障消息与异步消息,屏障消息则是为异步消息服务的。下面对他们进行详细的讲解。

    同步屏障的作用:

    屏障消息与普通消息的区别是target为null,它的作用就像是开了一绿色通道,如果开启同步屏障,那么发送异步消息后,都会得到优先处理,之前发送到普通消息则不会得到处理,得等到同步屏障的移除。

    开启同步屏障:

    调用MessageQueue的postSyncBarrier方法,则可以发送一个同步屏障消息,并且会返回对于的token,当我们关闭同步屏障的时候,则使用这个token去关闭:

    @UnsupportedAppUsage
    @TestApi
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }
    
    private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;
    
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) {
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
    

    可以看到,开启同步屏障是往消息链表中插入一个target为null的Message,并且返回一个token,但是我们平时在开发中,没法直接去调用,如果要使用的话,则可以使用反射进行调用。

    移除同步屏障:

    @UnsupportedAppUsage
    @TestApi
    public void removeSyncBarrier(int token) {
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();
    
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }
    

    通过token,找到屏障消息,然后将其从队列中移除掉,并且看看是否在此屏障消息后面还有消息,有的话需要唤醒操作。

    那么屏障消息在哪里起作用呢?其实我们在前面的MessageQueue的next方法已经提过了:

    if (msg != null && msg.target == null) {
         //同步屏障,拿到异步消息
         do {
              prevMsg = msg;
              msg = msg.next;
          } while (msg != null && !msg.isAsynchronous());
    }
    

    如果遇到同步屏障消息,则会去获取异步消息,然后进行分发处理。

    哪里有用到呢?

    在ViewRootImpl中,scheduleTraversals方法。

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
          //里边发送的是一条异步消息
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }
    

    也就是每次去调度刷新ui的时候,便会开启同步屏障,这也可以理解,因为是ui的刷新,所以优先级是比较高的。

    IdleHandler

    IdleHandler可以用在哪里呢?其实看它名字大概可以猜出它是用来干嘛的,主要是用来在空闲(当前没有Message分发)的时候进行调用。也就是我们可以使用IdleHandler来执行一些不重要或者优先级比较低的任务,让当前消息队列空闲时再去执行,避免影响其他重要的任务执行。

    public static interface IdleHandler {
        boolean queueIdle();
    }
    

    使用idleHandler可以如下:

    looper.getQueue().addIdleHandler(new IdleHandler() {
                public boolean queueIdle() {
                    //做一些事
                    return false;
                }
    });
    

    其中queueIdle返回的值表示是否继续保留着,等到下次空闲的时候再次调用。返回false表示用完即移除。

    idleHandler在什么时候调用呢?

    Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        for (;;) {
          
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
    
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            pendingIdleHandlerCount = 0;
        }
    }
    

    也就是在MessageQueue的next方法中,如果当前没有需要分发的Message,那么就会从mIdleHandlers拿出IdleHandler进行调用,调用后根据返回的结果来判断是否从列表里边移除掉。

    结语

    以上便是Handler的原理的讲解,内容较多,算是对Handler原理一个比较全面的解析,如果掌握了以上的分析,那么可以说基本对Handler原理了解了至少95%以上。

    展开全文
  • Handler原理解析

    2021-03-19 12:11:24
    Handler原理解析 学习内容: 名称 作用 Message 消息的载体,携带具体数据 MessageQueue 一系列Message的容器 Looper 内部对MessageQueue进行无限循环,有消息就取出交给Handler处理,没消息就阻塞 ...
  • Handler原理和源码

    2020-12-26 22:11:44
    handler原理,超级详细主要对象和它们的大致作用message跨线程旅游路线图 主要对象和它们的大致作用 Handler 发送和接受message的对象 Message 消息的实体对象 Looper 一直循环取消息,然后发送给handler Message...
  • Handler原理剖析,你不知道的事 本篇文章将会对Handler进行深层次的剖析,结合剖析图、剖析源码以及10个常见面试问题,希望看完文章的同学都能有所收获 一、Handler运行原理剖析 1.运行原理-剖析图 2.运行原理-剖析...
  • 之前听了一节享学课堂关于Handler原理介绍的课程,里面老师给了这么一张图,我一直以来觉得这张图很好理解。 先看右上角,handler发送消息后,消息队列就像有传送带一样一个一个接收并处理这些发来的消息,而让这...
  • Handler原理浅析

    2021-08-30 23:23:29
    Handler Handler定义 Handler本质上是线程间的一个消息传递和处理的机制 Handler通信实现的方案本质上是内存共享的方案 同一个进程内存是共享的 Handler是整个app通信的框架,在ActivityThread里面感受到,...
  • 本文实例讲述了Android编程中Handler用法。分享给大家供大家参考,具体如下:在Android的UI开发中,我们经常会使用Handler来控制主UI程序的界面变化。有关Handler的作用,我们总结为:与其他线程协同工作,接收其他...
  • android源码--Handler原理

    2021-07-25 08:09:12
    这里写自定义目录标题一、handler原理整体概述二、源码解析1、新建Handler源码分析2、ThreadLocal分析3、子线程中创建Handler handler基本原理基本都理解,但是有些细节时间一久仍会有所遗忘,做些整理也还是有必要...
  • Android中Handler原理

    2021-06-05 05:03:18
    Handler主要是主线程和子线程通信。一般子线程中做一些耗时操作做完之后通知主线程来修改UI。实际上android系统在Activity启动或者状态变化等都是通过Handler机制实现的。首先进入到ActivityThread的main方法中...
  • Handler原理源码分析

    2021-01-31 21:42:08
    Handler是android子线程和主线程之间通信的一种机制。 主要包含四个类handler looper messagequeue message handler作用? 用来发送和处理消息的。 一个线程中多个handler发消息处理消息为什么不混乱?...
  • Android Handler 原理分析Handler一个让无数android开发者头疼的东西,希望我今天这边文章能为您彻底根治这个问题今天就为大家详细剖析下Handler原理Handler使用的原因1.多线程更新Ui会导致UI界面错乱2.如果加锁会...
  • 一,TimerActivity代码 ...import android.os.Handler; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.Nullable; import andr
  • } } 然后在 mybatis xml 文件对应即可 <resultMap id="BaseResultMap" type="xxx"> <result column="bid_price" property="bidPrice" typeHandler="org.demo.comm.typehandler.ArrayTypeHandler"/> ...
  • Handler基本原理

    2021-01-31 22:23:51
    Handler基本原理 Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息...
  • Handler之前,先把和Handler有关的几个组件也说一下 Message:handler发送和处理的消息 Looper :每个线程只有一个looper,是个死循环,loop方法负责从MessageQueue 中拿取数据,然后把数据交给发送消息的...
  • Android handler原理详解

    2021-07-28 16:11:32
    因为这篇是写在onenote里面的,不知道怎么导出来,就用pdf转图片上传了。
  • 一 概述Handler主要被用来在子线程中访问UI线程,在ViewRootImpl中有一个checkThread()方法,对UI的操作都会有此验证。所以操作UI只能在主线程中进行。概念:Handler的运行由底层MessageQueue和Looper支撑。Message...
  • 本文主要说明android内部消息机制,用java模拟进行演示,并用图简单说明.并与生产者与消费者模型进行比对;需要解决的问题:1,主线程怎样跟子线程进行通信,...4,主线程如何与handler/message/looper...进行结合的?建议带...
  • 1、handler的作用handler是android线程之间的消息机制,主要的作用是将一个任务切换到指定的线程中去执行,(准确的说是切换到构成handler的looper所在的线程中去出处理)android系统中的一个例子就是主线程中的所有...
  • handler是什么? Handler 是一个消息分发对象。handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制,我们可以发消息,也可以通过它处理消息。 我们为什么需要handler来处理这种异步线程间...
  • handler在安卓开发中是必须掌握的技术,但是很多人都是停留在使用阶段。使用起来很简单,就两个步骤,在主线程重写handler的handleMessage( )方法,在工作线程发送消息。但是,有没有人想过这种技术是怎么实现的呢?...
  • Handler handler是Android SDK提供给开发者方便进行异步消息处理的类 AsyncTask、retrofit用到Handler Handler的四大组件 Message 接收和处理消息的对象 Looper 线程持有,读取消息队列MessageQueue中的消息...
  • 本篇主要是Handler机制常见面试题的讲解,希望大家能在面试中避免入坑。那么,我们就开始吧!主线程为什么不用初始化Looper?答:因为应用在启动的过程中就已经初始化主线程Looper了。每个java应用程序都是有一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 161,268
精华内容 64,507
关键字:

handler原理