标签: Android线程

Android 线程的正确使用姿势

进程优先级(Process Priority)

线程寄宿在进程当中,线程的生命周期直接被进程所影响,而进程的存活又和其优先级直接相关。在处理进程优先级的时候,大部分人靠直觉都能知道前台进程(Foreground Process)优先级要高于后台进程(Background Process)。但这种粗糙的划分无法满足操作系统高精度调度的需求。无论Android还是iOS,系统对于Foreground,Background进程有进一步的细化。

Foreground Process

Foreground一般意味着用户双眼可见,可见却不一定是active。在Android的世界里,一个Activity处于前台之时,如果能采集用户的input事件,就可以判定为active,如果中途弹出一个Dialog,Dialog变成新的active实体,直接面对用户的操作。被部分遮挡的activity尽管依然可见,但状态却变为inactive。不能正确的区分visible和active是很多初级程序员会犯的错误。

 

Background Process

后台进程同样有更细的划分。所谓的Background可以理解为不可见(invisible)。对于不可见的任务,Android也有重要性的区分。重要的后台任务定义为Service,如果一个进程包含Service(称为Service Process),那么在“重要性”上就会被系统区别对待,其优先级自然会高于不包含Service的进程(称为Background Process),*后还剩一类空进程(Empty Process)。Empty Process初看有些费解,一个Process如果什么都不做,还有什么存在的必要。其实Empty Process并不Empty,还存在不少的内存占用。

在iOS的世界里,Memory被分为Clean Memory和Dirty Memory,Clean Memory是App启动被加载到内存之后原始占用的那一部分内存,一般包括初始的stack, heap, text, data等segment,Dirty Memory是由于用户操作所改变的那部分内存,也就是App的状态值。系统在出现Low Memory Warning的时候会首先清掉Dirty Memory,对于用户来说,操作的进度就全部丢失了,即使再次点击App图标,也是一切从头开始。但由于Clean Memory没有被清除,避免了从磁盘重新读取app数据的io损耗,启动会变快。这也是为什么很多人会感觉手机重启后,app打开的速度都比较慢。

同理Android世界当中的Empty Process还保存有App相关的Clean Memory,这部分Memory对于提升App的启动速度大有帮助。显而易见Empty Process的优先级是*低的。

综上所述,我们可以把Android世界的Process按优先级分为如下几类:

%title插图%num

进程的优先级从高到低依次分为五类,越往下,在内存紧张的时候越有可能被系统杀掉。简而言之,越是容易被用户感知到的进程,其优先级必定更高。

 

线程调度(Thread Scheduling)

Android系统基于精简过后的linux内核,其线程的调度受时间片轮转和优先级控制等诸多因素影响。不少初学者会认为某个线程分配到的time slice多少是按照其优先级与其它线程优先级对比所决定的,这并不完全正确。

Linux系统的调度器在分配time slice的时候,采用的CFS(completely fair scheduler)策略。这种策略不但会参考单个线程的优先级,还会追踪每个线程已经获取到的time slice数量,如果高优先级的线程已经执行了很长时间,但低优先级的线程一直在等待,后续系统会保证低优先级的线程也能获取更多的CPU时间。显然使用这种调度策略的话,优先级高的线程并不一定能在争取time slice上有*对的优势,所以Android系统在线程调度上使用了cgroups的概念,cgroups能更好的凸显某些线程的重要性,使得优先级更高的线程明确的获取到更多的time slice。

Android将线程分为多个group,其中两类group尤其重要。一类是default group,UI线程属于这一类。另一类是background group,工作线程应该归属到这一类。background group当中所有的线程加起来总共也只能分配到5~10%的time slice,剩下的全部分配给default group,这样设计显然能保证UI线程绘制UI的流畅性。

%title插图%num

有不少人吐槽Android系统之所以不如iOS流畅,是因为UI线程的优先级和普通工作线程一致导致的。这其实是个误会,Android的设计者实际上提供了background group的概念来降低工作线程的CPU资源消耗,只不过与iOS不同的是,Android开发者需要显式的将工作线程归于background group。

所以在我们决定新启一个线程执行任务的时候,首先要问自己这个任务在完成时间上是否重要到要和UI线程争夺CPU资源。如果不是,降低线程优先级将其归于background group,如果是,则需要进一步的profile看这个线程是否造成UI线程的卡顿。

虽说Android系统在任务调度上是以线程为基础单位,设置单个thread的优先级也可以改变其所属的control groups,从而影响CPU time slice的分配。但进程的属性变化也会影响到线程的调度,当一个App进入后台的时候,该App所属的整个进程都将进入background group,以确保处于foreground,用户可见的新进程能获取到尽可能多的CPU资源。用adb可以查看不同进程的当前调度策略。

当你的App重新被用户切换到前台的时候,进程当中所属的线程又会回归的原来的group。在这些用户频繁切换的过程当中,thread的优先级并不会发生变化,但系统在time slice的分配上却在不停的调整。

是否真的需要新线程?

开线程并不是提升App性能,解决UI卡顿的万金油。每一个新启的线程会消耗至少64KB的内存,系统在不同的线程之间switch context也会带来额外的开销。如果随意开启新线程,随着业务的膨胀,很容易在App运行的某个时间点发现几十个线程同时在运行。后果是原本想解决UI流畅性,却反而导致了偶现的不可控的卡顿。

移动端App新启线程一般都是为了保证UI的流畅性,增加App用户操作的响应度。但是否需要将任务放入工作线程需要先了解任务的瓶颈在哪,是i/o,gpu还是cpu?UI出现卡顿并不一定是UI线程出现了费时的计算,有可能是其它原因,比如layout层级太深。

尽量重用已有的工作线程(使用线程池)可以避免出现大量同时活跃的线程,比如对HTTP请求设置*大并发数。或者将任务放入某个串行的队列(HandlerThread)按顺序执行,工作线程任务队列适合处理大量耗时较短的任务,避免出现单个任务阻塞整个队列的情况。

用什么姿势开线程?

new Thread()

这是Android系统里开线程*简单的方式,也只能应用于*简单的场景,简单的好处却伴随不少的隐患。

1
2
3
4
5
6
new Thread(new Runnable() {
            @Override
            public void run() {
            }
        }).start();

这种方式仅仅是起动了一个新的线程,没有任务的概念,不能做状态的管理。start之后,run当中的代码就一定会执行到底,无法中途取消。

Runnable作为匿名内部类还持有了外部类的引用,在线程退出之前,该引用会一直存在,阻碍外部类对象被GC回收,在一段时间内造成内存泄漏。

没有线程切换的接口,要传递处理结果到UI线程的话,需要写额外的线程切换代码。

如果从UI线程启动,则该线程优先级默认为Default,归于default cgroup,会平等的和UI线程争夺CPU资源。这一点尤其需要注意,在对UI性能要求高的场景下要记得

Android 中的线程

一、Android线程的基本介绍
线程在Android中是一个很重要的概念,从用途上来说,Android中的线程可以分为主线程和子线程,主线程主要用来处理和界面相关的事,比如界面绘制和响应用户的操作,为了确保用户体验,主线程必须确保其响应速度,所有任何时候我们都不应该在主线程中处理非常耗时的任务,否则会造成界面卡顿甚至ANR;而子线程的作用就是完成耗时的操作,确保主线程的响应速度。主线程和子线程之间的通信是基于Handler机制,对于Handler机制可以参考:https://blog.csdn.net/xiao_nian/article/details/81011361。

1、Android中常见的线程形态基本介绍
除了Thread本身,在Android中可以扮演线程角色的还有很多,比如AsyncTask、IntentSevice、HandlerThread等,尽管他们的表现形式有别于传统的线程,但他们的本质还是Thread,只不过结合了一些其他的功能,让它们适用于不同的应用场景。AsyncTask封装了线程池和Handler,它主要是为了开发者在使用子线程中能够方便的更新UI;HandlerThread是一种具有消息循环的线程,在它的内部可以使用Handler。IntentService内部采用HanderThread来执行任务,当任务执行完毕后IntentService会自动退出。

2、Android线程池的基本介绍
在操作系统中,线程是操作系统调度的*小单元,同时线程又是一种受限的系统资源,即线程不可能无限制的产生,并且线程的创建和销毁都会有相应的开销。如果一个进程需要频繁的创建子线程来执行任务,而每执行一次任务都需要重新创建和销毁线程,这显然不是高效的做法。正确的做法是采用线程池,一个线程池中会缓存一定数量的线程,当我们创建线程后会将线程存入到线程池中,那么当下次在需要使用线程时,就不用重复创建线程,而是直接从线程池中取出,这样就节省了频繁创建线程和销毁线程的开销。

二、Android中常见的线程形态
1、AsyncTask
AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把任务的执行进度和*终执行结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Hander,并且采用了线程池的机制。

1.1 AyncTask的基本用法
public class AsyncTaskTestActivity extends AppCompatActivity {
private ImageView mImageView;
private Button mButton;
private ProgressDialog mProgressDialog;
private DownloadImageAsyncTask mDownloadImageAsyncTask; // 不能声明为AsyncTask,否则会报类型转换错误

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task_test);
initView();
}

private void initView() {
mImageView = (ImageView) findViewById(R.id.imageView);
mButton = (Button) findViewById(R.id.button);

mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String imagePath = “https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1641460231,985790943&fm=27&gp=0.jpg”;
startAsyncTask(imagePath);
}
});
}

private void startAsyncTask(String imagePath) {
if (mDownloadImageAsyncTask != null && !mDownloadImageAsyncTask.isCancelled()) {
mDownloadImageAsyncTask.cancel(true);
}
mDownloadImageAsyncTask = new DownloadImageAsyncTask();
mDownloadImageAsyncTask.execute(imagePath);
}

@Override
protected void onDestroy() {
super.onDestroy();
// 在界面销毁后取消异步任务,以免发生内存泄露
if (mDownloadImageAsyncTask != null && !mDownloadImageAsyncTask.isCancelled()) {
mDownloadImageAsyncTask.cancel(true);
mDownloadImageAsyncTask = null;
}
}

public void showProgressDialog() {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setTitle(“提示信息”);
mProgressDialog.setMessage(“正在下载中,请稍后……”);
// 设置setCancelable(false); 表示我们不能取消这个弹出框,等下载完成之后再让弹出框消失
mProgressDialog.setCancelable(false);
// 设置ProgressDialog样式为水平的样式
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
}
mProgressDialog.show();
}

public void setLoadProgress(int progress) {
if (mProgressDialog != null) {
mProgressDialog.setProgress(progress);
}
}

public void dimissProgressDialog() {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
}

public void downloadImageSuccessCallBack(byte[] bytes) {
if (bytes != null && mImageView != null) {
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
mImageView.setImageBitmap(bitmap);
}
dimissProgressDialog();
}

/**
* 下载任务
*/
public class DownloadImageAsyncTask extends AsyncTask<String, Integer, byte[]> {
// 在UI线程中执行,任务开始前需要进行的UI操作,比如弹出加载框
@Override
protected void onPreExecute() {
super.onPreExecute();
showProgressDialog();
}

// 在异步线程中执行耗时任务,比如请求网络数据
@Override
protected byte[] doInBackground(String… params) {
// 通过Apache的HttpClient来访问请求网络中的一张图片
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(params[0]);
byte[] image = new byte[]{};
try
{
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
InputStream inputStream = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
if(httpEntity != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
{
// 得到文件的总长度
long file_length = httpEntity.getContentLength();
// 每次读取后累加的长度
long total_length = 0;
int length = 0;
// 每次读取1024个字节
byte[] data = new byte[1024];
inputStream = httpEntity.getContent();
while(-1 != (length = inputStream.read(data)))
{
// 每读一次,就将total_length累加起来
total_length += length;
// 边读边写到ByteArrayOutputStream当中
byteArrayOutputStream.write(data, 0, length);
// 得到当前图片下载的进度
int progress = ((int)(total_length/(float)file_length) * 100);
// 时刻将当前进度更新给onProgressUpdate方法
publishProgress(progress);
if (isCancelled()) {
break;
}
}
}
image = byteArrayOutputStream.toByteArray();
inputStream.close();
byteArrayOutputStream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
httpClient.getConnectionManager().shutdown();
}
return image;
}

// 任务取消时调用
@Override
protected void onCancelled() {
super.onCancelled();
dimissProgressDialog();
}

// 在UI线程中执行,在异步线程中调用publishProgress(Progress)后会立即回调onPregressUpdate方法,可以在该方法中更新UI界面上的任务执行进度
@Override
protected void onProgressUpdate(Integer… values) {
super.onProgressUpdate(values);
setLoadProgress(values[0]);
}

// 任务执行完毕调用该方法,在UI线程中执行,更新数据到UI界面
@Override
protected void onPostExecute(byte[] bytes) {
super.onPostExecute(bytes);
downloadImageSuccessCallBack(bytes);
}
}
}
上面的代码是AsyncTask典型的用法,通过AsyncTask下载了一张图片,下载完成后将图片显示在界面上;AsyncTask是一个抽象的泛型类,它提供了Params、Progress和Result三个泛型参数,分别表示传入参数的类型、任务执行进度的类型和任务返回结果的类型,如果不需要传递参数,那么上面三个泛型参数可以用void代替;AsyncTask提供了四个核心的方法,它们的含义如下:

onPreExecute():在主线程中执行,异步任务开始之前会执行该方法,一般用来做一些准备工作,比如界面上弹出加载框;

doInBackground(Params… params):在线程池中的线程中执行,该方法用来执行异步任务,params表示异步任务的输入参数,在该方法中可以通过调用publicProgress(Progress… values)来更新任务的执行进度,publicProgress(Progress… values)方法会触发调用onProgressUpdate(Progress… values)方法;

onProgressUpdate(Progress… values):在主线程中执行,用来更新任务的执行进度;

onPostExecute(Result result):在主线程中执行,该方法是异步任务执行完毕后的回调方法,result参数表示异步任务的返回结果。

除了上面四个方法外,还有一个比较重要的方法是onCancelled()方法,它同样是在主线程中执行,当异步任务被取消时,onCancelled()方法会被调用,这个时候onPostExecute方法不会被调用。

1.2 AsyncTask源码分析
上面介绍了AsyncTask的基本用法,接下来我们分析一下AsyncTask的源码:

我们通过调用execute方法来让AsyncTask开始工作,代码如下:

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params… params) {
return executeOnExecutor(sDefaultExecutor, params);
}

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params… params) {
if (mStatus != Status.PENDING) { // 每个AsyncTask只能执行一次
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(); // 调用onPreExecute()方法

mWorker.mParams = params;
exec.execute(mFuture); // 执行异步任务

return this;
}
在上面的代码中,我们可以知道AsyncTask的execute必须在UI线程中执行,并且每个AsyncTask只能执行一次,在执行异步任务之前,首先会先调用onPreExecute()方法,然后将参数保存在“mWorker”的参数中,*终执行异步任务的代码是“exec.execute(mFuture);”,其中“exec”如果没有指定,默认是“mDefaultExecutor”,“mDefaultExecutor”的定义如下:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;

public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() { // 将任务存入到队列中
public void run() {
try {
r.run(); // 执行任务
} finally {
scheduleNext(); // 任务执行完毕后继续执行下一个任务
}
}
});
if (mActive == null) { // 通过这个判断来确保任务是串行执行的
scheduleNext(); // 从队列中取出任务并执行
}
}

protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) { // 从队列中取出任务并执行,如果队列为空,则mActive也会为空
THREAD_POOL_EXECUTOR.execute(mActive); // 真正执行任务的线程池
}
}
}
“exec.execute(mFuture);”*终会调用SerialExecutor的execute方法,将任务存入到队列中,然后会触发线程池执行任务,从上面也可以看出在一个进程中所有AysncTask的异步任务默认是串行执行的,当*次调用execute方法时,mActive默认为空,所以会执行scheduleNext()方法,开始执行异步任务,这之后再次调用execute方法时,只要还有任务在执行,mActive就不为空了,也就不会调用scheduleNext,而只会将任务加入到队列中,当当前任务执行完毕后,会再次调用scheduleNext()方法执行下一个任务,如果此时队列中没有了任务,会将mActive置为空,当下一个任务来临后,也就会执行scheduleNext()方法继续执行任务。

那么有没有什么办法可以让AsyncTask的任务并行执行呢?答案是肯定的,AysncTask提供了executeOnExecutor(Executor exec, Params… params)接口,可以指定执行异步任务的Executor,并且AysncTask提供了一个public static类型的线程池对象,定义如下:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT – 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;

public static final Executor THREAD_POOL_EXECUTOR;

static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
根据代码,我们知道THREAD_POOL_EXECUTOR是一个线程池对象,其根据CPU的个数配置了线程池的核心线程数和*大线程数,我们可以指定异步任务的执行线程池为THREAD_POOL_EXECUTOR,这样就能做到并行执行了(当然,如果任务很多,超过了线程池的*大线程数,任务还是会排队执行)。

我们再来看一下传入的参数“mFuture”,它是在AsyncTask的构造函数中初始化的:

mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams); // 调用doInBackground方法执行异步任务
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result); // 将异步任务的返回结果发送给主线程
}
return result;
}
};

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 occurred while executing doInBackground()”,
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
FutureTask类是Future 的一个实现,并实现了Runnable,所以可通过Excutor(线程池) 来执行,下面看一下它的核心方法run方法:

public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable; // 获得callable对象
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); // 调用callable对象的call方法执行任务并获得返回结果
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
可以看到FutureTask的run方法*终是调用了其Callable对象的call()方法来执行任务,由此也可以看出,AsyncTask的异步任务的执行是在其WorkerRunnable对象的call()方法中执行的,而call()方法中又调用了AsyncTask的doInBackground方法来执行异步任务,执行完毕后会调用postResult方法将返回结果发送给主线程,下面我们来看一下postResult的源码:

private Result postResult(Result result) {
@SuppressWarnings(“unchecked”)
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result)); // 构造一个Message,将消息发送给主线程
message.sendToTarget();
return result;
}
上面的代码会构造一个Message,消息类型为MESSAGE_POST_RESULT,并且包含了一个AsyncTaskResult对象,这个对象中封装了当前AsyncTask对象和返回结果对象。继续看getHandler的代码:

private static Handler getHandler() {
synchronized (AsyncTask.class) { // 使用线程锁的单例模式构造Handler对象
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
getHandler使用线程锁的单例模式构造了一个InternalHander对象,InternalHander定义如下:

private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper()); // 使用主线程的Looper来创建Handler,也就是说这个Hander的所有消息都会在主线程中处理
}

@SuppressWarnings({“unchecked”, “RawUseOfParameterizedType”})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj; // 取出AsyncTaskResult对象
switch (msg.what) {
case MESSAGE_POST_RESULT: // 返回结果的消息类型
// There is only one result
result.mTask.finish(result.mData[0]); // 调用AsyncTask的finish方法处理返回结果
break;
case MESSAGE_POST_PROGRESS: // 更新进度的消息类型
result.mTask.onProgressUpdate(result.mData); // 调用AsyncTask的onProgressUpdate更新进度
break;
}
}
}
InternalHandler使用主线程的Looper来创建Handler,这就确保了InternalHandler的所有消息都会在主线程中处理,在handlerMessage中处理了两种消息,一种是返回结果,一种是更新任务进度,而更新任务进度调用的是AsyncTask的onProgressUpdate方法,所有我们在自定义的AsyncTask中通过重写onProgressUpdate方法来告知用户任务进度;我们再来看一下AsyncTask的finish方法:

private void finish(Result result) {
if (isCancelled()) {
onCancelled(result); // 如果任务已经取消,则调用onCancelled方法
} else {
onPostExecute(result); // 任务没取消,则调用onPostExecute方法处理任务返回结果
}
mStatus = Status.FINISHED;
}
可以看到,如果任务取消了,AsyncTask会调用onCancelled方法处理任务;如果没有取消,则会调用onPostExecute方法来处理任务的返回结果。

之前我们说过,我们会在doInBackground方法中调用publishProgress方法来触发更新任务进度,我们可以猜测一下,publicProgress方法应该是利用InternalHandler向主线程发送一个MESSAGE_POST_PROGRESS的消息,然后在主线程中就会调用AsyncTask的onProgressUpdate方法来更新任务进度了,查看publicProgress的源码:

@WorkerThread
protected final void publishProgress(Progress… values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
上面的代码也验证了我们的猜想。

到此,AsyncTask的代码也就分析完了,总结一下,AsyncTask是一个用来执行异步任务的类,通过它可以轻松的在子线程中执行异步任务,执行完之后将处理返回结果的代码在主线程中执行;AsyncTask内部的原理主要使用到了Handler和线程池,Handler用来切换任务执行的线程,而线程池用来执行异步任务;其中还有线程池方面的知识,我们后面再分析。

2、 HandlerThread
HandlerThread继承了Thread,它是一种可以使用Handler的Thread,其实现原理很简单,主要是其内部维护了自己的Looper对象,这个Looper对象在run方法中会开启循环,下面来看一下其基本用法:

HandlerThread handlerThread = new HandlerThread(“HandlerThreadTest”); // 构造一个HandlerThread对象
handlerThread.start(); // 启动线程
Handler handler = new Handler(handlerThread.getLooper()) { // 用HandlerThread的Looper构造一个Handler对象,也就是说这个Handler的所有消息都会通过HandlerThread的Looper循环处理
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
Log.i(“aaaaaaa”, “HandlerThread handlerMessage”);
}
}
};
Message message = Message.obtain();
message.what = 1;
handler.sendMessage(message); // 发送一个消息
首先我们构造了一个HandlerThread的对象,并且调用其start方法启动线程,然后利用HandlerThread的Looper构造了一个Handler对象,之后就可以用这个Handler对象向HandlerThread发送消息了,消息的都会在HandlerThread线程中处理。HandlerThread的源码如下:

public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper; // HandlerThread内部维护的Looper对象

public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}

/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}

/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() { // 正式开始Looper循环之前的回调方法,可以在该方法中做一些准备工作
}

@Override
public void run() {
mTid = Process.myTid();
Looper.prepare(); // 为当前线程创建Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared(); // 在正式开始循环之前调用onLooperPrepared方法
Looper.loop(); // 开启Looper循环
mTid = -1;
}

/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() { // 获得当前线程的Looper对象
if (!isAlive()) {
return null;
}

// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}

/**
* Quits the handler thread’s looper.
* <p>
* Causes the handler thread’s looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class=”note”>
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() { // 调用Looper的quit来退出Looper循环,一旦Looper循环退出,run方法也就结束了,当前线程也就结束了
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}

/**
* Quits the handler thread’s looper safely.
* <p>
* Causes the handler thread’s looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {// 调用Looper的quitSafely来退出Looper循环,一旦Looper循环退出,run方法也就结束了,当前线程也就结束了
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}

/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
HandlerThread的源码很简单,相比于普通的线程,其内部维护了一个Looper对象,并且会在run方法中开启循环,其他线程可以通过它的Looper对象来构造Handler,通过Handler向HandlerThread发送消息,这样就将任务切换到了HandlerThread线程中处理;同时,HandlerThread也提供了一下退出的方法,其退出方法其实就是调用Looper对象的退出方法来结束Looper循环,一旦Looper循环结束,run方法也就结束了,线程退出。

3、 IntentService
IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类,因此必须创建它的子类才能使用IntentService。IntentService可用于执行后台耗时的任务,当任务执行完后它会自动停止,同时由于IntentService是服务的原因,这就导致它的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务,因为它高优先级不容易被系统杀死。IntentService相比于传统的Service,其任务的执行是在子线程中执行的,而传统的Service是运行在主线程中的,当然,我们也可以在Service中开启一个子线程来执行任务,但是如果使用IntentService,我们就省去了维护子线程的麻烦,而且IntentService会在任务结束后自动停止服务;从实现上来说,IntentService封装了HandlerThread和Handler。我们先来看一下它的基本用法:

和普通的Service一样,首先要在AndroidManifest文件中配置服务:

<service android:name=”.asynctasktest.MyIntentService”/>
然后定义MyIntentService类:

public class MyIntentService extends IntentService {
public MyIntentService() {
super(“MyIntentService”);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) { // IntentService的任务处理方法
Log.i(“liunianprint:”, “run thread ” + Thread.currentThread().getId()); // 打印运行的这段代码的线程
}
}
*后启动IntentService即可:

Intent intent = new Intent(IntentServiceActivity.this, MyIntentService.class);
startService(intent);
启动方式和启动普通Service一样。

运行程序,我们发现onHandleIntent方法的处理确实是在子线程中处理的。下面我们分析一下IntentService的源码实现:

public abstract class IntentService extends Service { // 继承与Service,这也确定了IntentService首先是一个Service
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;

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);
}
}

public IntentService(String name) {
super();
mName = name;
}

public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}

@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.

super.onCreate();
HandlerThread thread = new HandlerThread(“IntentService[” + mName + “]”);
thread.start();

mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

@Override
public void onDestroy() {
mServiceLooper.quit();
}

@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}

@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
代码不多,首先IntentService继承了Service,说明其是一个服务,在启动服务后,首先会调用服务的onCreate方法,我们来分析一下IntentService的onCreate方法:

@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.

super.onCreate();
HandlerThread thread = new HandlerThread(“IntentService[” + mName + “]”); // 创建一个HandlerThread线程
thread.start(); // 启动HandlerThread线程,等待消息处理

mServiceLooper = thread.getLooper(); // 获得HandlerThread的Looper对象
mServiceHandler = new ServiceHandler(mServiceLooper); // 用HandlerThread的Looper对象来创建ServiceHandler对象
}
在onCreate方法中,首先会创建一个HandlerThread线程并启动,然后在利用HandlerThread的Looper来构造ServiceHandler对象,从之前HandlerThread的源码中,我们知道HandlerThread线程一旦启动就会启动Looper循环,等待消息的到来并处理,我们再来看一下ServiceHandler的源码:

private final class ServiceHandler extends Handler { // 继承Handler
public ServiceHandler(Looper looper) {
super(looper);
}

@Override
public void handleMessage(Message msg) { // 处理消息方法,该方法会在HandlerThread线程中调用
onHandleIntent((Intent)msg.obj); // 调用onHandleIntent方法处理消息
stopSelf(msg.arg1); // 停止服务,这里会根据传过来的startId来判断是否停止服务
}
}
ServiceHandler继承与Handler,由于它是使用上面HandlerThread的Looper对象构造的,所有其handleMessage方法也会在HandlerThread线程中调用,在handlerMessage方法中,首先会调用IntentService的onHandleIntent方法处理消息,消息处理完毕后会调用stopSelf来停止IntentService。我们知道,在onCreate方法调用完成后,Service会调用onStartCommand方法来运行服务,IntentService的onStartCommand定义如下:

@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage(); // 构造一个消息
msg.arg1 = startId; // 传入startId值
msg.obj = intent; // 传入intent对象
mServiceHandler.sendMessage(msg); // 发送消息
}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId); // 调用onStart方法
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
从上面的代码可以看出,onStart方法其实就是构造了一个消息,消息里面包含了Intent对象和当前调用onStart的startId,然后利用ServiceHandler对象向之前创建的HandlerThread线程发送消息,这样,在HandlerThread线程中就可以出来消息了,也就是会调用到ServiceHandler的handleMessage方法。这里注意一点,在handleMessage中调用stopSelf方法停止服务时,传入了startId,每次调用startService时,系统都会调用onStart方法,但是startId会递增,调用stopSelf(int startId)方法来停止服务时,会以*近的请求的startId为标准,也就是说,系统在回调stopSelf(startId)时,会用传入的startId和*近的请求的startId相比较,如果相同,则退出Service,否则,Service不会被停止,这样确保了IntentService的*近任务是肯定能够执行的。

好了,IntentService的源码也分析完了,总结一下,IntentService本质上是一个Service,其目的是为了减去普通Service对子线程的维护;IntentService适合执行在后台的耗时任务,因为它本质上是Service,相比于普通线程,所有不容易被系统杀死。IntentService的内部实现原理是Service、HandlerThread和Handler,其中,继承Service让其有了Service的功能,HandlerThread用来执行异步的耗时任务,Handler用来发送消息给HandlerThread,触发HandlerThread执行任务。

三、线程池
这部分我们来了解一下线程池的知识,AsyncTask*终就是通过线程池来执行异步任务的,提到线程池,我们必须先说一下使用线程池的好处:

1、重用线程池中的线程,避免了频繁创建和销毁线程所带来的开销;

2、能够有效的控制线程的*大并发数,避免了大量的线程之间因为相互抢占系统资源而导致的阻塞;

3、能够简单的对线程进行管理,并提供定时执行以及指定间隔循环执行等功能。

总的来说,线程池是系统为我们提供的管理线程的模块,通过它我们可以方便的控制项目中的线程。Android中的线程池的概念来源于Java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor。ThreadPoolExecutor提供了一系列参数来配置线程池。通过配置参数我们可以获得不同功能的线程池。

3.1 ThreadPoolExecutor
ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池,下面我们来看一下ThreadPoolExecutor的构造方法中各个参数的含义,这些参数将直接影响到线程池的功能特性:

public ThreadPoolExecutor(int corePoolSize, // 线程池核心线程个数
int maximumPoolSize, // 线程池*大线程个数
long keepAliveTime, // 非核心线程闲置时的超时时长
TimeUnit unit, // 非核心线程闲置的超时时长的时间单位
BlockingQueue<Runnable> workQueue, // 线程池中的任务队列
ThreadFactory threadFactory, // 线程工厂,为线程池提供创建新线程的功能
RejectedExecutionHandler handler) { // 当线程池无法执行新任务时的处理对象
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
corePoolSize:线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态;如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的线程在等待新任务到来时也会有超时策略,这个时间有kiipAliveTime所指定,当等待时间超过keepAliveTime所指定的时长后,核心线程就会被终止。

maximumPoolSize:线程池所能容纳的*大线程个数,当任务队列放不下后,会立刻启动非核心线程执行任务,当线程数量已经达到了线程池规定的*大个数,那么就拒*执行此任务,线程池会调用RejectedExecutionHandler的rejectedExecution方法来处理异常情况。

keepAliveTime:非核心线程闲置时的超时时长,超过这个时长后,非核心线程将会被回收;当allowCoreThreadTimeOut设置为true后,这个参数对核心线程也同样有效。

unit:用于指定keepAliveTime的时间单位。

workQueue:线程池中的任务队列,用来存储线程池将要执行的任务。

threadFactory:线程工厂,为线程池提供创建线程的功能;ThreadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r)。

handler:任务异常处理,当线程池无法执行新任务时,就会调用这个参数来处理异常情况,RejectedExecutionHandler是一个接口,它只有一个方法:void rejectedExecution(Runnable r, ThreadPoolExecutor executor);导致无法处理新任务的额原因可能是由于任务队列已满并且线程数量也达到了线程池规定的*大个数,新的任务过来后无法存入到任务队列中,这个时候线程池会调用handler的rejectedExecution方法来通知调用者。

 

ThreadPoolExecutor执行任务时大致遵循如下规则:

(1)如果线程池中的线程数量未达到核心线程的数量,那么会直接创建一个核心线程来执行任务;

(2)如果线程池中的线程数量已经超过核心线程的数量,那么会将任务插入到任务队列中排队等待执行;

(3)如果在步骤2中无法将任务插入到任务队列中,这往往是因为任务队列已满,这个时候如果线程数量没有达到线程池规定的*大个数,则会立即启动一个非核心线程来处理任务;

(4)如果在步骤3中线程数量已经达到了线程池规定的*大个数,那么就拒*执行此任务,线程池会调用RejectedExecutionHandler的rejectedExecution方法来处理异常情况。

 

下面我们看一下AsyncTask中使用的线程池的配置

AsyncTask中异步任务的执行代码如下:

THREAD_POOL_EXECUTOR.execute(mActive);
THREAD_POOL_EXECUTOR的定义:

public static final Executor THREAD_POOL_EXECUTOR;

static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true); // 设置线程池的核心线程也有超时机制
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
可以看到,AsyncTask是在静态代码块初始化线程池的,其中参数的定义如下:

private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT – 1, 4)); // 核心线程个数和CPU的核数相关
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; // *大线程数为CPU核数的2倍+1
private static final int KEEP_ALIVE_SECONDS = 30; // 线程的超时时间为30秒
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128); // 线程池的任务队列,容量为128
private static final ThreadFactory sThreadFactory = new ThreadFactory() { // 创建线程的工厂
private final AtomicInteger mCount = new AtomicInteger(1);

public Thread newThread(Runnable r) {
return new Thread(r, “AsyncTask #” + mCount.getAndIncrement());
}
};
3.2 线程池的分类
前面我们已经提到,根据对ThreadPoolExecutor配置不同的参数,其生成的线程池的功能特性也不一样。下面介绍Android中四类*常见的线程池,它们都直接或者间接的通过配置ThreadPoolExecutor的参数来实现自己的功能特性,这四类线程池分别是FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor。

FixedThreadPool

FixedThreadPool是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池关闭了。它可以通过Executors的newFixedThreadPool来创建:

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
通过观察上面的参数,我们可以知道,FixedThreadPool的*大核心线程数等于*大线程数,这也就意味着其没有非核心线程,任务队列没有设置*大容量,表示任务队列没有大小限制。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这就意味着它能够更加快的响应外界的请求,当核心线程都处于活动状态时,任务将会保存在任务队列中等待处理。

CachedThreadPool

CachedThreadPool是一种线程数量不定的线程池,它只有非核心线程,并且其*大线程数为Integer.MAX_VALUE,也就是不限制线程的*大个数。它可以通过Executors的newCachedThreadPool来创建:

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
观察上面的参数,我们发现CachedThreadPool的*大核心线程数为0,并且不限制*大线程数,线程空闲超时时间为60秒,任务队列为SynchronousQueue,SynchronousQueue是一个非常特殊的队列,它其实是无法存储任务的,当然,CachedThreadPool的*大线程数是不限的,所以每个任务都会立即执行。从CachedThreadPool的特性来看,这类线程池比较适合执行大量的耗时较少的任务。

ScheduledThreadPool

ScheduledThreadPool的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会立刻被回收。它可以通过Executors的newScheduledThreadPool方法来创建:

public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
ScheduledThreadPool主要用于执行定时任务和具有固定周期的重复任务。

SingleThreadExecutor

SingleThreadExecutor内部只有一个核心线程,它确保了所有任务都在同一个线程中按顺序执行。它可以通过Executors的newSingleThreadExecutor来创建:

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
当有任务到来时,如果唯一的一个核心线程处于工作状态,则任务将会存入到队列中等待执行;SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,使这些任务之间不需要处理线程同步的问题。

除了上面的四种常见的线程池,我们还可以来配置符合自己需求的线程池。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速