android 主线程 与 handler,Android中的Handler和AsyncTask的区别

问题的由来

最近老看见有人问Android中的Thread与AsyncTask的区别,因此研究了一下,总结如下。

第一,如果真心想弄清楚这两个的具体用法,最好的方法是把Message、MessageQueue、Looper、Handler、HandlerThread和AsyncTask这六个类的源代码全部看懂。

第二,如果觉得困难,那么就来看看我的这篇总结文章吧。

Handler类,可以理解为消息/任务处理者,Handler类是为了与其他几个类一起完成android的消息循环处理过程,是必不可少的一个类。它主要有两个用途:一是为了在将来某个时间点处理一个消息或者执行一个任务;二是将一个任务放入队列,以便它可以在另外的线程中执行。

AsyncTask类,可以理解为异步任务执行者;这个类的设计目的很明确,就是为了“执行一个较为耗时的异步任务(最多几秒钟),然后更新界面”。这种需求本可以使用Handler和Thread来实现,但由于编码较为复杂,因此Android提供了AsyncTask类。

几个规则

正式开始讨论Handler和AsyncTask之前,有几个Android的规则要再强调一次:

只能在UI线程中访问界面;

UI线程被阻塞(大概5秒钟)后会导致ANR(Application Not Responding)错误。

因此我们要使用Handler来创建消息循环,或者使用AsyncTask来创建异步任务来进行操作。

Handler类的使用场景

消息循环三兄弟Handler、Looper和HandlerThread

Handler用来和Looper、HandlerThread一起建立一个具有消息循环的子线程。如果这三个类中有一个你不理解,那么你三个都不会理解,也不会理解Android的消息循环与处理机制。

Looper环形使者(顺便说一下这个电影很好看):Looper中带有一个MessageQueue,即消息队列,Looper负责轮询此消息队列,将消息取出后交由Handler来处理。

HandlerThread消息循环线程:即Handler执行的线程,此线程大部分时间都在运行Looper.loop()方法,即消息轮询方法;它会通过getLooper方法返回一个Looper对象,Handler需要使用此对象作为参数创建对象。

Handler处理者:负责接收消息、发送消息(sendMessage等方法)和处理消息(handleMessage方法),我们需要重载handleMessage方法来处理各种消息。

一个具有完整消息循环的线程必然包括Looper、Handler和HandlerThread对象(或者你自己用Thread类来写一个线程),其中Looper对象内部还包含一个MessageQueue对象,我们这样来建立一个消息循环子线程。

Looper anotherLooper ;

AnotherHandler anotherHandler;

HandlerThread handlerThread;

class AnotherHandler extends Handler {

public AnotherHandler(Looper looper) {

super(looper);

}

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

if (msg.what == 1234) {

Toast.makeText(MainActivity.this, String.format("Another Handler : ProcessId=%d,ThreadId=%d",getProcId(),getThreadId()), Toast.LENGTH_LONG).show();

Log.i(TAG,"message in Another Handler");

}

}

}

handlerThread = new HandlerThread("handlerThread",Process.THREAD_PRIORITY_BACKGROUND);

handlerThread.start();

anotherLooper = handlerThread.getLooper();

anotherHandler = new AnotherHandler(anotherLooper);1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

211

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

记住,此处HandlerThread类不是为了运行一段自定义的代码来完成某个任务,而是为了让把消息循环运行起来,即运行Looper.loop()方法。

记住几个原则:

HandlerThread中创建了Looper对象,可以使用getLooper方法来得到这个Looper对象;

使用该Looper对象作为参数来创建Handler,才能使得此Handler能够处理消息;如果Handler对象创建时不带有对象,那么Handler会自动获取当前线程关联的Looper对象。因此若Handler是在主线程(即UI线程)中创建的,则Handler就会处理UI线程中的消息队列;如果Handler是在其他线程(此线程未创建Looper对象)中创建的,则此Handler不能处理消息。

因此,若我们不使用HandlerThread,而是直接使用Thread类,则代码如下:

class LooperThread extends Thread {

public Handler mHandler;

public void run() {

Looper.prepare();

mHandler = new Handler() {

public void handleMessage(Message msg) {

// process incoming messages here

}

};

Looper.loop();

}

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

151

2

3

4

5

6

7

8

9

10

11

12

13

14

15

我们会发现上面的代码例子中不能得到那个mHandler,因此收发消息时没有Handler对象可以引用,若对多线程的并发很熟悉的人可以扩展上面的代码,安全的取得该Handler对象,否则还是直接使用HandlerThread为好。

其实消息循环是五兄弟,还要加上消息(Message类)和消息队列(MessageQueue类),不过由于Message较为简单,而MessageQueue又被隐藏得很好,几乎不会被普通用户见到,因此就省略不介绍了,可以自己去看相关文档。

在主线程中使用Handler

好了,相信很多不熟悉的人看上面的消息循环三/五兄弟已经看晕了,会问“为什么我用了好久的Handler,却根本就没有见过Looper和HandlerThread呢?”

其实,那是因为绝大部分时间,你只在主线程(也就是UI线程)中使用Handler,此时Handler会默认使用主线程的Looper和Thread。可以这样理解,普通的Android应用一旦运行起来,就会创建主线程和一个主Looper,它们默默的处理着界面消息,这时你new了一个Handler并重载了它的handMessage方法,你的这个Handler对象就加入到消息的处理中来了。

在子线程使用Handler

在子线程中使用Handler则比主线程复杂,因此此时就必须使用Looper和Thread/HandlerThread类了,例子仍可以参考上一篇blog。

AsycnTask怎么来的?

好了,终于说到AsycnTask了。前面已经说过,它是一个辅助类,就是为了将Handler、Thread等封装为一个异步执行框架,供Android Coder可以方便的使用。其主要目的是为了“在其他线程中执行一个耗时操作,并随时报告执行进度给UI线程,执行完成后将结果报告给UI线程”。

AsyncTask使用方法

AsyncTask的使用方法其实Android developer中已经说得非常清楚了,我自认不能比他说得更好,因此择重点翻译一次:

AsyncTask使用时必须作为基类被扩展,子类至少重载一个方法doInBackground,另一个方法onPostExecute也经常被重载,代码例子如下:

private class DownloadFilesTask extends AsyncTask {

protected Long doInBackground(URL... urls) {

int count = urls.length;

long totalSize = 0;

for (int i = 0; i < count; i++) {

totalSize += Downloader.downloadFile(urls[i]);

publishProgress((int) ((i / (float) count) * 100));

// Escape early if cancel() is called

if (isCancelled()) break;

}

return totalSize;

}

protected void onProgressUpdate(Integer... progress) {

setProgressPercent(progress[0]);

}

protected void onPostExecute(Long result) {

showDialog("Downloaded " + result + " bytes");

}

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

211

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

AsyncTask含有三个泛型参数:

Params, 异步任务执行所需的参数类型;

Progress, 异步任务执行进度的类型;

Result, 异步任务执行结果的类型。

这三个参数不必全部使用,不使用的参数置为Void即可,例如:

private class MyTask extends AsyncTask { ... }11

AsyncTask的四个重要方法:

当一个异步任务被执行时,要经历四步:

onPreExecute(),在UI线程中执行,它会在异步任务开始前执行,一般用来设置任务参数;

doInBackground, 最重要的方法,在子线程中执行(事实上,只有它在子线程中执行,其他方法都在UI线程中执行)。当onPreExecute结束后,本方法立刻执行,它用来进行后台的耗时计算,异步任务的参数会被传给它,执行完成的结果会被送给第四步;执行途中,它还可以调用publishProgress 方法来通知UI线程当前执行的进度;

onProgressUpdate, 当publishProgress 被调用后,它在UI线程中执行,刷新任务进度,一般用来刷新进度条等UI部件;

onPostExecute, 当后台的异步任务完成后,它会在UI线程中被调用,并获取异步任务执行完成的结果。

小结

通过上面的分析,我们可以看到,其实Handler和AsyncTask的关系不太大,可以说不是同一个层面上的东西。只不过,它们有着一些共同的目的,就是都可以用来异步执行一些代码,避免阻塞UI线程。另外,AsyncTask实际上是使用Handler和线程池技术来实现的。

要想真正的了解它们之间的异同,还是那句话:RTFSC – Read The Fucking Source Code。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值