咱们都知道在Android主线程中处理耗时任务会产生ANR,所有很多耗时的任务都需要异步处理。Android里面的有四种常见的异步处理类见:Thread、HandlerThread、AsyncTask、IntentService。
一、Thread
线程是执行任务的最基本的单元,当然了在Android中线程分为两大部分:主线程、子线程。Thread线程主要有以下几种状态:
- New:实例被创建。
- Runnable:就绪状态。
- Running:线程被cpu执行调用run()函数之后 就处于运行状态。
- Blocked:调用join()函数、sleep()函数、wait()函数使线程处于阻塞状态。
- Dead:线程的run()方法运行完毕或被中断或被异常退出,线程将会到达Dead状态。
当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。
1.1、Thread实现方式
实现线程有两种方式:继承Thread类、以及实现Runnable接口
1.1.1、继承Thread类
private class YourThread extends Thread {
YourThread(String name) {
super(name);
}
@Override
public void run() {
//执行耗时操作,做你想做的
}
}
YourThread yourThread = new YourThread("我的线程");
yourThread.start();
1.1.2、实现Runnable接口
private class YourRunnable implements Runnable {
@Override
public void run() {
//执行耗时操作,做你想做的
}
}
Thread yourThread = new Thread(new YourRunnable(), "我的线程");
yourThread.start();
Thread两种实现方式,推荐使用实现Runnable接口的方式。因为 当继承了Thread类,则不能继承其他类,而实现Runnable接口可以,而且实现Runnable接口的线程类的多个线程,可以访问同一变量,而Thread则不能。
1.2、Thread 主要函数介绍
/**
* 线程运行时需要执行的代码
*/
public void run()
/**
* 启动线程
*/
public synchronized void start()
/**
* 线程休眠,交出CPU,让CPU去执行其他的任务,然后线程进入阻塞状态,sleep方法不会释放同步锁
*
* @param millis 休眠时间,单位毫秒
*/
public static void sleep(long millis) throws InterruptedException
/**
* @param millis 休眠时间,单位毫秒
* @param nanos 额外的休眠时间,单位纳秒
*/
public static void sleep(long millis, int nanos) throws InterruptedException
/**
* 线程让步,使当前线程交出CPU,当前线程重置为就绪状,然后又通过和其他线程公平竞争来看谁先执行。yield方法不会释放锁
*/
public static native void yield()
/**
* 等待线程终止,再继续执行。直白的说 就是发起该子线程的线程 只有等待该子线程运行结束才能继续往下运行
*/
public final void join() throws InterruptedException
/**
* @param millis 等待时间,单位毫秒
*/
public final void join(long millis) throws InterruptedException
/**
* @param millis 等待时间,单位毫秒
* @param nanos 额外等待时间,单位纳秒
*/
public final void join(long millis, int nanos) throws InterruptedException
/**
* 中断线程,我们通过interrupt方法和isInterrupted()方法来停止正在运行的线程,注意只能中断已经处于阻塞的线程
*/
public void interrupt()
/**
* 获取当前线程id
*/
public long getId()
/**
* 设置获取当前线程的名字
*/
public final void setName(String name)
public final String getName()
/**
* 设置获取当前线程优先级
*/
public final void setPriority(int newPriority)
public final int getPriority()
/**
* 设置和判断是否是守护线程 setDaemon()函数一点要在start()函数之前调用
*/
public final void setDaemon(boolean on)
public final boolean isDaemon()
/**
* 获取当前线程
*/
public static native Thread currentThread()
1.2.1、sleep(),wait(),yield(),join()函数的区别。
- sleep():在指定时间内让当前正在执行的线程暂停执行,可以执行其他的任务,但不会释放“锁标志”。
- wait():java.lang.Object类里面提供的方法,暂停执行,wait方法释放了“锁标志”,使得其他线程可以使用同步控制块或者方法。wait之后需要调用notify和notifyAll来通知往下执行。
wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用。
- yield():当前线程让步,当前线程又重新进入就绪状态。又和其他线程公平竞争看谁先执行。(注意哦:并不是完完全全的让给其他线程了,而是又重新竞争哦,有可能还是自己执行也有可能是其他的线程执行)。
- join():等待线程结束。
1.2.2、用户线程和守护线程
Java里面将线程分为User Thread(用户线程)和Daemon Thread(守护线程)两种。Daemon Thread是运行在后台的线程。我们Android里面的MainThread就是User Thread而不是Daemon Thread。Daemon Thread一般用来给User Thread提供服务。
调用setDaemon()函数去设置是否是守护线程的时候,该函数一点要在start()函数之前调用。否则无效。
1.3、如何优雅的终止Thread
JDK里面Thread提供了一个stop()方法,但是stop()方法是一个被废弃的方法。为什么stop()方法被废弃而不被使用呢?因为stop()方法太过于暴力,会强行把执行一半的线程终止。这样会就不会保证线程的资源正确释放,通常是没有给与线程完成资源释放工作的机会,因此会导致程序工作在不确定的状态下。既然stop()方法不让用了,那咱们还有哪些方法来终止Thread呢。
1.3.1、监视某个退出标志位
简单地设置一个标志位,需要配合while()循环使用,并通过设置这个标志来控制循环是否退出。
public class YourThread extends Thread {
public volatile boolean mExit = false;
@Override
public void run() {
super.run();
while (!mExit) {
System.out.println("I'm running");
}
}
}
1.3.2、使用interrupt方法终止线程
调用interrupt()方法之后interrupted()结果为true,最后采用抛异常的方式来终止线程,代码如下:
public class YourThread extends Thread {
@Override
public void run() {
try {
while (true) {
if (interrupted()) {
throw new InterruptedException();
}
System.out.println("I'm running");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
二、HandlerThread
HandlerThread是实现了Lopper的线程(归根到底他还是一个线程)。为什么需要HandlerThread这东西呢,因为Android中为了同时完成多个任务,经常会在应用程序当中创建多个线程。这个时候为了让多个线程之间能够方便的通信,这个时候我们通常会使用Handler实现线程间的通信。但是Handler对应的线程必须包含Looper(Android主线程默认已经包含Looper了)。默认线程是没有Looper的。不过我们可以自己去创建Looper,为线程创建Looper的方法如下:在线程run()方法当中先调用Looper.prepare()初始化Looper,然后再run()方法最后调用Looper.loop()启动循环。那,现在为了方便大家不用我们每次都去写这些Looper的创建。Android API就给我们提供了HandlerThread。
记住,想通过Handler把消息发送到那个线程里面去,对应的线程必须包含Looper。
2.1、HandlerThread使用
HandlerThread使用步骤:
- 创建HandlerThread线程。
- 启动HandlerThread线程。
- 创建Handler对象,Lopper必须是HandlerThread里面的Looper。(Hander用于发送消息)
- 用完之后记得退出循环,要不然HandlerThread线程一直占内存。
ublic class HandlerThreadActivity extends MobileBaseActivity {
private HandlerThread mHandlerThread;
private Handler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建一个线程,线程名字:handler-thread
mHandlerThread = new HandlerThread("handler-thread");
//开启一个线程
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
Log.d("handler ", "消息: " + msg.what + " 线程: " + Thread.currentThread().getName());
}
};
//在主线程给handler发送消息
mHandler.sendEmptyMessage(1);
}
@Override
protected void onDestroy() {
super.onDestroy();
//释放资源
mHandlerThread.quit();
}
}
千万要记得HandlerThread用完之后要调用quit()方法退出循环。
2.2、HandlerThread源码实现过程
上面对HandlerThread的使用做了一个很简单的介绍,接下来咱们就来具体的看下HandlerThread的源码。对于HandlerThread得明确他是一个带Looper的线程。
HandlerThread源文件也非常的简单,如下
public class HandlerThread extends Thread {
...
/**
* 线程开始loop之前的回调
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* 获取Looper对象
*/
public Looper getLooper() {
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;
}
/**
* 退出looper循环()
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* 和quit()方法一样,只不过这个更加的安全
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
...
}
这里我们主要关注run()方法里面的内容,其他的方法都很简单,大家肯定一看就懂。run()方法里面开始的时候调用了Looper.prepare();,run()方法快结束的时候调用了Looper.loop();这样就让HandlerThread循环起来了。run()方法里面大家有发现notifyAll();的调用把。这干啥的呀。来我们先看getLooper()方法了,是不是在getLopper()方法里面mLooper = null的时候调用了wait()。当mLooper=null的时候getLopper()就一直等在这里了,等mLooper不为null的时候才返回mLooper。notifyAll()就是run()方法里面获取到了mLooper之后通知wait()不用等了,可以去获取了。
三、AsyncTask
AsyncTask是Android提供的另一个异步处理的工具类,它对Thread和Handler进行了封装,方便我们在后台线程中执行操作,然后将结果发送给主线程,从而在主线程中进行UI更新等操作。使用AsyncTask的时候,我们无需关注Thread和Handler,AsyncTask内部会对其进行管理,这样我们就只需要关注于我们的业务逻辑即可。
AsyncTask有四个重要的回调方法,分别是:onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute。这四个方法会在AsyncTask的不同时期进行自动调用,我们只需要实现这几个方法的内部逻辑即可。这四个方法的一些参数和返回值都是基于泛型的,而且泛型的类型还不一样,所以在AsyncTask的使用中会遇到三种泛型参数:Params, Progress 和 Result。
四个重要方法介绍
- onPreExecute():运行在UI线程,后台任务即将开始。
- doInBackground():运行在单独的工作线程中,处理后台任务逻辑。
- onProgressUpdate():运行在UI线程,一般用于回传任务处理的进度。
- onPostExecute():运行在UI线程,后台任务执行完毕。
三种泛型介绍
- Params:开始异步任务执行时传入的参数类型。
- Progress:异步任务执行过程中,返回下载进度值的类型。
- Result:异步任务执行完成后,返回的结果类型。
3.1、AsyncTask使用
AsyncTask使用也非常简单,主要的关注点都在我们的业务逻辑上。比如我们来实现一个文件下载的任务。伪代码如下:
public class AsyncTaskActivity extends MobileBaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] urls = {"http://blog.csdn.net/iispring/article/details/47115879"};
DownloadTask downloadTask = new DownloadTask();
//开始执行任务
downloadTask.execute(urls);
}
/**
* Params - > String:文件url
* Progress - > Float 文件下载进度
* Result - > Long 下载的文件大小
*/
private class DownloadTask extends AsyncTask<String, Float, Long> {
@Override
protected void onPreExecute() {
super.onPreExecute();
//TODO:UI线程执行,开始执行任务的回调
}
@Override
protected Long doInBackground(String... strings) {
//TODO:1. 从参数里面获取到文件的下载地址。2. while循环读取网络文件信息,并且实时调用publishProgress告诉下载进度
downloadTask(strings[0]);
return null;
}
@Override
protected void onProgressUpdate(Float... values) {
super.onProgressUpdate(values);
//TODO:下载进度回调上来
}
@Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
//TODO:任务执行完成
}
private void downloadTask(String path) {
//模拟耗时操作
int count = 0;
try {
while (count < 100) {
count++;
Thread.sleep(3 * 1000);
publishProgress((float) count);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.2、AsyncTask源码实现过程
AsyncTask源代码的分析。
先从构造函数开始,
构造函数
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
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);
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);
}
}
};
}
就是初始化了三个变量,mHandler、mWorker、mFuture。mHandler是用来post消息的,把onProgressUpdate()和onPostExecute()post到主线程里面去执行。
接着,如果AsyncTask想执行任务,会调用execute()函数,我们直接看下execute()函数。
execute()
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute()函数直接调用了executeOnExecutor()函数。注意第一个参数是sDefaultExecutor。他是一个static修饰变量,是所有AsyncTask共有的。
sDefaultExecutor
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) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
分析代码我们发现sDefaultExecutor会把所有的任务依次加入到线程池中,而且保证了先进来的任务加入线程池,执行完一个任务就加入一个线程池。其中线程池THREAD_POOL_EXECUTOR也是一个static变量。
executeOnExecutor()
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
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();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
先判断了状态,看的出来一个AsyncTask只能excute一次。在接着调用onPreExecute(); 回调告诉任务准备开始了。最后exec.execute(mFuture);把mFuture加入到sDefaultExecutor里面去。mFuture在构造函数里面已经声明了。从构造函数得知会执行mWorker变量的call()函数,call()函数执行完之后会执行mFuture对象的done()函数。
先执行mWorker的call()函数
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);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mWorker的call()函数执行完之后执行mFuture的done()函数
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);
}
}
};
mWorker的call()函数里面调用了doInBackground()吧,并且是在后台线程中执行的。call()函数的最后调用了postResult(result);把结果回调给主线程。
我们看下postResult()是怎么回调到主线程的。
postResult()函数
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
很熟悉把,getHandler()对应的是哪个线程,就去哪个线程执行了。
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
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;
}
}
}
其中两种消息类型MESSAGE_POST_RESULT、MESSAGE_POST_PROGRESS一个用来回调执行结果的,一个用来回调执行进度的。一个对应onPostExecute()的回调,一个对应onProgressUpdate()的回调。当然了如果你想触发onProgressUpdate()执行进度的回调,你的自己在doInBackground()里面主动的去调用publishProgress()函数。
AsyncTask源码的分析过程关键是抓住onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute函数的调用位置和所处的线程。AsyncTask的简单分析就到这里有疑问也可以留言。
四、IntentService
IntentService 是继承自 Service 并处理异步任务的一个类。IntentServicer最大的特点是用完即走。处理玩异步任务IntentService会自动结束。
4.1、IntentService的使用
IntentService有一个非常重要的方法onHandleIntent(),onHandleIntent()当某个请求需要处理时,这个方法会在工作者线程被调用,一次仅仅会有一个请求被处理。所以我们在onHandleIntent()函数里面处理我们的异步逻辑。
写一个非常的下载例子
public class IntentServiceActivity extends MobileBaseActivity {
public final static String ACTION_TYPE_THREAD = "action.type.thread";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 启动异步任务
DownloadIntentService.startDownload(IntentServiceActivity.this, "image path");
}
static class DownloadIntentService extends IntentService {
public static final String EXTRA_DOWNLOAD_IMAGE = "com.tuacy.convenientinputedit.DOWNLOAD_IMAGE";
public DownloadIntentService() {
super("DownloadIntentService");
}
public static void startDownload(Context context, String path) {
Intent intent = new Intent(context, DownloadIntentService.class);
intent.putExtra(EXTRA_DOWNLOAD_IMAGE, path);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
String path = intent.getStringExtra(EXTRA_DOWNLOAD_IMAGE);
downloadTask(path);
}
}
private void downloadTask(String path) {
//模拟耗时操作
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//利用广播通知主界面更新
Intent intent = new Intent(IntentServiceActivity.ACTION_TYPE_THREAD);
intent.putExtra(EXTRA_DOWNLOAD_IMAGE, path);
sendBroadcast(intent);
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
}
IntentService的启动方式采用startService()。
4.2、IntentService源码分析
IntentService的源码也非常的简单。我们做一个简单的分析。
public class IntentService extends Service {
private volatile ServiceHandler mServiceHandler;
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);
}
}
@Override
public void onCreate() {
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;
}
...
}
通过startService()方式启动Service之后会先回调onCreate()方法,然后回调onStart()或者onStartCommand()方法(android2.0以下调用onStart(),android2.0之上调用onStartCommand()方法)。onCreate()方法里面启动了一个HandlerThread线程。并且mServiceHandler绑定到HandlerThread线程上去了。后面通过mServiceHandler发送的消息都会在这个HandlerThread线程里面执行。onStart()的回调函数马上就发送了一个消息出去,消息到ServiceHandler类的handleMessage()里面处理。调用onHandleIntent()函数,之后调用stopSelf()整个IntentService也就自动停止了。
以上就是对Android里面的异步处理做了一个简单的总结。
网友评论