Android的线程和线程池


主要有哪些类型:


(1)Thread。

(2)AsyncTask。(线程池,Handler)(更新UI)

(3)IntentService。(Thread线程,HandlerThread)(后台服务,不容易被杀死)

(4)HandlerThread。(Thread线程,Handler)(消息循环)


注意点:


(1)线程开太多的时候,系统会通过时间片轮转的方式调度每个线程。

(2)频繁创建和销毁线程是要消耗很大的资源的,正确的做法是采用线程池。

(3)用Executor来派生特定类型的线程池。

(4)主线程就是UI线程,只有一个。不能执行耗时(网络请求,I/O操作)的任务。

从Android3.0开始系统要求网络访问必须在子线程中进行,否则网络访将会失败并抛出NetworkOnMainThreadException这个异常。



一、AsyncTask


1、封装了Thread 和 Handler:


它是轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的最终结果传递给主线程并在主线程中更新UI。


2、并不适合进行特别耗时的后台任务:

对于特别耗时的后台任务来说,建议使用线程池。

3、注意点介绍:


(1)源码位置: sources\android\os\AsyncTask.java
(2)AsyncTask是一个抽象的泛型类,提供了 Params(参数的类型)、Progress(后台任务执行速度的类型)、Result(后台任务返回结果的类型)这三个泛型参数。不需要的时候可以用Void来代替。
public abstract class AsyncTask<Params, Progress, Result>
(3)AsyncTask包括四个核心方法:
onPreExecute():主线程,异步任务执行之前调用,做准备工作。
doInBackground(Params...params):线程池,用于执行异步任务,参数params表示异步任务的输入参数,需要返回计算结果给onPostExecute方法,doInBackground方法中可以通过调用publishProgress方法来更新任务的进度,而publishProgress中又会调用onProgressUpdate方法。
onProgressUpdate(Progress...values):主线程,当后台任务执行进度发生变化时在publishProgress方法中调用。
onPostExecute(Result  result):主线程,当异步任务执行后被调用,其中result参数是doInBackground方法的返回值。
onCncelled():主线程,当异步任务被取消时调用,这个时候onPostExecute方法就不会被调用啦。

这里一直提各种主线程,线程池什么的,意思是这样的:
只有doInBackground方法是在线程池中进行的,意味着在其他的方法中,都是可以直接进行UI操作的!!!!!

下面是几个AsyncTask使用的注意点:

(4)AsyncTask的类必须在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程,
当然这个过程在Android4.1及以上版本中已经被系统自动完成,
在Android5.0的源码中,可以查看ActivityThread的main方法,它会调用AsyncTask的init方法,这就满足了AsyncTask的类必须在主线程中加载的条件。
(5)AsyncTask的对象必须在主线程中创建。
(6)execute方法必须在UI线程(主线程)调用。
(7)不要在程序中直接调用 onPreExecute()、onPostExecute()、doInBackground和onProgressUpdate方法。
(8)一个AsyncTask对象只能执行一次,即只能调用execute方法一次,否则运行时会出现异常。
(9)在Android1.6以前,AsyncTask是串行执行任务的,Android1.6的时候AsyncTask开始采用线程池(Executor)处理并行任务,但从Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程串行执行任务。
尽管如此,在Android3.0以及后续版本中,我们仍然可以通过AsyncTask的executeOnExecutor方法来并行的执行任务。


举个例子吧:模拟后台下载的小例子。
(在Java中 ... 表示参数的数量不定,它是一种数组型参数)
package com.ryg.my;

import java.net.URL;

import android.os.AsyncTask;

public class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {

	/*
	 * 在线程池中执行。
	 * */
	@Override
	protected Long doInBackground(URL... urls) {
		int count = urls.length;
		long totalSize = 0;

		for (int i = 0; i < count; i++) {
			// 这里Downloader是假的
			totalSize += Downloader.downloadFile(urls[i]);
			// 更新进度条:它里面会调用onProgressUpdate方法。
			publishProgress((int) ((i / (float)count) * 100));
			// 是否取消后台异步操作?
			// 这个isCancelled是AsyncTask类中的一个方法。
			if(isCancelled()){
				break;
			}
		}
		return totalSize;
	}
	
	/*
	 * 在doInBackground方法中的publishProgress方法中调用:
	 * */
	@Override
	protected void onProgressUpdate(Integer... progress) {
		// 这个方法是假的:
		setProgressPercent(progress[0]);
	}
	
	/*
	 * 异步操作doInBackground后调用:
	 * */
	@Override
	protected void onPostExecute(Long result) {
		// 这也是个假的:
		showDialog("Download " + result + " bytes.");
	}
}
那具体的调用呢这样写:
new DownloadFilesTase().execute(url1, url2, url3);
或者可以这样写:
DownloadFilesTase task = new DownloadFilesTase();
task.execute(url1, url2, url3);
注意啦注意啦:传入的参数是传递给execute的,不是给DownloadFiles的!!!


4、源码分析


(1)源码位置: sources\android\os\AsyncTask.java
(2)AsyncTask执行时要调用它的 execute方法:
    /**
     * Executes the task with the specified parameters. The task returns
     * itself (this) so that the caller can keep a reference to it.
     * 
     * <p>Note: this function schedules the task on a queue for a single background
     * thread or pool of threads depending on the platform version.  When first
     * introduced, AsyncTasks were executed serially on a single background thread.
     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
     * to a pool of threads allowing multiple tasks to operate in parallel. Starting
     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
     * executed on a single thread to avoid common application errors caused
     * by parallel execution.  If you truly want parallel execution, you can use
     * the {@link #executeOnExecutor} version of this method
     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
     * on its use.
     *
     * <p>This method must be invoked on the UI thread.
     *
     * @param params The parameters of the task.
     *
     * @return This instance of AsyncTask.
     *
     * @throws IllegalStateException If {@link #getStatus()} returns either
     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
     *
     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
     * @see #execute(Runnable)
     */
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
注意它的参数params是指输入参数,在实际调用的时候,可以传入多个Params类型的参数。
注意它的返回值是AsyncTask<Params, Progress, Result>,神奇。
可以看到啦,它里面调用的是 executeOnExecutor方法,就是我们刚刚提到的它具体是用串行还是并行的那个方法,
在executeOnExecutor 方法中,第一个参数 sDefaultExecutor是一个线程池Executor对象,这里的sDefaultExecutor代表的是一个串行的线程池,
一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行,这个排队执行的过程后面再进行分析。

那我们去看看executeOnExecutor这个方法吧:

(3)executeOnExecutor(Executor exec, Params... params)方法:
AsyncTask执行状态的声明:初始化mStatus为PENDING未执行状态。
private volatile Status mStatus = Status.PENDING;

/**
 * Indicates the current status of the task. Each status will be set only once
 * during the lifetime of a task.
 */
public enum Status {
    /**
     * Indicates that the task has not been executed yet.
     */
    PENDING,
    /**
     * Indicates that the task is running.
     */
    RUNNING,
    /**
     * Indicates that {@link AsyncTask#onPostExecute} has finished.
     */
    FINISHED,
}
/**
 * Executes the task with the specified parameters. The task returns
 * itself (this) so that the caller can keep a reference to it.
 * 
 * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
 * allow multiple tasks to run in parallel on a pool of threads managed by
 * AsyncTask, however you can also use your own {@link Executor} for custom
 * behavior.
 * 
 * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
 * a thread pool is generally <em>not</em> what one wants, because the order
 * of their operation is not defined.  For example, if these tasks are used
 * to modify any state in common (such as writing a file due to a button click),
 * there are no guarantees on the order of the modifications.
 * Without careful work it is possible in rare cases for the newer version
 * of the data to be over-written by an older one, leading to obscure data
 * loss and stability issues.  Such changes are best
 * executed in serial; to guarantee such work is serialized regardless of
 * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
 *
 * <p>This method must be invoked on the UI thread.
 *
 * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
 *              convenient process-wide thread pool for tasks that are loosely coupled.
 * @param params The parameters of the task.
 *
 * @return This instance of AsyncTask.
 *
 * @throws IllegalStateException If {@link #getStatus()} returns either
 *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
 *
 * @see #execute(Object[])
 */
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
	
	/*
	 * 一个异步任务AsyncTask只能执行一次,
	 * 所以启动一个异步任务时,
	 * 如果它正在执行,抛出异常
	 * 如果它已经执行完毕,抛出异常
	 * */
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    // 修改执行状态
    mStatus = Status.RUNNING;

    /*
     * 在异步任务真正执行前最先执行的就是这个方法了:
     * */
    onPreExecute();

    /*
     * 然后线程池exec开始执行:
     * */
    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

(4)下面我们看看线程池的执行过程:
这里所说的线程池就是刚刚在executeOnExecutor 方法中传递的第一个参数 sDefaultExecutor ,它是一个串行的线程池。
先去看看sDefaultExecutor 是怎么来的:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

private static class SerialExecutor implements Executor {

	/*
	 * 首先系统会把AsyncTask的Params参数也就是输入参数封装为 FutureTask 对象,
	 * 这个FutureTask 是一个并发类,在这里它充当了Runnable 的作用。
	 * */
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    /*
     * 接着这个FutureTask会交给 SerialExecutor 的execute方法去处理,
     * */
    public synchronized void execute(final Runnable r) {
    	/*
    	 * SerialExecutor的execute方法首先会把FutureTask对象插入到任务队了mTasks中,
    	 * */
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                	/*
                	 * 同时当一个AsyncTask任务执行完以后,
                	 * AsyncTask会继续执行其他任务直到所有的任务都被执行为止。
                	 * */
                    scheduleNext();
                }
            }
        });
        /*
         * 如果这个时候没有正在活动的AsyncTask任务,
         * 那么就会调用SerialExecutor的scheduleNext方法来执行下一个AsyncTask任务。
         * */
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);//这个在下面介绍
        }
    }
}

从SerialExecutor的实现我们来分析一下AsyncTask的排队执行过程。
首先系统会把AsyncTask的Params参数也就是输入参数封装为 FutureTask 对象,这个FutureTask 是一个并发类,在这里它充当了Runnable 的作用。
接着这个FutureTask会交给SerialExecutor 的execute方法去处理,
SerialExecutor的execute方法首先会把FutureTask对象插入到任务队了mTasks中,
如果这个时候没有正在活动的AsyncTask任务,那么就会调用SerialExecutor的scheduleNext方法来执行下一个AsyncTask任务。
同时当一个AsyncTask任务执行完以后,AsyncTask会继续执行其他任务直到所有的任务都被执行为止。
所以从上面的分析我们可以看出,AsyncTask是串行执行的。


(5)这时候我们就会发现在SerialExecutor线程池中,真正的异步执行过程是在scheduleNext方法中进行的,
这里面有一个THREAD_POOL_EXECUTOR 这样一个线程池。
其实,在AsyncTask中是有两个线程池的(分别是THREAD_POOL_EXECUTOR 和SerialExecutor)和一个Handler(InternalHandler)的。
其中,
SerialExecutor 线程池用于任务的排队,
THREAD_POOL_EXECUTOR 线程池用于真正地执行任务,
InternalHandler 用于将执行环境从线程池切换到主线程,其本质仍然是线程的调用过程。

我们来看看AsyncTask的构造方法吧:在这里就可以看到刚刚说的FutureTask了。
/**
 * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
 */
public AsyncTask() {
	/*
	 * 刚刚讲了在SerialExecutor中的scheduleNext中执行真正的异步操作,
	 * 而在SerialExecutor中有这两条语句:
	 * final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
	 * Runnable mActive;
	 * 而我又在上面讲过系统会把Params参数封装成FutureTask对象来充当Runnable的作用,
	 * 反正说了这么多我就是想表达,Params和FutureTask和mTasks和mActive和mWorker直接是有联系的
	 */
	/*
	 * 由于FutureTask的run方法会调用mWorker的call方法,
	 * 因此mWorker的call方法最终会在线程池中执行。
	 * */
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
        	// 在mWorker的call方法中,首先将mTaskInvoked设为true,
        	// 表示当前任务已经被调用过了,
            mTaskInvoked.set(true);

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            // 然后执行AsyncTask的doInBackground方法,
            // 接着将其返回值传递给postResult
            return postResult(doInBackground(mParams));
        }
    };

    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                postResultIfNotInvoked(get());
            } catch (InterruptedException e) {
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e) {
                throw new RuntimeException("An error occured while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };
}

(6)随后我们再去看看postResult方法:
private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
这个方法中会通过 sHandler发送一个MESSAGE_POST_RESULT的消息。

其中sHandler的定义如下所示:
private static final InternalHandler sHandler = new InternalHandler();


private static class InternalHandler extends Handler {
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;
        switch (msg.what) {
        /*
         * sHandler在接收到MESSAGE_POST_RESULT后会调用
         * AsyncTask的finish方法:
         * */
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
我们可以看到sHandler是一个static的静态Handler对象,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程中创建,
由于静态成员会在加载类的时候进行初始化,因此这就变相要求AsyncTask的类必须在主线程中加载(这样会捎带把它内部的静态成员也初始化了),
否则同一个进程中的AsyncTask都将无法正常工作。


sHandler在接收到MESSAGE_POST_RESULT后会调用 AsyncTask的finish方法:

private void finish(Result result) {
	/*
	 * 如果AsyncTask被取消执行了,
	 * 那就取消执行。
	 * */
    if (isCancelled()) {
        onCancelled(result);
    } else {
    	// 否则调用这里喽:
        onPostExecute(result);
    }
    // 状态也跟着变喽:
    mStatus = Status.FINISHED;
}

到此为止,AsyncTask的整个工作过程就分析完毕了!!!


























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值