Handler相关面试题有哪些

本篇内容主要讲解“Handler相关面试题有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Handler相关面试题有哪些”吧!

创新互联成立以来不断整合自身及行业资源、不断突破观念以使企业策略得到完善和成熟,建立了一套“以技术为基点,以客户需求中心、市场为导向”的快速反应体系。对公司的主营项目,如中高端企业网站企划 / 设计、行业 / 企业门户设计推广、行业门户平台运营、app软件开发公司移动网站建设、微信网站制作、软件开发、遂宁托管服务器等实行标准化操作,让客户可以直观的预知到从创新互联可以获得的服务效果。

1.获取Message实例的方式有哪些?哪一种更好?

获取Message实例的方法主要有两种,一种是直接创建,Message msg = new Message。另一种是通过Message.obtain()或者Handler.obtatinMessage()来得到一个Message对象。更推荐使用后一种方式,这种方式得到的对象是从对象回收池中得到,复用已经处理完的Message对象,而不是重新生成一个新对象。Message.obtain()Handler.obtatinMessage()最终都是调用了Message类的obtain()方法,查看方法内容,就知道是从对象回收池里得到Message。

  public static Message obtain() {
          synchronized (sPoolSync) {
              if (sPool != null) {
                  Message m = sPool;
                  sPool = m.next;
                  m.next = null;
                  m.flags = 0; // clear in-use flag
                  sPoolSize--;
                  return m;
              }
          }
          return new Message();
    }

2.当Activity有多个Handler的时候,Message消息是否会混乱?怎么样区分当前消息由哪个Handler处理?

不会混乱,哪个Handler发送的消息,到时候也是这个handler处理。在发送消息的时候,会绑定target,这个target就是Handler本身,当需要handler调用dispatchMessage(msg)处理消息的时候,这个Handler就是发送消息时绑定的handler。
无论用哪一种方法发送消息,最终都会调用enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)来发送消息

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

这里的this,就是当前的handler。在来看需要Handler处理消息的时候,取的是哪一个handler,下面贴出主要源码。

 public static void loop() {
  ......
        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
         ......
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
           ......
            msg.recycleUnchecked();
        }
    }

这是循环消息时的部分代码,处理消息代码是msg.target.dispatchMessage(msg);,这里的target就是当时发送消息的handler。

3.在子线程发送消息,却能够在主线程接收消息,主线程和子线程是怎么样切换的?

子线程用handler发送消息,发送的消息被送到与主线程相关联的MessageQueue,也是主线程相关联的Looper在循环消息,handler所关联的是主线程的Looper和MessageQueue,所以最后消息的处理逻辑也是在主线程。只有发送消息是在子线程,其它都是在主线程,Handler与哪个线程的Looper相关联,消息处理逻辑就在与之相关的线程中执行,相应的消息的走向也就在相关联的MessageQueue中。所以子线程切换到主线程是很自然的过程,并没有想象中的复杂。

4.能不能在子线程中创建Handler?

可以,但是在创建前先调用prepare()方法创建Looper。Handler创建的时候,会去检查是否有创建Looper,如果没有创建就会抛出异常。相关源码如下:

 public Handler(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;
    }

所以我们在子线程需要先调用prepare()方法创建Looper。这里还多提一点,在主线程创建就不需要自己创建Looper,因为在ActivityTread类里面,已经为我们创建好了,相关源码如下:

 public static void main(String[] args) {
     ......
        Looper.prepareMainLooper();// 为主线程创建looper
        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Looper.prepareMainLooper();这句代码就是为主线程创建Looper。 所以在主线程中直接创建一个Handler,就直接可以循环消息,因为安卓的主线程已经为我们准备好了Looper。

5.一个线程可以有几个Handler?几个Looper?

一个线程可以有多个Handler,但是只有一个Looper。创建Handler之前,需要创建Looper,否则会报错。源码里面已经做了说明。

 public Handler(Callback callback, boolean async) {
   ......
        mLooper = Looper.myLooper();
        if (mLooper == null) {//判断Looper是否被创建
            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的创建,是在prepare()方法里。

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

在创建之前去判断looper是否存在,存在就会抛出Only one Looper may be created per thread异常,这是在告诉我们一个线程只能有一个Looper。而TreadLocal的作用就是线程间隔离,确保一个线程对应一个Looper。还可以看看Looperg构造方法的源码

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

MessageQueue的创始化是在Looper的构造方法里。不管一个线程有多少个Handler,相关联的都是同一个Looper和MessageQueue。

关于Handler,可以问的问题有很多,以上只是抽出一些我认为比较重要的问题。在寻找答案以后,我将Handler机制的整个过程在脑海中过了一遍,并且画了个草图。

Handler机制中重要类的相互关联图

Handler相关面试题有哪些

Handler机制原理涉及几个重要的类:Handler、Message、MessageQueue、Looper。
就用子线程向主线程发送消息来说明整个过程。
首先在主线程创建一个Handler,在Handler类里面会创建Looper以及MessageQueue的对象,并且在Handler构造方法里面赋值

 final Looper mLooper;
 final MessageQueue mQueue;
 public Handler(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;
    }

mLooper = Looper.myLooper();得到的Looper是主线程的Looper
mQueue = mLooper.mQueue;得到的MessageQueue就是在Looper构造方法里面创建的MessageQueue。
创建好了Handler实例,我们就会在子线程调用handler.sendMessage(msg);发送消息,将message放到MessageQueue里面。在enqueueMessage()里面就给每个message设置target,这个target就是当前的handler。

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

再然后调用Looper.loop();(在ActivityThread的main()里面已经帮我们写好)开始循环消息,拿到消息以后就会用handler取出消息进行处理,重点代码是msg.target.dispatchMessage(msg);,这里的handler就和一开始我们为message设置的Handler对应。

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

最后我们在Handler的handleMessage(msg)里面去处理我们的消息。
这就是子线程给主线程发送消息的整个过程,源码很多,我只是截取了部分重点。这里多提一点就是线程处理消息。 在线程中处理消息需要做三件事情

1. 先创建一个Looper (Looper.prepare()) 2. 再创建Handler,默认是和当前线程的Looper关联起来 3. 循环消息(Looper.loop()

这三个步骤的顺序不能调换。因为主线程已经帮我们创建了Looper,所以我们不需要写,如果是在子线程创建Looper就需要了。

Handler机制的理解要靠自己去琢磨,不断的看源码,去理解,理清它们之间的互相调用。只有比较深入的理解了Handler,才能在面试中回答面试官的问题,靠死记硬背是不可取的。

到此,相信大家对“Handler相关面试题有哪些”有了更深的了解,不妨来实际操作一番吧!这里是创新互联网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

文章名称:Handler相关面试题有哪些
当前网址:https://www.cdcxhl.com/article34/jiegse.html

成都网站建设公司_创新互联,为您提供关键词优化企业网站制作网站导航微信小程序网页设计公司品牌网站制作

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

小程序开发