1,今天和大家一起从底层看看 Handle 的工作机制是什么样的,那么在引入之前我们先来了解 Handle 是用来干什么的
- 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 的一些技术来实现,这里有可能有同学已经想到了一种简单的方法就是类似于一下的代码:
- 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,具体实现代码如下:
- 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 方法,在里面进行一些收到消息的处理,然后在子线程中发送消息。
这里我们需要注意到的属性和方法是:
- 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 的一种实现方法,我们再看下一种方法
- 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() 方法,然后我们还有下面一种方法
- 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 中的源码超级简单,这里就不给大家废话了,只需要知道这几个重要的方法和属性
到这里我们基本上就分析完了。其实很简单有没有.......................
来源: http://www.cnblogs.com/wjtaigwh/p/6769983.html