最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Android中主线程与子线程之间相互通信教程
时间:2022-11-14 22:17:01 编辑:袖梨 来源:一聚教程网
有时候,我们也可能碰到这样子的一种需求:需要主线程来向子线程发送消息,希望子线程来完成什么任务。如果这样子应该怎么做呢?这就是这篇文章将要讨论的内容。
一、HandlerThread类
主线程发送消息给子线程,通常思维逻辑就是:其实很简单,在主线程中实例化一个Handler,然后让他与子线程相关联(只要它与子线程的Looper相关联即可),这样子它处理的消息就是该子线程中的消息队列,而处理的逻辑都是在该子线程中执行的,不会占用主线程的时间。那么我们就来实现一下,看看这样子到底行得通还是行不通。新建项目,修改它的MainActivity的代码,如下即可:
package com.example.handldertest; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.widget.TextView; public class ThreadHandlerActivity extends Activity{ //创建子线程 class MyThread extends Thread{ private Looper looper;//取出该子线程的Looper public void run() { Looper.prepare();//创建该子线程的Looper looper = Looper.myLooper();//取出该子线程的Looper Looper.loop();//只要调用了该方法才能不断循环取出消息 } } private TextView tv; private MyThread thread; private Handler mHandler;//将mHandler指定轮询的Looper protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tv = new TextView(this); tv.setText("Handler实验"); setContentView(tv); thread = new MyThread(); thread.start();//千万别忘记开启这个线程 //下面是主线程发送消息 mHandler = new Handler(thread.looper){ public void handleMessage(android.os.Message msg) { Log.d("当前子线程是----->", Thread.currentThread()+""); }; }; mHandler.sendEmptyMessage(1); } }
好了,现在运行该程序。有没有得到预期的结果呢?显然没有,因为报错误了,如下:
这是一个空指针错误。这是为什么呢?仔细思考,也不难发现原因。因为当主线程走到第38行时,此时子线程的Looper对象还没有被创建出来,那么此时thread.looper肯定为空了。其实这个时间是很不好控制的,当然了,你可以让主线程休眠2秒后再执行第38行以后的代码。但是如果有很多个子线程都需要主线程类给其分配任务怎么办??那简直要乱套了。所以我们就更好的解决方式。就是android显然也考虑到了这个问题,于是它我们提供了一个HandlerThread类。这个类是专门处理这个问题的。
当主线程中有耗时的操作时,需要在子线程中完成,通常我们就把这个逻辑放在HandlerThread的对象中执行(该对象就是一个子线程),然后在需要开始执行逻辑的地方发送一个Message来通知一下就可以了。下面我们就修改上面的代码,看一看如何使用HandlerThread这个类。修改MainActivity中的代码如下:
package com.example.handldertest; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.widget.TextView; public class ThreadHandlerActivity extends Activity{ private TextView tv; private Handler mHandler;//将mHandler指定轮询的Looper protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tv = new TextView(this); tv.setText("Handler实验"); setContentView(tv); //实例化一个特殊的线程HandlerThread,必须给其指定一个名字 HandlerThread thread = new HandlerThread("handler thread"); thread.start();//千万不要忘记开启这个线程 //将mHandler与thread相关联 mHandler = new Handler(thread.getLooper()){ public void handleMessage(android.os.Message msg) { Log.d("当前子线程是----->", Thread.currentThread()+""); }; }; mHandler.sendEmptyMessage(1);//发送消息 } }
运行程序,打印的结果如下:
从打印结果来看,当前子线程的名字正是我们所起的那个名字“handler thread"。
你会有疑问,表面上看HandlerThread并没有创建自己的Looper啊?而且既然是一个线程,那么我们肯定也能重写它的run方法吧。在解答你的疑问之前,我们不妨重写它的run方法来看一看会有什么结果。将代码修改如下:
package com.example.handldertest; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.widget.TextView; public class ThreadHandlerActivity extends Activity{ private TextView tv; private Handler mHandler;//将mHandler指定轮询的Looper protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tv = new TextView(this); tv.setText("Handler实验"); setContentView(tv); //实例化一个特殊的线程HandlerThread,必须给其指定一个名字 HandlerThread thread = new HandlerThread("handler thread"){ @Override public void run() { for(int i=0;i<3;i++){ Log.d("handler thread run ",i+""); } } }; // HandlerThread thread = new HandlerThread("handler thread"); thread.start();//千万不要忘记开启这个线程 //将mHandler与thread相关联 mHandler = new Handler(thread.getLooper()){ public void handleMessage(android.os.Message msg) { Log.d("当前子线程是----->", Thread.currentThread()+""); }; }; mHandler.sendEmptyMessage(1);//发送消息 } }
红色部分就是我们重写了它的run方法。再云运行程序,打印的结果如下:
for循环的打印结果正常,但是为什么没有打印出”当前子线程“呢。其实这正是我们要解释的地方。还记得上一篇文章中实现与子线程相关联的的Handler,我们是怎么做的吗?没读过的朋友看以点击链接(http://www.cnblogs.com/fuly550871915/p/4889838.html)。其实我们实现Handlei与线程的关联正是写在run方法中的。而对于HandlerThread这样的线程,也是如此。我们翻看这个类的源代码,找到它的run方法,如下:
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
在源代码的第4行,进行了实例化自己的Looper,如果继续追踪源代码翻看其getLooper方法你会发现,如果一个Handler在与HandlerThread进行绑定时,发现Looper为空,Handler则会一直等待直到Looper被创建出来为止,然后才继续执行后续的代码。所以我们重写了HandlerThread的run方法,肯定就不会去创建Looper对象,那么绑定的Handler就会永远处于等待状态,自然而然就不会打印出”当前子线程“信息了。这也是为什么我们要使用HandlerThread这个特殊的线程,因为使用这个,我们不必关心多线程会混乱,Looper会为空等一系列问题,只要去关心我们要实现的逻辑就行了。
好了,现在做一下简单的总结吧。
小结:
1. Handler与哪个线程的Looper相关联,那么它的消息处理逻辑就在与之相关的线程中执行,相应的消息的走向也就在相关联的MessageQueue中。(最常见的就是Handler与主线程关联,那么接收Looper回传的消息后的逻辑就会在主线程中执行)
2. 当主线程中需要与子线程进行通信时(比如将耗时操作放在子线程中),建议使用HandlerThread。同时要注意,千万不要去重写它的run方法。
二、一个主线程与子线程互相通信的例子
知识点都说完了。下面我们来写一个具体的例子实践一下吧。新建一个项目,修改它的MainActivity代码,如下:
package com.example.handldertest; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.widget.TextView; public class ThreadHandlerActivity extends Activity{ private TextView tv; private Handler mHandler;//与子线程关联的Handler private Handler handler;//与主线程关联的Handler protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tv = new TextView(this); tv.setText("Handler实验"); setContentView(tv); //实例化一个特殊的线程HandlerThread,必须给其指定一个名字 HandlerThread thread = new HandlerThread("handler thread"); thread.start();//千万不要忘记开启这个线程 //将mHandler与thread相关联 mHandler = new Handler(thread.getLooper()){ public void handleMessage(android.os.Message msg) { Log.d("我是子线程----->", Thread.currentThread()+""); handler.sendEmptyMessage(1);//发送消息给主线程 }; }; handler = new Handler(){ public void handleMessage(android.os.Message msg) { Log.d("我是主线程----->", Thread.currentThread()+""); mHandler.sendEmptyMessage(1);//发送消息给子线程 }; }; mHandler.sendEmptyMessage(1);//发送消息 handler.sendEmptyMessage(1);//发送消息 } }
注释很详细,不解释 了。运行程序,结果如下:
这样子,就会一直循环下去,轮流打印出主线程和子线程。
Android主线程、子线程通信(Thread+handler)
Android是基于Java的,所以也分主线程,子线程!
主线程:实现业务逻辑、UI绘制更新、各子线程串连,类似于将军;
子线程:完成耗时(联网取数据、SD卡数据加载、后台长时间运行)操作,类似于小兵;
一、子线程向主线程发消息(Thread+handler):
1、主线程中定义Handler:
Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 0: //do something,refresh UI; break; default: break; } } };
2、子线程处理完耗时操作之后发消息给主线程,更新UI:
mHandler.sendEmptyMessage(0);
这样在子线程与主线程任务分工的条件下完成了消息交互;
二、主线程向子线程发送消息(Thread+handler):
主线程碰到耗时操作要子线程完成,此时发通知给子线程,操作步骤如下:
1、子线程中定义Handler,Handler定义在哪个线程中,就跟那个线程绑定,在线程中绑定Handler需要调用Looper.prepare();方法,主线程中不调用是因为主线程默认帮你调用了;
public class LoopThread implements Runnable { public Handler mHandler = null; @Override public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { String result = NetUtil.getJsonContent("北京"); //完成了获取北京天气的操作; Log.i("test", "handler"+result); } }; Looper.loop(); } }
其中Looper.prepare();和Looper.loop();维护了一个消息队列,等待消息注入并在子线程中执行;
2、主线程中这样调用:
lThread.mHandler.sendEmptyMessage(0);
主线程向子线程发消息,让子线程执行指定的操作,在Android中还有一种方法,即:HandlerThread,看下面的例子:
HandlerThread handlerThread = new HandlerThread("jerome"); handlerThread.start(); /** * 这里要将HandlerThread创建的looper传递给threadHandler,即完成绑定; */ threadHandler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 0: 这儿可以做耗时的操作; Log.i("jerome", "hello,I am sub thread"); break; default: break; } } };
相关文章
- 王者荣耀S38赛季有什么更新 12-25
- 王者荣耀S38赛季有什么更新 王者荣耀S38赛季更新内容介绍 12-25
- 世界之外12.25有什么更新 世界之外12月25日更新内容介绍 12-25
- 光遇12.25红石碎片在哪里 光遇12月25日红石碎片位置攻略 12-25
- 奇迹暖暖绚光引途第二天怎么玩 绚光引途day2庆祝之舞搭配攻略 12-25
- 无限暖暖拍照打卡位置在哪里 无限暖暖世界巡游位置全攻略 12-25