[本文链接:https://blog.csdn.net/kaitao0/article/details/81095620,转载请注明出处]
前言:
查阅网上众多文章,又对源代码进行了解析,大致对Handler,Asyntask,handlerThread,intentService的异步原理有了一定了解,至此做一次总结,我觉得多个异步机制一同掌握会对其理解更加深刻。
一、Handler
首先是最常用的handler有两个使用方式
1.post(runnable) 延时操作
用法:
new Handler().post(new Runnable() {
@Override
public void run() {
textView.setText("This is post");//更新UI
}
});
post内部代码实现:可以看出内部也是调用sendMessage方法。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
2.sendMessage(message) 从子线程到主线程消息传递
- UI 线程 主线程 ActivityThread
- Message Handler 发送和处理的消息,由 MessageQueue 管理。
- MessageQueue 消息队列,用来存放通过 Handler 发送的消息,按照先进先出执行,内部使用的是单链表的结构。
- Handler 负责发送消息和处理消息。
- Looper 负责消息循环,循环取出 MessageQueue 里面的 Message,并交给相应的 Handler 进行处理。
看一下Handler构造源源码:
由此可见handler构造中创建了Lopper对象,而又由Lopper对象创建了MessageQueue,也就是说当Handler被new出来后就被绑定在由线程中,同时创建了Lopper,MessageQueue与之协作工作,开启Looper.myLooper()后才能从MessageQueue中获取消息。
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在往下看Looper是从哪里获取到的呢?
以下代码看出Looper.myLooper()是从threadLocal.get()中获取的,也就是说Looper.prepare()创建looper时,looper被保存到ThreadLocal中,创建Handler()时就可以在threadLocal中获取looper了。
ThreadLocal 就是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据,这样就保证了一个线程对应了一个 Looper,从源码中也可以看出一个线程也只能有一个 Looper
,否则就会抛出异常。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
在看下面这段英文,如果获取的Lopper对象为空因为是没有通过Looper.prepare()创建Looper
"Can't create handler inside thread that has not called Looper.prepare()");
??那looper什么时候被创建的呢?----当然是UI类被创建时一同创建的。
总结
应用启动时会启动 UI 线程也就是主线程 ActivityThread,在 ActivityThread 的 main 方法中会调用 Looper.prepareMainLooper( ) 和 Looper.loop ( ) 启动 Looper 。
Looper 启动时会创建一个 MessageQueue 实例,并且只有一个实例,然后不断从 MessageQueue 中获取消息,无则阻塞等待消息,有则调用 msg.target.dispatchMessage(msg) 处理消息。
我们在使用 Handler 时 需要先创建 Handler 实例,Handler 在创建时会获取当前线程关联的 Looper 实例 ,和 Looper 中的消息队列 MessageQueue。然后在发送消息时会自动给 Message 设置 target 为 Handler 本身,并把消息放入 MessageQueue 中,由 Looper 处理。Handler 在创建时会重写的
handleMessage 方法中处理消息。
如果要在子线程中使用 Handler 就需要新建 Looper 实例,传给 Handler 即可。
二、AsynTask
什么是AsynTask?
轻量级的内部类,在类中可以实现异步操作,可以在UI线程与非UI线程进行切换,它本质上就是一个封装了线程池和handler的异步框架,AsyncTask泛型中三个参数,1传入参数类型,2进度,3返回参数类型,内部五个方法
AsyncTask的机制原理
线程池中的工作线程执行doInBackground(params)方法执行异步任务
当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数
使用:
private class ATask extends AsyncTask<String, Integer, String> {
//onPreExecute方法用于在执行后台任务前做一些UI操作
@Override
protected void onPreExecute() {
textView.setText("loading...");
}
//doInBackground方法内部执行后台任务,不可在此方法内修改UI
@Override
protected String doInBackground(String... params) {
return new String("返回数据");
}
}
//onProgressUpdate方法用于更新进度信息
@Override
protected void onProgressUpdate(Integer... progresses) {
progressBar.setProgress(progresses[0]);
textView.setText("loading..." + progresses[0] + "%");
}
//onPostExecute方法用于在执行完后台任务后更新UI,显示结果
@Override
protected void onPostExecute(String result) {
textView.setText(result);
}
//onCancelled方法用于在取消执行中的任务时更改UI
@Override
protected void onCancelled() {
textView.setText("cancelled");
progressBar.setProgress(0);
}
}
}
源码解析:
- AsyncTask构造中,查看postResult(result)方法,result参数是doInBackground方法执行后返回参数,
- 最终postResult创建Handler,Message.....发送异步消息,
public 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);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
总结:
AsyncTask实际就是对handler的二次封装。
三、handlerThread
handerThrad产生的背景
当开启一个子线程做耗时操作是非常消耗资源的,线程执行完就会被销毁,如果在一段时间内又要做一个耗时任务,这时候又重新创建线程做耗时操作,多次创建和销毁线程是很消耗系统资源的,通过阻塞等待保持性能最优。
一般启动一个线程:
new Thread(){...}.start();
handler
Handler handler = new Handler(){...};
HandlerThread的用法
//String代表线程名,priority代表优先级
HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();//创建HandlerThread时要把它启动了,即调用start()方法。
// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
// 使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
//定义要发送的消息
Message msg = Message.obtain();
msg.what = 100; //消息的标识
//通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
// 结束线程,即停止线程的消息循环
mHandlerThread.quit();
源码解析:
HandlerThread本身继承Thread
- 构造参数:String代表线程名,priority代表优先级。优先级范围为-20到19,默认为0,优先级越高,获得的CPU资源更多,反之则越少。-20代表优先级最高,反之19最低。
- onLooperPrepared子类需要重写的方法,在这里做一些执行前的初始化工作
- 调用start()执行run()后,帮我们创建了 Looepr,notifyAll()/Looper 已经创建,唤醒阻塞在获取 Looper 的线程,Looper.loop()开始循环。
- getLooper() 获取当前线程的 Looper,如果线程启动后,Looper 还没创建,就 wait() 等待 创建 Looper 后 notify
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
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() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
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() {
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 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 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本身就是一个线程,但与线程不同的是HandlerThread封装了Looper与Handler,避免重复多次开启线程导致内存的额外消耗。
四、IntentService
什么是IntentService?
IntentService是继承并处理异步请求的一个类,IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统的Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要手动去控制stopSelf(),另外可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且每次只会执行一个工作线程,执行一个工作线程,执行完第一个再执行第二个串行的
使用:name必传线程名称,onHandleIntent方法异步
public class MyIntentService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);//线程名称
}
/**
*实现异步任务的方法,在此方法中可以做异步操作
* @param intent activity传来的数据在intent中
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//可以多次被执行
}
}
源码解析
首先是IntentSerivce的每次启动都执行的onCreate()
- 方法中创建了handlerThread;证明IntentSerivce内部就是handlerThread做异部消息的传递的。
- 方法中又创建了sericeHandler,它是继承Handler的Handler做了内部封装,构造方法参数又要传入handlerThread的looper,所以它就成了处理异步线程的执行类
@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);
}
再看,IntentService是怎么启动异步任务的,通过ServiceHandler.sendMessage()传递消息,最终这个方法肯定会发送到handlerThread中去处理,因为handlerThread持有looper对象,然后再调用ServiceHandler方法
handleMessage中调用onHandleIntent抽象方法给其它子类继承做些实际的操作,主动又调用了stopSelf方法停止服务
PS:stopSelf无参数会立即停止该服务,有参数会等待所有消息都处理完后才会终止任务
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
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);
}
}
总结:
ItentService本质就是实现封装了handlerThread + handler开启循环的线程利用Handler发送消息构成了异步框架,IntentSerice内部就是通过发送消息的方式交给handlerThread,由handler当中的looper处理消息,是按照顺序消息对列中取出任务的, 当有多个后台任务的时候也会按照顺序来执行的 在继承ItentService的时候,可以在onHandleIntent方法里做耗时操作。