-
如图蓝色选中区域,主线程的 handler 使用其他线程的 looper,运行时会报出空指针异常,程序崩溃。 为什么呢?这涉及到多线程并发问题。当主线程已经运行到图示蓝色选中区域时,可能其他线程(这里是thread线程)还没有创建或者初始化 looper。于是就造成了空指针异常。 如何解决多线程导致的空指针问题? 使用HandlerThread。查看全部
-
如图,分别在主线程和自定义的子线程中创建 Handler 。在各自的 handlerMessage()方法中打印当前线程的id。 Handler和线程是相关联的。 再次强调:不可以在主线程中做耗时操作,就比如示例里面主线程中的 handlerMessage(),不能执行耗时操作。 创建一个与自定义线程相关的Handler的代码: class MyThread extends Thread{ public Handler handler; @Override public void run() { Looper.prepare(); // 创建Looper,默认情况是根据当前线程去拿到Looper handler = new Handler(){ // 与子线程相关的Handler @Override public void handleMessage(Message msg) { System.out.printf("currentThread:"+Thread.currentThread()); // 打印当前线程的id } }; Looper.loop(); // 调用该方法实现对消息的循环处理 } } 要在线程中创建一个 Handler,需要先创建 Looper ,创建的方法很简单:Looper.prepare(); 之后就可以创建 Handler 了。在创建 Handler 之后别忘了调用 Looper.loop(); 这样才能实现对消息的循环处理。 摘自评论区: 推荐博客一篇:http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html查看全部
-
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对象的方法 sendEmptyMessage(int what)会回调 sendEmptyMessageDelayed(what,0)方法: public final boolean sendEmptyMessageDelayed(int what,long delayMillis){ Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg,delayMillis); } 方法内部回调了如下方法: public final boolean sendMessageDelayed(Message msg,long delayMillis){ if(delayMillis < 0){ delayMillis = 0; } return sendMessageAtTime(msg,SystemClock.uptimeMillis() + delayMillis); } 方法内部回调了如下方法: public boolean sendMessageAtTime(Message msg,long uptimeMillis){ boolean sent = false; MessageQueue queue = mQueue; // 通过当前线程获得消息队列 if(queue!=null){ msg.target = this; // 发送的对象是Handler自己 sent = queue.enqueueMessage(msg, uptimeMillis); // 将消息放入消息队列 } else{ RuntimeException e = new RuntimeException( this + "sendMessageAtTime()called with no mQueue"); Log.w("Looper",e.getMessage(),e); } return sent; } 如上的种种方法揭示了Handler和MessageQueue的部分关系。查看全部
-
threadlocal 作用:在线程中保存一些变量信息。创建和线程相关的对象(变量)。主要有两个方法:set和get,set将变量放入threadlocal,get则是从 threadlocal 中取出变量。 -------------- 消息队列如何轮询呢?整个“轮询”是通过Looper.Looper来实现的,其核心代码是一个for(;;)的死循环。 -------------- Handler的dispatchMessage()的源码: public void dispatchMessage(Message msg){ if(msg.callback != null){ handleCallack(msg); }else{ if(mCallback!=null){ if(mCallback.handMessage(msg)){ return; } } handleMessage(msg); // Handler自身的handlerMessage()方法。 } } 其中,mCallback就是我们在创建Handler对象的时候传入的Callback()参数,如果它不为空,就判断它的 handlerMessage()返回的布尔变量,如果是true就直接retuen,不再执行 Handler本身的handlerMessage() 方法,这就是所谓的“拦截”消息。 从上述可以看出,Handler是通过 dispatchMessage() 去调用 handleMessage(),这就是为什么 Handler 在发送 Message 的时候会回调 handleMessage()方法。 之前有总结过:Handler 负责发送消息,Looper 负责接收 Handler 发送的消息,并直接把消息回传给 Handler 自己,MessageQueue就是一个存储消息的容器。 那么,如何回传?调用Looper.Looper,判断target是谁,默认就是Handler自己,调用Handler自己的dispatchMessage()方法去回调自己的 handleMessage()方法。这就是回传。查看全部
-
Android为什么要设计只能通过Handler机制更新UI呢? 最根本的目的就是解决多线程并发的问题。 1. 假如在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样子的问题? --> 更新界面混乱。 2. 如果对更新UI的操作都进行加锁处理的话又会产生什么样子的问题? --> 性能下降。 正是对以上问题的考虑,Android给我们提供了一套更新UI的机制,我们只需遵循这样的机制就可以了,根本不用去关心多线程(并发)的问题,所有的更新UI的操作,都是在主线程的消息队列中去“轮询”处理的。 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就是一个存储消息的容器。查看全部
-
Callback()和 Handler()一样,都有handleMessage(Message message)方法,不同的是前者是有返回值的。 使用Callback()拦截消息: 在 Handler 发送消息的过程中,我们是可以去截获这个消息。要怎么去截获呢?在创建Handler对象的时候我们需要传入一个参数,也就是 new 一个 Callback 对象,在 Callback 对象当中就可以截获 Handler 发送过来的消息: Callback内部的方法 handlerMessage()默认返回一个 false,如果改成 true,就会截获消息,也就是说,Handler内部的方法 handlerMessage()将不会被执行。查看全部
-
我们可以自定义一个Message对象,然后通过Handler去发送这个对象。但我们也可以直接复用系统的Message对象。 通过Handler对象的obtainMessage()去复用系统的Message(内部原理:系统会优先返回空的Message,如果没有,则创建一个Message): Message message = handler.obtainMessage(); 然后,可以使用该 message 的 sendToTarge()方法将信息发送出去,而不用通过Handler对象的sendMessage(message)方法。 查看 sendToTarget()源码: public void sendToTarget(){ target.sendMessage(this); } 其中“target”就是Handler自己。实际上就是调用Handler自己的sendMessage()方法。本质上和普通的发送消息( handler.sendMessage(message) )是一样的。 -------------- 消息的移除 handler.removeCallbacks(myRunnable);查看全部
-
Handler的示例,实现图片轮播。 代码实现 1. 在主布局中定义一个ImageView控件。 2. 在 MainActivity 中创建并初始化ImageView,定义一个图片数组 images 和数组下标索引 index,创建一个Handler对象。 3. 创建一个内部类 MyRunnable 实现 Runnable 接口,重写 run() 方法: public void run() { index++; index = index%3; // 图片轮播(一般是通过ViewPager实现图片轮播) imageView.setImageResource(images[index]); handler.postDelayed(myRunnable,1000); // 每隔一秒换一次图片 } 4. 在onCreste()方法中调用handler,也就是在主线程中调用handler: handler.postDelayed(myRunnable,1000);查看全部
-
#摘自Hongyang的博客 1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。 2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。 3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。 4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。 5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。 好了,总结完成,大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。查看全部
-
1. 我们要创建一个handler的时候,它会和默认的一个线程进行绑定,而这个默认的线程当中就有一个MessageQueue(消息队列)。 2. handler的两个用途:(1)定时发送一个Messages或者Runnables对象;(2)可以在一个线程当中去处理并执行一个Action的动作。 3. 主线程运行一个消息队列,并管理着一些顶级的应用对象(top-level application objects),包括Activity、Broadcast Receiver等等,这些对象默认都是创建在Activity Thread(也就是我们常说的UI线程或者主线程)当中。查看全部
-
以下摘自评论区: 我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的, 也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。 这个时候,Handler就出现了。,来解决这个复杂的问题 ,由于Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。 Handler主要接受子线程发送的数据, 并用此数据配合主线程更新UI。 解释:当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件, 进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。 如果此时需要一个耗时的操作,例如: 联网读取数据, 或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。查看全部
-
Handler是什么? Handler是Android提供的一套用来更新UI的机制,也是一套消息处理机制,可以通过它发送消息,也可以通过它处理消息。 所有Activity生命周期回调的方法(例如onCreate()、onDestory()等等),都是通过handler机制去发送消息的,然后根据不同的消息(message)做相应的分支处理。例如发送一个消息给 Framework,告知需要调用onCreate()或onDestory()方法。实际上在 Framework 当中,Activity的交互大部分都是用AMS(Activity Manager Service)做处理的。整个应用程序的核心的一个类就是 Activity Thread,Activity Thread就是通过handler机制接收到 Activity Manager Service发送的有关Activity生命周期的管理的消息(比如启动)。 为什么要使用Handler? Android在设计的时候,就封装了一套消息的创建、传递、处理机制,如果不遵循这样的机制,就没有办法更新UI信息,并且会抛出异常信息。这样的机制便包含Handler机制。查看全部
-
部分内容查看全部
举报
0/150
提交
取消