5年前在学校错过一次Android,等到现在才开始补救。但愿还不晚。
本文以一个更新UI的例子来讲述Looper和Handler如何配合使用。
本文所写范例仅供研究、学习之用,不喜勿喷,敬请谅解!
UI目标:
1. 用户滑动seekBar至任意值(例如X%),然后点击UPDATE按钮,等待2s后progressBar更新到seekBar的相同值X%,底部editText_log中显示出“set progress=X”的日志。
多线程设计
据说,不可以做任何阻塞UI的工作!
本文设计一共两个线程:UI线程 + Looper线程。
两个线程的职责分别是:
UI线程:UI启动、处理UI消息、更新UI。
Looper线程:处理耗时2s的复杂任务,处理完毕后返回消息给UI线程。
本文设计了两个Handler,分别是:MainHandler + LooperHandler。
两个Handler的职责分别是:
MainHandler:
1. 由Looper线程调用,发送更新UI的消息到UI线程;
2. 由UI线程处理Message时被调用处理UI更新的Message,更新UI。
LooperHandler:
1. 由UI线程调用,发送处理任务的消息给Looper线程;
2. 在Looper线程中调用,处理从UI线程发来的耗时任务。
两个线程的时序图如下所示:
部分实现代码如下:
package com.frank.simplehandler_0611;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.SeekBar;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "SimpleHandler_0611";
public static class MyLooperHandler extends Handler {
private MainHandler mMainHandler = null;
private static final int MSG_SET_PROGRESS = 0;
public MyLooperHandler(Looper looper, MainHandler mainHandler) {
super(looper);
mMainHandler = mainHandler;
}
/****
* handleMessage called in LooperThread.
*
* @param msg message object.
*/
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_SET_PROGRESS) {
Log.d(TAG, "MyLooperHandler.handleMessage msg.what=" + msg.what + " arg1=" + msg.arg1);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mMainHandler.updateProgress(msg.arg1);
}
}
/***
* setProgress called in Main Thread.
*
* @param arg0 new seek bar value.
*/
public void setProgress(int arg0) {
Log.d(TAG, "MyLooperHandler.setProgress arg0=" + arg0);
Message msg = Message.obtain();
msg.what = MSG_SET_PROGRESS;
msg.arg1 = arg0;
sendMessage(msg);
}
}
public class MainHandler extends Handler {
public static final int MSG_SET_PROGRESS_MAIN = 1;
public MainHandler(Looper looper) {
super(looper);
}
/***
* handleMessage called in UI thread
*
* @param msg
*/
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_SET_PROGRESS_MAIN) {
int prg = msg.arg1;
String content = (String) msg.obj;
Log.d(TAG, "MainHandler.handleMessage msg.what=" + msg.what + " progress=" + prg + " content=" + content);
if (progressBar_01 != null) {
progressBar_01.setProgress(prg);
}
if (editText_log != null) {
editText_log.append(content);
}
}
}
/***
* updateProgress called in MyLooperThread
* This message is send to Main Thread for UI update
*
* @param arg
*/
public void updateProgress(int arg) {
Log.d(TAG, "MainHandler.updateProgress arg=" + arg);
Message msg = Message.obtain();
msg.what = MSG_SET_PROGRESS_MAIN;
msg.arg1 = arg;
msg.obj = String.format("set progress=%d", arg);
sendMessage(msg);
}
}
MyLooperHandler myLooperHandler = null;
MainHandler mainHandler = null;
EditText editText_log = null;
SeekBar seekBar_01 = null;
ProgressBar progressBar_01 = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "MainActivity.onCreate UI thread running...");
// UI components initialize
editText_log = (EditText) findViewById(R.id.editText_log);
seekBar_01 = (SeekBar) findViewById(R.id.seekBar_01);
progressBar_01 = (ProgressBar) findViewById(R.id.progressBar_01);
findViewById(R.id.button_action).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (seekBar_01 != null) {
int prg = seekBar_01.getProgress();
if (myLooperHandler != null) {
Log.d(TAG, "MainActivity.onCreate.button_action.onClick prg=" + prg);
myLooperHandler.setProgress(prg);
}
}
}
});
MyLooperThread myLooperThread = new MyLooperThread("Worker");
myLooperThread.start();
mainHandler = new MainHandler(getMainLooper());
myLooperHandler = new MyLooperHandler(myLooperThread.getLooper(), mainHandler);
}
}
代码工程详见:
https://github.com/wangqunfeng/AndroidDemos/tree/master/apps/SimpleHandler_0611