在Android开发中,主线程(UI线程)不能执行耗时的操作,通常将一些耗时的操作使用异步任务的方式进行处理,简单概括一下:
1.只能在UI线程操作UI视图,不能在子线程中操作;
2.不能在UI线程中执行耗时操作,否则会阻塞UI线程,引起ANR、卡顿等问题。
接下来看一下在Android开发中,经常用到的处理耗时操作用的异步任务实现方式及实现原理:
一.Thread
创建一个Thread是最简单直接的方式,在Thread内部去执行耗时的操作,实现方式如下:
Thread t = new Thread(new Runnable() {
@Override
public void run() {
//执行耗时操作
}
});
t.start();
上面仅仅是在Thread内部执行耗时的操作,如果在执行完耗时操作后,需要UI来进行更新,那应该如何操作呢?接下来继续看:
二.Thread+Handler
Android中提供了Handler机制来进行线程之间的通信,可以使用异步方式:Thread + handler 来进行异步任务执行及UI更新,实现方式如下:
详细了解Handler的原理请参考Android Handler原理详解
new Thread() {
public void run() {
//执行耗时操作.....
//更新UI
mUIHandler.sendMessage(mUIHandler.obtainMessage(UIHandler.MSG_UPDATE_UI,2));
}
}.start();
//UI线程Handler
private static class UIHandler extends Handler {
private final static int MSG_UPDATE_UI = 1;
private final WeakReference<HandlerFragment> mFragment;
private UIHandler(HandlerFragment fragment) {
mFragment = new WeakReference<>(fragment);
}
@Override
public void handleMessage(Message msg) {
HandlerFragment fragment = mFragment.get();
switch (msg.what) {
case MSG_UPDATE_UI:
fragment.updateUI((int)msg.obj);
break;
default:
break;
}
}
}
从以上可以看到,在Thread内部执行耗时操作后,然后调用Handler来通知主线程更新UI。
这样的话,每次执行耗时请求,都需要new一个Thread,会导致开销过大,可以通过以下方式来进行改进:
子线程内部创建Looper+Handler
new Thread() {
public void run() {
Looper.prepare();
mNoUIHandler = new NoUIHandler(handlerFragment);
Looper.loop();
}
}.start();
//工作线程Handler来执行耗时操作
private static class NoUIHandler extends Handler {
private final static int MSG_HANDLE = 1;
private final WeakReference<HandlerFragment> mFragment;
private NoUIHandler(HandlerFragment fragment) {
mFragment = new WeakReference<>(fragment);
}
@Override
public void handleMessage(Message msg) {
HandlerFragment fragment = mFragment.get();
switch (msg.what) {
case MSG_HANDLE:
fragment.handleMsg();
break;
default:
break;
}
}
}
private void handleMsg() {
//执行耗时操作......
//通知主线程更新UI
mUIHandler.sendMessage(mUIHandler.obtainMessage(UIHandler.MSG_UPDATE_UI,1));
}
//启动耗时操作
mNoUIHandler.sendEmptyMessage(NoUIHandler.MSG_HANDLE);
通过以下改善,在Thread内部创建Looper,然后创建Handler,这样的话Thread就不会退出,就不需要频繁创建Thread,此时Handler使用的是子线程创建的looper,从而Handler消息处理(耗时操作)就在子线程里面,执行完耗时操作,通知主线程来更新UI;
这样的话,Thread一直不退出,是不是会造成内存泄露呢?
如果不再使用的话,直接通过mNoUIHandler.getLooper().quitSafely()来让Looper.loop()结束循环,Thread也就退出了。
三.HandlerThread
针对以上问题,Android也早就考虑到了,HandlerThread就出现了,结合了Thread+Looper+Handler进行封装实现,实现如下:
private HandlerThread mWorkHandlerThread;
private Handler mWorkHandler;
//首先创建HandlerThread
mWorkHandlerThread = new HandlerThread("handler thread");
//HandlerThread执行start()
mWorkHandlerThread.start();
//创建Handler将HandlerThread的Looper传入,从而在对应的线程内处理消息
mWorkHandler = new WorkHandler(mWorkHandlerThread.getLooper(), this);
//工作线程的Handler
private static class WorkHandler extends Handler {
private static final int MSG_START = 200;
private ControlService mService;
private WorkHandler(Looper looper, ControlService service) {
super(looper);
mService = service;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START:
mService.createClient();
break;
default:
break;
}
}
}
//发送处理消息
mWorkHandler.sendEmptyMessageDelayed(WorkHandler.MSG_START, delayTime);
通过上述可以看到,通过HandlerThread来执行异步任务相对简单一些,不需要自己创建Looper,创建一个HandlerThread,然后执行start()即可,看一下HandlerThread内部的实现:
HandlerThread源码分析
public class HandlerThread extends Thread {
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
@Override
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
......
Looper.loop();
}
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;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
HandlerThread继承了Thread,当调用start()方法后,会回调到HandlerThread内部的run()方法,在run()方法内部,执行了Looper.prepare()及Looper.loop();
提出一个小问题,getLooper()会不会返回null?
答案是否定的,可以看到在getLooper()内部有加锁及wait()相关操作,如果Looper没有创建好的话,会执行wait()等待,当run()方法内创建完成后执行notifyAll(),从而确保getLooper()不为null。
同理,如果不需要HandlerThread处理消息后,通过调用quitSafely()来退出Looper.loop()循环,从而结束HandlerThread。
四.IntentService
IntentService继承Service类,启动了一个异步服务任务,内部是通过HandlerThread来实现异步处理任务,先看下代码实现:
Intent intent = new Intent(this, WorkService.class);
intent.putExtra("name", "获取图片信息");
intent.putExtra("url", baseUrl);
//启动IntentService
startService(intent);
public class WorkService extends IntentService {
public WorkService() {
super("WorkService");
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
String url = intent.getStringExtra("url");
String name = intent.getStringExtra("name");
//执行耗时操作
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
从以上可以看到,实现也比较简单,通过startService()来启动IntentService,然后在onHandleIntent()内部执行耗时操作就可以了,看一下IntentService内部的实现原理:
IntentService源码分析
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
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;
}
@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;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
通过上述代码可以看到,onHandleIntent()是一个抽象方法,需要子类来实现的,IntentService内部是通过HandlerThread来实现的,在执行startService()后,会IntentService的onCreate()内部创建HandlerThread,确保多次调用startService()仅创建一次HandlerThread相关的操作,每次在执行onStartCommand()后,都是通过mServiceHandler.sendMessage(),最终会回调子类实现的onHandleIntent(),从而在方法内部执行耗时操作就可以了。
在执行完onHandleIntent()后,会自动调用stopSelf(msg.arg1)来结束该service,在onDestroy()内部会通过mServiceLooper.quit()来退出loop()循环,继而结束HandlerThread。
五.AsyncTask
AsyncTask是一个封装好的轻量级异步任务处理类,轻量级,一个异步任务使用AsyncTask很方便,多个异步任务使用HandleThread更好。
有一种场景非常适合使用AsyncTask:需要显示进度值的场景,使用这个封装好的类,方便理解,节省代码。
先看一下AsyncTask 这个抽象类的定义,当我们定义一个类来继承AsyncTask时,需要为其指定3个泛型参数:
public abstract class AsyncTask<Params, Progress, Result>
Params: 传递给异步任务执行时的参数的类型。
Progress: 异步任务在执行的时候将执行的进度返回给UI线程的参数的类型。
Result: 异步任务执行完后返回给UI线程的结果的类型。
代码实现如下:
private class WorkTask extends AsyncTask<String, Integer, Bitmap> {
//执行线程任务前的操作,工作在主线程
@Override
protected void onPreExecute() {
}
//接收输入参数、执行任务中的耗时操作、返回线程任务执行的结果
//该方法必须复写
@Override
protected Bitmap doInBackground(String... strings) {
//执行耗时操作.......
//可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
publishProgress(10);
return null;
}
//在主线程显示线程任务执行的进度
@Override
protected void onProgressUpdate(Integer... progresses) {
}
//接收线程任务执行结果、将执行结果返回给主线程显示到UI组件
@Override
protected void onPostExecute(Bitmap result) {
}
//将异步任务设置为:取消状态
@Override
protected void onCancelled() {
}
}
//创建AsyncTask子类的实例对象,注:AsyncTask子类的实例必须在UI线程中创建
WorkTask workTask = new WorkTask();
//手动调用execute(Params... params)从而执行异步线程任务
//注:必须在UI线程中调用;同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
workTask.execute(baseUrl);
通过以上可以看到,在使用AsyncTask时,先定义一个类来继承AsyncTask,耗时任务在doInBackground()里面执行,执行完后的结果通过onPostExecute(result)来返回。
使用时,先创建一个本地异步任务实例,然后执行execute(para)即可,注意:必须在UI线程中调用execute();同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常。
AsyncTask源码分析
public abstract class AsyncTask<Params, Progress, Result> {
public AsyncTask() {
this((Looper) null);
}
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
.......
};
mFuture = new FutureTask<Result>(mWorker) {
.......
};
}
//抽象方法,子类必须实现
protected abstract Result doInBackground(Params... params);
从上述可以看到,在AsyncTask的构造函数内,主要做了三件事:
①.获取主线程的Handler;Handler传入的Looper是主线程的Looper,所以该Handler工作在主线程;
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
.......
}
}
②.创建了一个WorkerRunnable实例;WorkerRunnable是一个抽象类,实现了Callable接口;
③.创建了一个FutureTask实例,将上面的WorkerRunnable实例作为参数传入,FutureTask是Runnable的子类,即后面在线程池中需要执行的runnable。
以上就是在创建完本地实例后,做的三项工作,接下来执行execute(para),看一下内部实现:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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;
}
通过以上可以看到,在执行execute(paras)后,会调用到executeOnExecutor(),在executeOnExecutor()内部主要执行了三件事:
①.先判断当前的状态,如果是RUNNING,直接抛异常,这就是一个AsyncTask实例不能执行两次的原因;接下来设置状态为RUNNING;
②.执行onPreExecute(),准备工作,在主线程执行;
③.将execute()传入的参数赋值给WorkRunnable实例,后续会用到,接着通过Executor.execute(mFuture)[sDefaultExecutor]来执行逻辑;
以上可以看到,最终是通过Executor.execute(mFuture)来执行逻辑,接下来看一下sDefaultExecutor是如何创建的:
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) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
创建逻辑如上,一目了然,当执行excute(mFuture)后,创建了一个Runnable实例,然后加入到mTasks的队列中,注意:此时还未执行;接下来mActive==null,执行scheduleNext(),在scheduleNext()内部取出mTasks里面的runnable,最终通过THREAD_POOL_EXECUTOR线程池来执行,注意该线程池是在静态代码块里面创建的,也就是说AsyncTask在整个Android系统中维护一个线程池,有可能被其他进程的任务抢占而降低效率。
简单总结一下execute()的执行过程:在AsyncTask在执行execute()时,会创建两个线程池,一个是SERIAL_EXECUTOR,负责统一管理线程,将需要执行的runnable加入到mTasks队列里面,顺序执行;另外一个是THREAD_POOL_EXECUTOR,负责执行线程,将mTasks队列里面的runnable进行执行。
看一下FutureTask内部的run()方法逻辑:
public void run() {
......
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
}
........
}
在run()内部会执行到WorkRunnable实例mWorker的call()方法,在返回去看看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);
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
在call()内部,异步线程的优先级已经被默认设置成THREAD_PRIORITY_BACKGROUND,不会与 UI 线程抢占资源,然后执行doInBackground(),mParams是在构造函数内部传入的,最后执行postResult(result)。
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
private Result postResult(Result result) {
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
在postResult()内部将result、AsyncTask对象封装成AsyncTaskResult作为object,通过handler进行处理,最终调用了AsyncTask的finish()方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
如果在执行execute()后返回结果前调用了cancle()方法,会调用onCancelled(),正常会调用onPostExecute(result),以上都是在主线程回调的。
六.总结
实现方式 | 优点 | 缺点 |
---|---|---|
Thread+Handler | 实现简单 | 1.代码规范性较差,不易维护 2.每次操作都会开启一个匿名线程,系统开销较大 |
HandlerThread | 1.内部已经实现了普通线程的Looper消息循环 2.可以串行执行多个任务 3.内部拥有自己的消息队列,不会阻塞UI线程 |
1.没有结果返回接口,需要自行处理 2.消息过多时,容易造成阻塞 3.只有一个线程处理,效率较低 4.线程优先级默认优先级为 THREAD_PRIORITY_DEFAULT,容易和 UI 线程抢占资源 |
IntentService | 1.只需要继承 IntentService,就可以在 onHandlerIntent 方法中异步处理Intent 类型任务 2.任务结束后 IntentService 会自行停止,无需手动调用 stopService 3.可以执行处理多个 Intent 请求,顺序执行多任务 4.IntentService 是继承自Service,具有后台Service的优先级 |
1.需要启动服务来执行异步任务,不适合简单任务处理 2.异步任务是由 HandlerThread 实现的,只能单线程、顺序处理任务 3.没有返回 UI 线程的接口 |
AsyncTask | 1.结构清晰,使用简单,适合与后台任务的交互 2.异步线程的优先级已经被默认设置成THREAD_PRIORITY_BACKGROUND,不会与 UI 线程抢占资源 |
1.结构略复杂,代码较多 2.每个 AsyncTask 只能被执行一次,多次调用会发生异常 3.AsyncTask在整个Android系统中维护一个线程池,有可能被其他进程的任务抢占而降低效率 |
以上就是Android系统中常用的异步任务的实现方式。
网友评论