Runnable
代码一个可执行的命令,通常用于在另一个线程执行一段代码。定义了一个方法run(),要在线程执行的代码就放在run()里面,当线程启动时,会调用run(),run()执行完毕之后,线程就结束了。Runnable时一个抽象类,可以从字面理解,凡是要在一个不同线程执行一段代码的类都需要继承Runnable。
像下面这样的用法实际上没有什么意义:
new Runnable() {
@Override
public void run() {
Log.d(LOG_TAG, "I'm thread" + Thread.currentThread().getName());
}
}.run();
等价于
Log.d(LOG_TAG, "I'm thread" + Thread.currentThread().getName());
new Runnable不会创建一个新的线程。一般的用法是:用Runnable包装一段代码,作为一个task,然后延迟执行或post到另一个线程执行,或作为一个callback。Runnable可以理解为一个task。
Thread
顾名思义,代表一个线程。最常用的方法有:start()、run()(重载)、setPriority()。
在java中,有两种方式实现在一个新的线程执行一段代码。
1 实现一个类,继承自Thread,重载run()方法
class MyThread extends Thread {
@Override
public void run() {
// 线程的主体代码,一般是一个耗时的任务
}
}
MyThread t = new MyThread();
// 启动线程,并调用run()
t.start();
2 创建一个新的Thread,并传给它一个Runnable
new Thread() {
@Override
public void run() {
// 线程的主体代码,一般是一个耗时的任务
Log.d(LOG_TAG, "I'm thread " + Thread.currentThread().getName());
}
}.start();
或者
new Thread(new Runnable() {
@Override
public void run() {
Log.d(LOG_TAG, "I'm thread " + Thread.currentThread().getName());
}
}).start();
无论是哪一种,最终都需要调用start方法来启动新线程。
Looper
是一个用来执行消息循环的类。默认情况下,线程是没有消息循环的。可以调用prepare()来创建一个,然后调用loop()开始处理消息。
典型的实现Looper Thread的例子如下:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
该线程启动之后,会在后台执行消息循环,等待消息。我们可以通过Handler来与消息循环通信。在上面的例子中,Handler初始化时会获取当前线程的Looper,即Handler默认绑定到当前线程的Looper,我们也可以在初始化时传一个Looper给它。Looper执行时,会把收到的消息分发给相关的Handler(也就是调用相关的Handler的handleMessage。一个Looper可以有多个Handler。但一个Handler只能对应一个Looper。
常用的接口
- static Looper getMainLooper():返回应用的main looper,它位于应用的主线程
- Thread getThread():获取与该looper相关的线程
- static Looper myLooper():返回当前线程的Looper对象
- static void prepare():为当前线程创建Looper对象
- static void prepareMainLooper():为当前线程创建Looper,并标识它为应用的main looper。
Handler
Handler用来发送和处理消息,以及执行与线程的消息队列相关的可执行对象。
一般提到Handler,都会首先说它是一种用来不阻塞主线程,更新UI的机制。当我们需要在其它线程更新UI的时候就可以使用Handler。通过在主线程new Handler(绑定到主线程的Looper),在其它线程获取该Handler,并通过它发送消息到主线程,让主线程更新UI。
下面是一个通过Handler实现一个类似于秒表的功能:
public class MainActivity extends AppCompatActivity {
private Button mButton;
private final String LOG_TAG = "thread_test";
private final int UPDATE_TIME = 0;
private TextView mSeconds = null;
private MainHandler mHandler = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建Handler
mHandler = new MainHandler();
mButton = (Button) findViewById(R.id.button);
mSeconds = (TextView) findViewById(R.id.textView);
mButton.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
int seconds = 0;
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 通知UI更新时间,1秒通知一次,也就是一秒更新一次
Message msg = new Message();
seconds++;
msg.what = UPDATE_TIME;
msg.obj = new Integer(seconds);
mHandler.sendMessage(msg);
}
}
}.start();
}
});
}
class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TIME:
mSeconds.setText(String.format("%d seconds", (Integer)msg.obj));
break;
default:
break;
}
}
}
}
每一个Handler实例必须绑定到一个线程和线程的消息队列。默认绑定到创建Handler实例的线程,我们也可以给Handler构造函数传递一个Looper。
Handle的两个主要的用法:
- 延迟处理消息或执行Runnable对象
- 在另一个线程插入一个动作等待执行
发送一个消息可以用下面这些方法:
post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long)
处理可以用过消息让另一个线程执行一个任务,还可以直接post给它一个Runnable对象,加入到它的执行队列。
mHandler.post(new Runnable() {
@Override
public void run() {
Log.d(LOG_TAG, "post runnable " + Thread.currentThread().getName());
}
});
当启动应用时,会创建一个进程,它的主线程专门负责执行管理顶层应用对象和所有的窗口的消息队列。我们可以创建自己的线程,并通过Handler与主线程进行通信。
Thread, Looper, Handler之间的关系
Thread可以独立存在,用于执行一个任务。
Looper必须绑定到一个线程。
一个Looper可以有多个Handler。
应用
下面是一个通过后台线程获取网络数据的例子
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
/**
* Created by uvsjoh on 2016/3/16.
*/
public class DataFetcher extends Thread{
static private MyHandler mHandler;
static private final int WORKER_MSG_FETCH_DATA = 0;
DataFetcher() {
//run();
}
public void fetchData() {
mHandler.sendEmptyMessage(WORKER_MSG_FETCH_DATA);
}
static private void doFetchData() {
try {
URL url = new URL("http://www.baidu.com/");
URLConnection conn = url.openConnection();
conn.connect();
String result = "";
BufferedReader in = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
}
public void run() {
Looper.prepare();
mHandler = new MyHandler();
Looper.loop();
}
static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// process incoming messages here
switch (msg.what) {
case WORKER_MSG_FETCH_DATA:
doFetchData();
break;
default:
break;
}
}
};
}
外部通过如下方式调用:
if (mDataFetcher == null) {
mDataFetcher = new DataFetcher();
mDataFetcher.start();
}
mDataFetcher.fetchData();