Handler线程间通信实例
1、Handler线程切换原理
由于工作线程与主线程共享地址空间,即Handler实例对象mHandler位于线程间共享的内存堆上,工作线程与主线程都能直接使用该对象,只需要注意多线程的同步问题。
工作线程通过mHandler向其成员变量MessageQueue中添加Message,主线程一直处于loop()方法内,当收到新的 Message 时按照一定规则分发给相应的 handleMessage() 方法来处理。所以说,Handler 消息机制用于同进程的线程间通信,其核心是线程间共享内存空间。
图解:
- Handler 通过sendMessage() 发送Message 到MessageQueue 队列;
- Looper 通过loop(),不断提取出达到触发条件的Message,并将Message 交给target 来处理;
- 经过dispatchMessage() 后,交回给Handler 的handleMessage() 来进行相应地处理。
- 将Message 加入MessageQueue 时,处往管道写入字符,可以会唤醒loop 线程;如果MessageQueue 中没有Message,并处于Idle 状态,则会执行IdelHandler 接口中的方法,往往用于做一些清理性地工作。
下面是以前的理解,其实一句话:由于工作线程与主线程共享地址空间,即Handler实例对象 mHandler位于线程间共享的内存堆上,工作线程与主线程都能直接使用该对象
- 当在A线程中创建handler的时候,同时创建了MessageQueue 与Looper
你在子线程向主线程发消息,这个mMainHandler 是在主线程初始化的,意思是用了主线程的Looper.loop(),Looper 在主线程中调用loop 进入一个无限的for 循环从MessageQueue 中取消息
- 子线程调用mMainHandler 发送一个message
通过msg.target.dispatchMessage(msg) 将message 插入到mMainHandler 对应的MessageQueue 中
- Looper 发现有message 插入到MessageQueue 中,便取出message 执行相应的逻辑
因为Looper.loop()是在主线程中启动的,所以则回到了主线程中,达到了切换线程的目的
public class MainActivity extends AppCompatActivity { public static final String TAG = "MainActivity"; private Handler mThreadHandler; private Handler mMainHandler = new Handler(new Handler.Callback() { // 主线程接收到子线程发送的消息 @Override public boolean handleMessage(Message msg) { if (msg.obj != null) { Log.e(TAG, msg.obj.toString()); mThreadReplyText.setText("收到子线程回复:" + msg.obj.toString()); } return true; } }); private EditText mMainEditText; private TextView mThreadReplyText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMainEditText = findViewById(R.id.main_edit_text); mThreadReplyText = findViewById(R.id.thread_reply_text); new Thread(new Runnable() { @Override public void run() { Looper.prepare(); mThreadHandler = new Handler(new Handler.Callback() { // 子线程接收到主线程发送的消息并回复 @Override public boolean handleMessage(Message msg) { if (msg.obj != null) { Message reply = Message.obtain(); reply.obj = "主线程说\"" + msg.obj.toString()+"\""; // 在子线程中使用主线程中的Handler对象的引用向主线程发送消息 mMainHandler.sendMessage(reply); } return true; } }); Looper.loop(); } }).start(); } public void onClick(View view) { switch (view.getId()) { case R.id.main_send_button: Message message = Message.obtain(); message.obj = mMainEditText.getText(); // 主线程向子线程发送消息:在主线程中使用子线程中的Handler对象的引用向子线程发送消息 if (mThreadHandler != null) { mThreadHandler.sendMessage(message); } else { while (mThreadHandler == null) { Log.e(TAG, "子线程还没有完成ThreadHandler的创建"); if (mThreadHandler != null) { Log.e(TAG, "ThreadHandler创建完成!"); mThreadHandler.sendMessage(message); } } } break; } } }
2、实例
2.1需求背景
在开发中,socket网络通信需要放到子线程做
2.2、代码实现
1)定义一个SendHandler类继承Handler
public static SendHandler mHandler; private HandlerThread mHandlerThread;
public class SendHandler extends Handler{
//子线程接收到主线程发送的消息 private String TAG = "lemon"; public SendHandler(Looper looper) { super(looper); } public void handleMessage(Message msg){ Log.i(TAG, "handlermessage =" + msg.what); try { if (mSocket != null && mSocket.isConnected()) { if (pw != null) { Log.i(TAG, "handlermessage in newThread is " + msg.what); String res = String.valueOf(msg.what); Bundle bundle = msg.getData(); Object velocityX = bundle.get("velocityX"); Object velocityY = bundle.get("velocityY"); Log.i(TAG, "handlermessage in newThread x is " + bundle.get("velocityX") + ",y is " + bundle.get("velocityY")); if (velocityX != null && velocityY != null) { res = res + " " + bundle.get("velocityX") + " " + bundle.get("velocityY"); } pw.println(res); pw.flush(); } } } catch (Exception e) { Log.i(TAG, "Exception is" + e); } } }
2)Handler线程和Hadnler对象在初始化的时候实例化
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_touch); mHandlerThread = new HandlerThread("TrackPad_thread"); mHandlerThread.start(); mHandler = new SendHandler(mHandlerThread.getLooper()); }
3)主线程调用mHandler.sendMessage到子线程handleMessae处理
mHandler.sendMessage(message)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)