概述
在Android开发中离不开子线程,网络请求等耗时的操作一般都需要放在子线程中执行,那么开启子线程的方法有哪些呢?下面做个总结。
- 直接使用Thread类。
- 使用Runnable和Thread。
- 使用Runnable和线程池。
- 使用AsyncTask。
- 使用HandlerThread。
- 使用IntentService。
下面对各种方法的用法及原理做详细的解释。
直接使用Thread类开启子线程
这是最简单开启子线程的方法,也是最本质的方法,其他开启子线程的方法都是在此方法基础上的扩展。
一,使用示例如下:
new Thread(){
@Override
public void run() {
super.run();
}
}.start();
使用Thread类开启子线程一共分为三步:
- 创建Thread的子类对象。
- 重写run方法。
- 调用start方法开启子线程。
此时就开启了子线程,run方法执行在子线程中。
二,使用Thread类开启子线程的原理
子线程的开启重要的是start方法,start方法的原码是:
public synchronized void start() {
checkNotStarted();
hasBeenStarted = true;
nativeCreate(this, stackSize, daemon);
}
很明显开启子线程的本质是nativeCreate方法,而这个方法是native方法,所以开启线程是底层C语言实现的。
使用Runnable接口和Thread类开启子线程
此种方法使用的是Thread类的有参构造创建线程对象。
一,使用示例如下
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
此种方法是将Runnable接口的子类对象当作Thread构造方法的参数。重写Runnable的run方法,run方法就执行在子线程中。
二,使用Runnable接口和Thread类开启子线程的原理
查看Runnable接口的原码会发现:里面只有一个抽象的方法,所以Runnable在本质上与开启线程没有任何关系。只是Runnable的run方法在子线程中被调用了。
下面重点看一下Thread类的这个有参构造,原码是:
public Thread(Runnable runnable) {
create(null, runnable, null, 0);
}
继续看create方法,方法的核心代码是:
private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
Thread currentThread = Thread.currentThread();
if (group == null) {
group = currentThread.getThreadGroup();
}
this.group = group;
this.target = runnable;
this.stackSize = stackSize;
this.priority = currentThread.getPriority();
this.group.addThread(this);
}
分析:这个方法的主要功能是给Thread类的字段赋值,与Runnable有关的是:this.target = runnable;
下面我们再看一下Thread类的run方法的源码:
public void run() {
if (target != null) {
target.run();
}
}
此时就很明显了,Runnable的run方法在Thread的run方法中被调用,所以Runnable的run方法执行在子线程中。
注:由以上的分析可知,Runnable接口对线程的开启没有任何作用,只是它的run方法在子线程中调用了而已。
三,出现Runnable接口的作用
单独使用Thread类就可以开启子线程了,为什么还要出现Runnable接口呢?
因为Thread是一个抽象类,当一个类已经有了父类时就不能再继承Thread了,而这个类可以实现Runnable接口,然后这个类重写的run方法就可以执行在子线程中了。
使用Runnable和线程池开启子线程
线程池中维护的是线程,所以使用线程池开启子线程本质还是使用的Thread类,因为用到的类和方法不同,所以单独列了出来。
使用示例如下:
ExecutorService threadPool = Executors.newFixedThreadPool(5);
threadPool.submit(new Runnable() {
@Override
public void run() {
}
});
此时的run方法执行在子线程中。
注:在后面会单独讲述线程池,所以这里就不讲解此种方式开启线程的原理了。
使用AsyncTask开启子线程
AsyncTask是一个类,里面封装了Thread和Handler,开启了子线程,实现了线程间的通讯。
使用示例
new AsyncTask<String, Integer, Boolean>() {
protected void onPreExecute() {
super.onPreExecute();
}
protected Boolean doInBackground(String... params) {
publishProgress(1);
return null;
}
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
}
}.execute("abc");
分析:
1,创建一个AsyncTask对象,这儿有三个泛型,泛型类型和下面的方法都是相对应的
泛型一:对应的execute方法中的参数类型,execute里面的参数即是doInBackground方法中的参数,这个是可变参数。
泛型二:对应的是onProgressUpdate方法中的参数,publishProgress方法中的参数即是onProgressUpdate方法中的参数。
泛型三:doInBackground方法的返回值类型。doInBackground的返回值即是onPostExecute的参数。
2,创建AsyncTask对象要重写四个方法:
方法一:onPreExecute。这个方法执行在UI线程,且在doInBackground执行前执行,常做一些耗时操作前的初始化。
方法二:doInBackground。这个方法是关键,它执行在子线程,常做一些耗时的操作。
方法三:onProgressUpdate。这个方法运行在UI线程,可以更改进度。但必须在doInBackground中调用publishProgress方法。
方法四:onPostExecute。这个方法在doInBackground执行后执行,类似于Handler中的handleMessage方法。
3,调用AsyncTask的execute方法
这个方法是代码执行的入口,重写的方法都是被这个方法直接或间接的调用。
使用HandlerThread开启子线程
HandlerThread是一个类,他继承了Thread类,并重写了run方法,在run方法中使用Looper创建了消息队列,此时就可以在这种Thread中直接使用Handler了。
一,使用示例
HandlerThread mHandlerThread = new HandlerThread("MyHandlerThread");;
mHandlerThread.start();//开启子线程
Handler mhandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//这个方法执行在子线程,里面可以做耗时的操作
}
};
mhandler.sendEmptyMessage(10);
分析:由于mHandlerThread.getLooper()得到的是子线程中的looper对象,所以mhandler是子线程中的handler,所以handleMessage方法也执行在子线程。
二,使用HandlerThread开启子线程的原理
HandlerThread类继承了Thread类,并重写了run方法。下面我们看一下这个run方法的源码:
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
分析:里面调用了两个重要的方法,Looper.prepare()和Looper.loop()。由handler的学习我们知道,Looper.prepare()中创建了Looper对象和MessageQueue对象,Looper.loop()中开启一个死循环不断的从消息队列中取出消息并处理。
注:这两个方法都是在子线程中被调用,所以创建的Looper对象属于子线程中的,且handler的handleMessage也在子线程中被执行。
使用intentService开启子线程
IntentService这是一个特殊的服务,它是一个抽象类,继承了Service。里面封装了HandlerThread和Handler,本质等效于在服务中开启一个子线程。而且这个服务可以在子线程执行结束后自动关闭。
一,使用示例
public static class MyIntentService extends IntentService{
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
//这个方法执行在子线程,里面可以做耗时的操作
}
}
分析:自定义一个类,继承intentService,然后重写onHandleIntent方法,这个方法执行在子线程。
二,使用intentService开启子线程的原理
intentService类继承了service,并重写了onCreate方法。下面看一下onCreate方法的原码:
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
里面使用HandlerThread类开启子线程。ServiceHandler是Handler的子类,并重写了handleMessage方法。下面看一下ServiceHandler类的原码:
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
分析:从代码中可以看到onHandleIntent方法执行在子线程。
下面继续看intentService类的onStart方法的源码:
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
这个方法中使用 mServiceHandler.sendMessage(msg)发送了消息,此时ServiceHandler 类的handleMessage方法就会被调用。而handleMessage方法执行在子线程,所以onHandleIntent方法也执行在子线程。