1,今天和你们一块儿从底层看看Handle的工做机制是什么样的,那么在引入以前咱们先来了解Handle是用来干什么的java
handler通俗一点讲就是用来在各个线程之间发送数据的处理对象。在任何线程中,只要得到了另外一个线程的handler,则能够经过 handler.sendMessage(message)方法向那个线程发送数据。基于这个机制,咱们在处理多线程的时候能够新建一个thread,这个thread拥有UI线程中的一个handler。当thread处理完一些耗时的操做后经过传递过来的handler向UI线程发送数据,由UI线程去更新界面。 主线程:运行全部UI组件,它经过一个消息队列来完成此任务。设备会将用户的每项操做转换为消息,并将它们放入正在运行的消息队列中。主线程位于一个循环中,并处理每条消息。若是任何一个消息用时超过5秒,Android将抛出ANR。因此一个任务用时超过5秒,应该在一个独立线程中完成它,或者延迟处理它,当主线程空闲下来再返回来处理它。
上面的总结成一句话来讲就是,因为主线程不能作耗时操做而子线程不能更新UI,解决线程间通讯问题。网络
2,这里防止有些同窗压根没有了解过Handle,因此这里仍是带着你们简单的从使用入手,先知道怎么使用以后,再来经过源码来理解它的使用多线程
先看一下效果:异步
很简单的功能,就是相似于一个定时器的东西,TextView不停的去更新UI,要实现这个功能很简单,在java中咱们能够经过Timer或者ScheduledExecutorService来实现,而如今咱们打算使用Android的一些技术来实现,这里有可能有同窗已经想到了一种简单的方法就是相似于一下的代码:async
public class FourActivity extends AppCompatActivity{ private TextView tv_textview; private int count = 0;// 用于显示到textView上的数据 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_four); // 子线程中产生数据,而后须要将数据传递到主线程中,设置到Textview。 tv_textview = (TextView) findViewById(R.id.tv_main_show); // 数据产生自子线程 new Thread() { public void run() { // 子线程中执行的内容: while (true) { if (count < 100) { count++;// 反复的设置到textivew上 tv_textview.setText(count + "");// (错误代码)。 try { Thread.sleep(1000);// 模拟网络延迟。睡眠。 } catch (InterruptedException e) { e.printStackTrace(); } } else { break; } } }; }.start(); } }
是的上面的代码貌似看上去没什么问题,逻辑也很清楚,可是运行以后咱们会发现报错了,报错的缘由也很简单,就是子线程中不能去更新UI,那么咱们又想更新UI该怎么办呢?这里就要使用到咱们的Handle,具体实现代码以下:ide
public class FourActivity extends AppCompatActivity{ private TextView tv_textview; private int count = 0;// 用于显示到textView上的数据 // 在主线程中建立handler对象,用于子线程发送消息,主线程处理消息。 private Handler handler = new Handler() { // 重写handler处理消息的方法。 public void handleMessage(Message msg) { switch (msg.what) { case 0: int count = msg.arg1; int num = msg.arg2; String str = (String) msg.obj; tv_textview.setText(count + "," + str); // Log.i("tag", "===num:" + num + ",what:" + what); } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_four); // 子线程中产生数据,而后须要将数据传递到主线程中,设置到Textview。 tv_textview = (TextView) findViewById(R.id.tv_main_show); // 数据产生自子线程 new Thread() { public void run() { // 子线程中执行的内容: while (true) { if (count < 100) { count++;// 反复的设置到textivew上 // tv_textview.setText(count + "");// 错误代码。 // 将子线程中产生的数据count,传递到UI主线程中。 // 写法一:Message从消息池中取数据。由handler发送给主线程。 Message msg = Message.obtain();// // 从消息池中获取一个Message对象,用于存储数据,若是消息池中没有Message,会自动建立一个,以供使用。不要本身new // // Message,为了提升效率。 msg.arg1 = count;// arg1,arg2,用于存储int类型的数据的。 msg.arg2 = 10; msg.obj = "么么哒";// 用于存储Objct类型的数据 // // Bundle,用于存储大量的数据。 msg.what = 0;// 用于标记该Mesage,用于区分的。 // handler.sendMessage(msg);// // 在子线程中使用主线程的handler对象,发送消息到主线程。 try { Thread.sleep(1000);// 模拟网络延迟。睡眠。 } catch (InterruptedException e) { e.printStackTrace(); } } else { break; } } }; }.start(); } }
咱们能够看到要实现这个功能,首先要建立一个全局变量的handle,重写它的handleMessage方法,在里面进行一些收到消息的处理,而后在子线程中发送消息。oop
这里咱们须要注意到的属性和方法是:post
1,获取Message对象的话使用 Message msg = Message.obtain(); 在消息池中去拿消息对象,而不要动手建立 2,Message对象中有几个属性和方法 ,具体代码以下: msg.arg1 = count;// arg1,arg2,用于存储int类型的数据的。 msg.arg2 = 10; msg.obj = "么么哒";// 用于存储Objct类型的数据 // // Bundle,用于存储大量的数据。 msg.what = 0;// 用于标记该Mesage,用于区分的。 首先咱们想传递的若是是int类型的数据的话咱们能够直接使用Message中的arg1和arg2变量,若是咱们想传递object对象的话咱们可使用message中的obj变量,若是想传递大的变量的话,咱们可使用bundle对象,而后能够用what来区分Message的分类
咱们能够一看到当咱们调用handler.sendMessage(msg)的时候,执行的是咱们handleMessage()方法中的逻辑。ok,这是handle的一种实现方法,咱们再看下一种方法ui
new Thread() { public void run() { // 子线程中执行的内容: while (true) { if (count < 100) { count++;// 反复的设置到textivew上 // 写法二: Message msg = handler.obtainMessage();// // 由handler来获取的消息,天然就归handler来处理 msg.arg1 = count; msg.sendToTarget();// 将msg发送给对应的目标:target:就是handler try { Thread.sleep(1000);// 模拟网络延迟。睡眠。 } catch (InterruptedException e) { e.printStackTrace(); } } else { break; } } }; }.start();
这时候咱们是经过handle来拿到对应的Message对象的,而后直接经过message对象的sendToTarget方法发送消息,而不须要像咱们以前的handler.sendMessage()方法,而后咱们还有下面一种方法this
new Thread() { public void run() { // 子线程中执行的内容: while (true) { if (count < 100) { count++;// 反复的设置到textivew上 // 写法三: Message msg = Message.obtain(); msg.arg1 = count; msg.setTarget(handler); msg.sendToTarget(); try { Thread.sleep(1000);// 模拟网络延迟。睡眠。 } catch (InterruptedException e) { e.printStackTrace(); } } else { break; } } }; }.start();
这里咱们仍是经过消息池拿到咱们的Message对象,可是调用了msg.setTarget()来绑定handle,因此也能够直接调用msg.sendToTarget()方法
先看一下的基本使用
/** * step1:启动子线程获取数据 * * step2:在主线程中建立Handler对象 * * step3:在子线程中直接操做handler.post(Runnable r):重写该接口的是实现类。。 * * step4:run()---》执行在main线程中。 * * @author wangjitao * */
再看看具体的实现
public class MainActivity extends Activity { private TextView tv_show; private Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_show = (TextView) findViewById(R.id.tv_show); } // 点击按钮,由子线程向主线程发送数据 public void click(View v) { new Thread() { @Override public void run() { // 产生数据,发送到主线程中 final int num = 100; final String str = "HelloWorld"; ArrayList<String> list = new ArrayList<String>(); Collections.addAll(list, "abc", "aa", "bb"); Bundle bundle = new Bundle(); bundle.putString("str", str); bundle.putSerializable("list", list); // msg.setData(bundle); handler.post(new Runnable() {// handler将来要作的事,写在run里。 @Override public void run() { // 执行在主线程中的。 Log.i("tag", "==线程id:" + Thread.currentThread().getId() + ",线程名称:" + Thread.currentThread().getName()); tv_show.setText("str," + str + ",num:" + num); } }); } }.start(); } }
这里关键的是调用handle.post方法,且在方法里面直接执行了更新UI操做,可是你们运行一下,不报错,因此这里的和你们说一下,这里的post方法是将运行在主线程中,因此就能够更新ui,咱们过一下看看源码它是怎么实现的。
咱们上面实现了从子线程中发送消息,在主线线程中获取消息,那么咱们如今想在主线程中发送消息,在子线程中处理消息,这个咱们该怎么实现呢?
public class MainActivity extends Activity { private Handler handler = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 点击按钮,建立子线程 public void click_start(View v) { new MyThread().start(); } // 点击该按钮,数据从主线程传递到子线程中 public void click_send(View v) { // 由handler发送数据 Message msg = Message.obtain();// msg.what = 1; msg.arg1 = 100; msg.obj = "我是从主线程中传递来的数据"; handler.sendMessage(msg);// msg:target:handler } // 自定义类,继承Thread,表示用于子线程 class MyThread extends Thread { @Override public void run() { // 子线程中要执行的代码: // 用于接收主线程传来的数据 // 由handler接收处理数据就能够。 /** * 报错信息: java.lang.RuntimeException: Can't create handler inside * thread that has not called Looper.prepare() */ // 应该先执行:Looper.prepare(); /** * 表示将当前的线程升级为:Looper线程 * * 1.建立一个Looper对象。注意:一个线程中只能有一个Looper对象。用ThreadLocal<T> * * 2.一个Looper对象,负责维护一个消息队列:new MessageQueue */ Looper.prepare();// 将子线程升级,成Looper线程。就能够操做handler对象。不然一个普通的子线程,不能操做handler。 handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: int num = msg.arg1; String str = (String) msg.obj; Log.i("tag", "===子线程:" + Thread.currentThread().getId() + ",线程名字:" + Thread.currentThread().getName() + ",数据:" + num + ",str:" + str); break; default: break; } } }; Looper.loop();// 用于循环处理消息。 } } }
这里要注意,要想子线程收到messsage ,首先要将子线程升级成Looper线程,主要调用下面这两个方法
Looper.prepare();// 将子线程升级,成Looper线程。就能够操做handler对象。不然一个普通的子线程,不能操做handler。 Looper.loop();// 用于循环处理消息。
既然上面的先调用的是Looper.prepare();方法,那么咱们来看看prepare方法吧
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)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
再看一下它的一些重要的成员变量
//ThreadLocal线程本地变量,用于为该线程中只有一个Looper对象。 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); //Looper内部维护的消息队列 final MessageQueue mQueue; //当前线程对象自己 final Thread mThread;
能够看到,这里咱们调用Looper.prepare()方法方法的时候,首先判断sThreadLocal 是否已经存在Looper对象,若是存在则抛异常,这里是由于每一个线程中只能包含一个Looper对象去维护,若是不存在,则new Looper对象,且初始化内部消息维护队列 mQueue、线程自己mThread。
看完prepare()方法以后咱们继续往下看,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; 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 traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } 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(); } } public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
而后我将上面的代码提取出重要的
//循环工做 public static void loop() { //获取当前的looper对象 final Looper me = myLooper(); //获取消息队列 final MessageQueue queue = me.mQueue; //循环处理消息 for (;;) { Message msg = queue.next(); // 从消息队列中获取消息 msg.target.dispatchMessage(msg);//将msg交给对应的handler去分发。msg.target:就是对应的handler //handler.dispatchMessage(msg); msg.recycle();//回收消息到消息池,便于下次使用。 } } //从线程的本地变量中获取Looper对象 public static Looper myLooper() { return sThreadLocal.get(); } }
首先咱们经过调用自己的 myLooper()来获取当前的Looper对象,而后获取mQueue消息队列,而后就是for循环了,一直获取mQueue消息队列中的Massage对象,知道消息队列中没有消息而后退出循环,而后在循环中最关键的是一下这句代码
msg.target.dispatchMessage(msg);
首先msg.target,这个你们有没有很熟悉,咱们上面使用的时候曾经使用过msg.sendToTarget()和msg.setTarget(handler),看里面的参数!是一个handle,为了验证咱们的猜测咱们看看message源码中的target属性
public final class Message implements Parcelable { ...........省略代码 /*package*/ int flags; /*package*/ long when; /*package*/ Bundle data; /*package*/ Handler target; /*package*/ Runnable callback; // sometimes we store linked lists of these things ...........省略代码 }
果真,这里target是Handle对象,也就是说是调用的handle.dispatchMessage(msg)的对象,咱们来看看handle源码
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } public interface Callback { public boolean handleMessage(Message msg); }
看到没 这里咱们mCallback.handleMessage仍是最终调用的是handle中的handleMessage方法!!!,这样整个Looper源码的逻辑就通了,首先建立Looper对象用来维护本线程中的消息队列,而后for循环,一直将消息队列中的message分发到对应的handle上去。
这里咱们的Looper中有如下注意事项
总结:Looper的注意事项 1.每个线程只能有最多一个Looper对象。 2.当建立Looper的同时,MessageQueue一同被建立。 3.调用loop()方法,循环从消息队列上获取消息,分发给对应的handler。
下面咱们来看看Handle的源码
首先先看一下全局变量
//与当前的handler对象关联的消息队列 final MessageQueue mQueue; //与handler关联的Looper对象 final Looper mLooper; final Callback mCallback;
在构造方法中进行一些赋值
//建立Handler对象 public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//将Looper维护的消息队列,赋值给当前handler所关联的消息队列。 mCallback = callback; }
这里的构造和咱们的Thread构造很像,可经过直接匿名内部内的方式也可使用建立CallBack子类的方式获得Handle对象这里调用 Looper.myLooper()拿到,而后设置一下消息队列,其实还有一个构造方法,以下:
public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
这里因为咱们不怎么使用,因此只说一下几个参数的含义传第一个参数进来一个looper来代替默认的looper,第二个参数callback接口用于处理handler传递的Message,第三个是说是否要异步
ok,咱们继续往下看方法,咱们handle常使用sendMessage方法,来看看它里面是怎么使用的
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } 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) { 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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
最后其实调用的是handle类中的enqueueMessage方法,而后 msg.target = this;很重要 ,绑定发送message消息对象和当前的handle对象,把message插入到消息队列中。
下面咱们继续看handle.dispatchMessage()因为在Looper中已经讲过了的,因此在这里不在和你们废话了。
再看看咱们的HandleMessage()方法
//由接收数据的线程, 重写的方法,表示处理msg public void handleMessage(Message msg) { }
咱们通常会重写该方法,进行一些接收消息的操做。
继续往看handle.post()方法
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
能够看到这里最终仍是调用了sendMessageDelayed方法,和咱们的sendMessage方法同样,可是不一样的区别是什么呢?这里调用了getPostMessage()中,咱们建立了一个Message对象,并将传入的Runnable对象赋值给Message的callback成员字段,而后返回该Message,而后在post方法中该携带有Runnable信息的Message传入到sendMessageDelayed方法中。由此咱们能够看到全部的postXXX方法内部都须要借助sendMessageXXX方法来实现,因此postXXX与sendMessageXXX并非对立关系,而是postXXX依赖sendMessageXXX,因此postXXX方法能够经过sendMessageXXX方法向消息队列中传入消息,只不过经过postXXX方法向消息队列中传入的消息都携带有Runnable对象(Message.callback),这时候要要给你们贴上咱们以前看到的一些代码。
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(); }
这里咱们首先判断msg的callback是否为空,显然在调用post方法的时候咱们不为空,因此走的是handleCallback方法,而在这个里面直接调用的是咱们Runnable中的run
方法,也就是说直接执行Runnable中的run方法。这里你们可能会有疑问了,以前不是说handle.post方法是运行在主线程中吗,我这里明明new了一个Runnable,为何还运行在主线程中啊,首先咱们要明白一个误区,不是建立一个Runnable对象就是标明是新建一个子线程,在咱们上例的状况中咱们的Handle是建立在Activity中的,Handler是绑定到建立它的主线程中的,Handler跟UI主线程是同一个线程,因此它原本就是在主线程中,再看看源码google给的注释
* Causes the Runnable r to be added to the message queue. * The runnable will be run on the thread to which this handler is * attached.
注释的意思是runnable会在handler所在的线程中执行。按照注释的意思以及咱们常用的状况来看,runnable的逻辑无疑是在主线程中执行的。这也解决了咱们为何post方法是执行在主线程中的。
咱们来总结一下Handle相关的知识点
Handler的属性和方法: 属性: final MessageQueue mQueue; final Looper mLooper; final Callback mCallback; 方法:5个重要方法 sendMessage(msg); handleMessage(); post(); sendEmptyMessage(); obtainMessage(); dispatchMessage(); sendMessageDelayed(); sendMessageAtTime();
public final class Message implements Parcelable { public int what; public int arg1; public int arg2; public Object obj; } what arg1 arg2 obj 方法: obtain(); setTarget(Handler target) setData() sendToTarget();
Message中的源码超级简单,这里就不给你们废话了,只须要知道这几个重要的方法和属性
到这里咱们基本上就分析完了。其实很简单有没有.......................