为了账号安全,请及时绑定邮箱和手机立即绑定

Android面试常客Handler详解

难度中级
时长 2小时 0分
学习人数
综合评分9.30
169人评价 查看评价
9.7 内容实用
9.3 简洁易懂
8.9 逻辑清晰
  • Handler原理的图解。 员工(Handler)要上厕所,需要和领导知会一下(sendMessage),领导(Looper)反馈消息( Looper.loop() )后,“上厕所”这件事还是由员工自己去解决(handleMessage)。 Looper仅仅起了传达消息的作用,而消息的发送和处理都还是由Handler自己去做。 ------------- 摘自大神笔记: MessageQueue在这里就是领导的脑子啊,如果有五个人ABCDE同时要上厕所它就会把这五个人记在脑子里并依次处理这五个人能否上厕所,比如A没什么事就让他去,B负责等待客户紧急电话他就不能去,并依次各自回传给ABCDE(handleMessage)。
    查看全部
  • 以下内容来自大神笔记: handler、looper、messagequeue、message四者可以这样理解: handler:工人; looper:传送带移动的动力; messagequeue:传送带; message:传送带上面的产品。 工人(handler)把自己的产品(message)放在传送带(messagequeue)尾部,在动力(looper)作用下,传送带向前移动,最终产品到达传送带头部,被取出来处理(handmessage())。 ----------------- 先要有个概念。 1、handler 消息处理器,负责处理消息。 2、Message 消息,包含消息id,被处理的对象。 3、MessageQueue 消息队列,存放Handler发送过来的Message 4、looper 消息泵,不间断的从MessageQueue消息队列中抽取消息。 简单的比喻looper就是水泵,MessageQueue储水的池塘,Message就是水,Handler就是操作的人。
    查看全部
  • Handler的原理是什么? 面试经典问题:Looper、Handler、Message(或MessageQueue)三者间的关系? 一、Handler封装了消息的发送(主要是将消息发送给谁(默认是Handler自己),以及什么时候发送)。 Looper: 1.内部包含一个消息队列 MessageQueue,所有的 Handler 发送的消息都走向(加入)这个消息队列。 2.Looper.Looper方法,就是一个死循环,不断地从 MessageQueue 取得消息,如果有消息就处理消息,没有消息就阻塞。 二、MessageQueue MessageQueue 就是一个消息队列,可以添加消息,并处理消息。 三、Handler 也很简单,内部会跟 Looper 进行关联,也就是说,在 Handler 的内部可以找到 Looper,找到了 Looper 也就找到了 Message。在 Handler 中发送消息,其实就是向 MessageQueue 队列中发送消息。 总结:Handler 负责发送消息,Looper 负责接收 Handler 发送的消息,并直接把消息回传给 Handler 自己,MessageQueue就是一个存储消息的容器。
    查看全部
  • 1)postDelayed(Runnable r, long delayMillis)延时delayMillis毫秒 将Runnable插入消息列队,Runnable将在handle绑定的线程中运行。 2)第一个postDelayed方法里面的runnable对象是main上面MyRunnable类线程的对象,main里面的handler.postDelayed调用之后不断的在线程中调用自身,从而实现轮播。
    查看全部
  • handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它处理消息 Android在设计的时候,就封装了一套消息创建、传递、处理机制,如果不遵循这样的机制,就没有办法更新UI信息,就会抛出异常。
    查看全部
    0 采集 收起 来源:Handler是什么

    2017-04-15

  • Handler简介
    查看全部
    0 采集 收起 来源:课程内容介绍

    2017-04-14

  • 1)postDelayed(Runnable r, long delayMillis)延时delayMillis毫秒 将Runnable插入消息列队,Runnable将在handle绑定的线程中运行。 2)第一个postDelayed方法里面的runnable对象是main上面MyRunnable类线程的对象,main里面的handler.postDelayed调用之后不断的在线程中调用自身,从而实现轮播。
    查看全部
  • 关于第二个异常的举例: new Thread(){ @Override public void run() { Handler handler = new Handler(); } } 运行时就会抛出异常“Can't create handler inside thread that has not called Looper.prepare()”。 如图,查看Handler()的源代码。Looper.myLooper()是从当前线程中取出 Looper对象,但如果我们之前并没有在该线程中创建 Looper 对象( Looper.prepare() )的话,myLooper 就是空的,那么 mLooper 便也是空的,然后就报错了。 Looper.myLooper():通过 TreadLocal 去拿出与当前线程相关联的 Looper 对象。
    查看全部
  • 使用handler时候常遇到的问题(系统抛出的异常): 1. android.view.ViewRootImpl$CalledFromWrongTreadException:Only the original thread that created a view hierarchy can touch its views 2. Can't create handler inside thread that has not called Looper.prepare() 在子线程中创建一个 Handler对象,却没有给它指定一个Looper对象的时候,就会抛出该异常。 如图是第一个异常抛出的原因:checkThread()判断“当前线程(更新UI的线程)是不是主线程”,如果不是,就抛出该异常。
    查看全部
  • 如图,真正判断当前线程是否在UI线程进行更新操作的核心代码就是图中的蓝色部分。 通过 ViewParent对象的 invalidateChild()方法做出判断。 ViewParent 是 ViewRootImpl 的实现类。 ViewRootImpl 主要用于更新 UI 的判断处理逻辑。它的初始化是在Activity的onResume()方法中进行。 我们做了个小例子:在子线程中更新TextViwe,也就是更新UI: new Thread(){ @Override public void run() { textView.setText("在非主线程中更新UI"); } }.start(); 会发现系统并不会报错,原因是 ViewParent 对象的 invalidateChild()方法还没来得及做出判断!为什么来不及? 因为“检查当前线程是否是主线程”的操作是在onResume()方法中,该方法在onCreate()之后调用,所以存在极为短暂的时间是无法做出检查的,就在这段时间内通过非主线程更新UI,系统也无法报错。 虽然可以在非主线程中更新 UI ,但是不建议这么做,因为这样的做法是有隐患的。 --------------- 摘自评论区: 在ViewRootImpl没有实例化的时候是不会check是否是主线程,也就是说,在非UI线程中是可以更新UI的,但是为了线程安全的原因,系统会强制要求只能在主线程中更新UI(猜测)。之所以说没有太大的意义是因为实际开发中是不会有这种情况的,谷歌官方也是说了只能在UI线程更新UI,这顶多算是一个BUG吧。 回调的路径: text.setText()-->checkForRelayout()-->invalidate()-->ViewParent.invalidateChild()-->invalidateChildInParent()-->checkThread()
    查看全部
  • 代码实现: 1.使用Handler的post(): public void handlerChangeUI(){ handler.post(new Runnable(){ @Override public void run() { textView.setText("OK"); } }); } 2. 使用Handler的sendMessage(): public void handlerChangeUI2(){ handler.sendEmptyMessage(1); } 3. 使用Activity的runOnUiThread(): public void activityUpdateUI(){ runOnUiThread(new Runnable() { @Override public void run() { textView.setText("Changed by Activity"); } }); } 4. 使用View的 post(): public void viewUpdateUI(){ textView.post(new Runnable() { @Override public void run() { textView.setText("UI was updated by view.post"); } }); } 最后在 onCreate()内部创建一个线程,依次调用如上四种方法,观察结果。 new Thread(){ @Override public void run() { try { Thread.sleep(1000); // handlerChangeUI(); // handlerChangeUI2() // activityUpdateUI() viewUpdateUI(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start();
    查看全部
  • 最后,看一下View的post()方法。 public boolean post(Runnable action){ final AttachInfo attachInfo = mAttachInfo; if(attachInfo != null){ return attachInfo.mHandler.poat(action); } ViewRootImpl.getRunQueue().post(action); return true; } 首先判断 attachInfo 是否为空。如果不为空,则通过 attachInfo 的Handler对象去post一个Runnable对象。如果为空,则调用ViewRootImpl的getRunQueue去拿到一个Handler去post一个Runnable对象。其中,ViewRootImpl 和更新UI的机制有着密切相关的联系。 综上,更新UI的四种方法,本质上都是通过Handler机制去更新UI的。
    查看全部
  • Android中更新UI的几种方式: 1. Activity 的 runOnUiThread() 2. Handler 的 post() 3. Handler 的 sendMessage() 4. View 的 post() 查看源码可发现,handler的post()内部调用了 sendMessageDelayed()方法,和我们普通发送Message是一样的,只不过它还要先调用getPostMessage()方法: public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r),0); } private static Message getPostMessage(Runnable r){ Message m = Message.obtain(); m.callback = r; ruturn m; } 原来Callback就是一个Runnable对象。 总之,handler使用post()去发送一个Runnable,其实它内部也是封装了Message去发送的,本质上和直接发送Message没有区别。 那么Activity的runOnUiThread()又是如何更新UI的呢? public final void runOnUiThread(Runnable action){ if(Thread.currentThraed()!= mUiThread){ mHandler.post(action); // mHandler是Activity自己的Handler对象 }else{ action.run(); } } 首先,判断当前线程是不是UI线程,不是的话,则调用Activity自己的Handler对象,通过该对象发送一个Runnable;如果是,则调用UI线程的run()方法。 所以,Activity的runOnUiThread()内部也是使用了Handler机制去更新UI。
    查看全部
  • 主线程和子线程之间的交互的实现 本例的思路: 1. 在主线程中创建两个Handler对象 handler 和 handler2(图中是threadHandler),并直接初始化 handler,而handler2 不初始化。 2. 使用 HandlerThread 创建一个子线程 thread ,并启动 thread 。 3. 初始化 handler2(使用 thread 的 getLooper()作为参数),这样一来handler2就是子线程的 Handler 对象。 4. 重写 handler 和 handler2 的 handleMessage()方法,在里面创建Message对象,并使用对方的sendMessageDelayed(message,1000);方法发送消息。 5. 定义一个Button,定义点击事件,由点击事件触发如上交互的操作。 如上操作中,第4点是核心,也就是两个 Handler 各自重写 handleMessage(),方法里面却是使用对方的 sendMessage() 方法。这样就实现了主线程和子线程的相互发送信息。 -------------- 摘自评论区,亲测有效: 取消发送,经测试,需要将UIhandler 和 threadhandler中的message的what 设置为1,取消的时候要同时removeMessage(1);
    查看全部
  • HandlerThread对象的方法 getLooper()首先会判断线程是否活着,如果活着,并且默认的 Looper 对象 mLooper 等于null,就让当前线程处于等待的状态( wait() )。 那么,处于等待状态的线程,什么时候被唤醒呢?可以去查看 HandlerThread 的 run()方法,毕竟 HandlerThread 是一个Thread类: public void run(){ mTid = Process.myTid(); Looper.prepare(); // 先调用该方法创建Looper对象。 synchronized(this){ mLooper = Looper.myLooer(); // 将Looper对象赋值给 mLooper,没错,也是getLooper()的mLooper。 notifyAll(); // 唤醒线程,Handler对象就可以拿到Looper对象,也就不会报出空指针的问题。 } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; } --------------- 主要是在HandlerThread里面创建一个Looper,和默认的 handler 进行关联。所有的handlerMessage()都是在子线程中调用。我们可以使用 HandlerThread 模拟异步任务的操作,只需要在主线程中给子线程发送消息,让子线程做耗时操作。总之,HandlerThread可以处理耗时操作,例如下载网络图片、更新数据库等等,可以在完全不占用主线程的情况下处理比较耗时的操作。从设计的角度来看,是比较方便快捷的。 因为如果自己想要实现异步任务机制,就需要考虑一些问题,例如要开启线程、往异步任务中添加任务,任务本身还要考虑存储结构、什么时候添加任务、什么时候移除任务,以及任务如何传递、派发等等。比较麻烦。 而Android提供了HandlerThread方法,它是系统已经将任务的发送、处理等都封装好了,使用起来比较方便,我们只需调用 HandlerThread。
    查看全部

举报

0/150
提交
取消
老师告诉你能学到什么?
通过本课程,你将学到: 1、什么是Handler 2、如何使用Handler 3、Handler的原理是什么 4、如何定义一个与线程相关的Handler 5、Android更新UI的几种方式 6、非UI线程真的不能更新UI吗 7、Handler使用过程中遇到的问题

微信扫码,参与3人拼团

意见反馈 帮助中心 APP下载
官方微信
友情提示:

您好,此课程属于迁移课程,您已购买该课程,无需重复购买,感谢您对慕课网的支持!