概述

在Android开发中离不开子线程,网络请求等耗时的操作一般都需要放在子线程中执行,那么开启子线程的方法有哪些呢?下面做个总结。

  1. 直接使用Thread类。
  2. 使用Runnable和Thread。
  3. 使用Runnable和线程池。
  4. 使用AsyncTask。
  5. 使用HandlerThread。
  6. 使用IntentService。

下面对各种方法的用法及原理做详细的解释。

直接使用Thread类开启子线程

这是最简单开启子线程的方法,也是最本质的方法,其他开启子线程的方法都是在此方法基础上的扩展。

一,使用示例如下:

new Thread(){
    @Override
    public void run() {
        super.run();
    }
}.start();

使用Thread类开启子线程一共分为三步:

  1. 创建Thread的子类对象。
  2. 重写run方法。
  3. 调用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方法也执行在子线程。