昨天羣裏一實習生問了我關於 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