昨天群里一实习生问了我关于 Handler 主线程跟子线程 Thread 的通信与交互的问题,我当时就跟他解释了一丢丢,然后他说很
笼统,就说给一个demo看下,介于反正都是要写,那就写一篇博客,就可以解决在遇到类似问题的小伙所面临的问题了,开篇前
新手可以打开工具一起敲,老手没事吐吐槽就行了,哈哈,OK,我们开始
首先介绍一下 Looper ,Looper 作为 MessageQueue 的管理制,在一个主线程中一个 MessageQueue 只有一个 Looper 管理整
个 MessageQueue,但是一个 MessageQueue 可以有多个 Message ,多个 Handler ,Looper 分别需要 Message 和 Handler 从
MessageQueue 中存储或者拿出消息执行相应的任务操作
即:Looper 数目1 它是 MessageQueue 的管理者
MessageQueue 数目1 即消息队列
Message 数目多 即消息媒介
Handler 数目多 即搬运工人
Demo:
package com.example.engineerjspcustomview; import custom.thread.CustomThread; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class HandlerActivity extends Activity { private TextView handler_text, thread_text; private Button start_working; private CustomThread mThread; private int id = 0; private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case 0:// show handler status switch (msg.arg1) { case 0: handler_text.setText("Handler require Thread calc :1+1"); HandRequireThreadCalc(1, 1); break; case 1: handler_text.setText("Handler require Thread calc :1+2"); HandRequireThreadCalc(1, 2); break; case 2: handler_text.setText("Handler require Thread calc :1+3"); HandRequireThreadCalc(1, 3); break; case 3: handler_text.setText("Handler require Thread calc :1+4"); HandRequireThreadCalc(1, 4); break; } break; case 1:// show thread status thread_text.setText((String) msg.obj); isLossFoucse = true; setBtnLossFoucse(); break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.handler_activity); initView(); mThread = new CustomThread(mHandler); mThread.start(); } private boolean isLossFoucse = true; private void initView() { handler_text = (TextView) findViewById(R.id.show_handler_status); thread_text = (TextView) findViewById(R.id.show_thread_status); start_working = (Button) findViewById(R.id.start_working); start_working.setOnClickListener(mListener); } private void setBtnLossFoucse() { Log.v("Engineer-Jsp", "setBtnLossFoucse() Enabled:" + isLossFoucse); start_working.setEnabled(isLossFoucse); } private void HandRequireThreadCalc(int j, int k) { Message msg = mThread.thread_handler.obtainMessage(); msg.what = 0; msg.arg1 = j; msg.arg2 = k; mThread.thread_handler.sendMessage(msg); } private OnClickListener mListener = new OnClickListener() { @Override public void onClick(View arg0) { switch (id) { case 0: mHandler.obtainMessage(0, 0, 0).sendToTarget(); id++; isLossFoucse = false; setBtnLossFoucse(); break; case 1: mHandler.obtainMessage(0, 1, 0).sendToTarget(); id++; isLossFoucse = false; setBtnLossFoucse(); break; case 2: mHandler.obtainMessage(0, 2, 0).sendToTarget(); id++; isLossFoucse = false; setBtnLossFoucse(); break; case 3: mHandler.obtainMessage(0, 3, 0).sendToTarget(); id++; if (id >= 3) { id = 0; } isLossFoucse = false; setBtnLossFoucse(); break; } } }; }
作,执行到 case 3 的时候会重新初始化,从最初的值开始循环,在点击的过程中需要子线程即 CustomThread 完成之后才能让
它可以点击,这样做可以避免在大型的消耗操作时,避免子线程 即 CustomThread 的臃肿,导致出现异常,需要在子线程 即
CustomThread 完成之后重新获得可点击事件,这个结果需要由 主页面即 HandlerActivity 的 mHandler 对象来发送给自己,并且更新到UI上,
因为在实例化 子线程 即 CustomThread 的时候,我传了一个 mHandler 对象过去,为的就是方便演示结果显示在主UI上,而 子线程 即
CustomThread 也定义了一个搬运工 即 Handler 对象 thread_handler ,它主要负责将 主页面即 HandlerActivity 的计算要求发送给到自己的
消息队列中,当子线程 即 CustomThread 拿到结果之后 调用 Calc 函数进行计算,并且将结果推送到自己的消息队列中,再由主页面即
HandlerActivity 在初始化时传过来的 mHandler 对象来发送到主界面 UI 进行计算结果的显示,以及更新 start working 按钮的可点击事件
大致的构思就这样
package custom.thread; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; public class CustomThread extends Thread { public Handler thread_handler; private Handler handler; public CustomThread(Handler h) { this.handler = h; } @Override public void run() { super.run(); Looper.prepare(); thread_handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 0: int j = msg.arg1; int k = msg.arg2; Calc(j, k); break; case 1: String sendStr = (String) msg.obj; if (handler != null) { Message msg1 = handler.obtainMessage(); msg1.what = 1; msg1.obj = sendStr; handler.sendMessage(msg1); } break; } } }; Looper.loop(); } private void Calc(int j, int k) { int result = j + k; String results = "Thread Calc from Handler test result:" + j + "+" + k + "=" + result; Log.v("Engineer-Jsp", results); Message msg = thread_handler.obtainMessage(); msg.what = 1; msg.obj = results; thread_handler.sendMessage(msg); } }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" > <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" > <TextView android:id="@+id/show_handler_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" android:textColor="#000000" /> <Button android:id="@+id/start_working" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/show_handler_status" android:layout_margin="5dp" android:text="@string/start_working" /> <TextView android:id="@+id/show_thread_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/start_working" android:layout_margin="5dp" android:textColor="#000000" /> </RelativeLayout> </RelativeLayout>
源码地址:https://github.com/Mr-Jiang/EngineerJspCustomView
git url:https://github.com/Mr-Jiang/EngineerJspCustomView.git