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)

 

posted @   余黎明  阅读(115)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示