昨天群里一实习生问了我关于 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;
}
}
};
}
上述代码大致构思是在主页面即 HandlerActivity 下点击 satrt working 按钮之后,初始化一个整形数值来分工4个异化操
作,执行到 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>
源码目录附带了博主写的个别自定义控件,欢迎 clone 和 pull !
源码地址:https://github.com/Mr-Jiang/EngineerJspCustomView
git url:https://github.com/Mr-Jiang/EngineerJspCustomView.git