手记

AsyncTask与Thread+Handler简要分析

   !个人研究探索整理

异步处理

目的:提高程序响应性

体现:

耗时操作

网络数据(图片)加载

数据库查询

复杂业务逻辑处理

开发中涉及UI的操作必须遵守单线程模型的原则,因为Android的UI操作并非线程安全,并且这些操作必须在UI线程中执行。

单线程模型的两条法则:

1. 不要阻塞UI线程

2. 确保只在UI线程中访问Android UI工具包

 

几种其他线程访问主线程的方法

[代码]java代码:

?

1

2

3

4

5

6

7

1.Activity.runOnUiThread( Runnable   )  

 

2.View.post( Runnable )  

 

3.View.postDelayed( Runnable, long )  

 

4.Hanlder

 

当需要实现一些很复杂的操作并需要频繁地更新UI时以上的方法会变的很繁琐。 所以,Android 1.5提供了一个工具类:AsyncTask,方便处理长时间运行的任务,并操作UI。相对来说AsyncTask更轻量级一些,不需要借助线程和Handler即可实现简单的异步处理。 但AsyncTask也并不完美,如当网络情况较差,异步任务不能尽快完成执行的情况下,新开的线程会造成界面不流畅,当开启的线程过多时,还可能出现FC。

 

AsyncTask

特点:

AsyncTask的规范型很强,能够时时反映更新的情况,Task在主线程之外运行,回调方法在主线程执行。

内部实现原理:

本质是一个线程池,所有提交的异步任务都会在这个线程池中的工作线程内执行,当工作线程需要跟UI线程交互时,工作线程会传递消息到UI线程创建的Handler,由Handler调用相关的回调方法,实现UI界面的更新。

成员变量:

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

private static final int CORE_POOL_SIZE = 5;                      //5个核心工作线程

 

    private static final int MAXIMUM_POOL_SIZE = 128;              //最多128个工作线程

 

    private static final int KEEP_ALIVE = 10;                           //空闲时线程的超市时间为10s

 

    private static final BlockingQueue<runnable>   sWorkQueue =

 

            new LinkedBlockingQueue<runnable>(10);               //等待队列

 

private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,

 

            MAXIMUM_POOL_SIZE,   KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,   sThreadFactory);</runnable></runnable>

 

//线程池是静态变量,所有的异步任务都会放到这个线程池的工作线程中执行

 

我们在new AsyncTask();实例化的时候,会调用父类的AsyncTask()构造函数

构造函数:

构造函数参数:三种泛型类型

1.Params:传递给后台任务的参数类型,比如HTTP请求的URL

2.Progress:后台计算执行过程中,进度单位的类型(就是后台程序已经执行了百分之几了)。

3.Result:后台执行返回的结果的类型,比如String,List等

通常我们使用Void标识不使用的类型

任务的当前状态:

在一个任务的生命周期内,每个状态只会设置一次

PENDING  尚未执行

RUNNING 正在执行

FINISHED 表示onPostExecute已经完成

后台线程运行的五个状态:

1、准备运行,2、正在后台运行,3、进度更新,4、完成后台任务,5、取消任务

对应的回调方法(我们需要做的就是实现这些方法):

1、准备运行:onPreExecute(),该回调方法在任务被execute()提交到线程池之后立即由UI线程调用。这个步骤通常用来做一些准备工作,在UI上显示进度条等。

2、正在后台运行:doInBackground(Params...),该回调方法由后台线程在onPreExecute()方法执行结束后立即调用。通常在这里执行耗时的后台计算,计算的结果必须由该方法返回,并被传递到onPostExecute()中。在该方法内也可使用publishProgress(Progress...)来发布一个或多个进度单位(units of progress),这些值将会在onProgressUpdate(Progress...)中被发布到UI线程,该方法为抽象方法,子类必须实现。

3. 进度更新:onProgressUpdate(Progress...),该方法由UI线程在publishProgress(Progress...)方法调用完后被调用,一般用于动态地显示一个进度条。

4. 完成后台任务:onPostExecute(Result),当后台计算结束后调用。后台计算的结果会被作为参数传递给该方法。

5、取消任务:onCancelled (),运行在UI线程,在AsyncTask的cancel()方法后执行,可在取消线程操作时调用

 

要取消正在运行的任务时:检查asynctask状态,可以为RUNNING, FINISHED,PENDING ,只能在非FINISHED状态取消

示例:

主线程中:

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

private AsyncTask asynctask;

 

//启动新的线程

 

asynctask= new AsyncTask();// 实例化

 

asynctask.execute();// 将task放入线程池,执行task

 

//如果Task还在运行,则先取消它

 

if (asynctask!= null && asynctask.getStatus() != AsyncTask.Status.FINISHED) {

 

asynctask.cancel(true);

 

}

 

protected Void doInBackground(Void... params) {

 

// Task被取消了,马上退出循环

 

if(isCancelled()){//如果task在正常完成前被取消则返回ture

 

 return null;

 

}

 

}

 

protected void onProgressUpdate(Void...   values) {

 

super.onProgressUpdate(values);

 

//Task被取消了,不再继续执行后面的代码

 

if(isCancelled()){

 

return;

 

}

 

}

 

AsyncTask运行过程

 onPreExecute(),当任务执行之前开始调用此方法,可以不用实现。 doInBackground(Params…) 此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中调用 publicProgress(Progress…)更新任务的进度。 onProgressUpdate(Progress…) 此方法在主线程执行,用于显示任务执行的进度。 onPostExecute(Result) 此方法在主线程执行,任务执行的结果作为此方法的参数返回

当任务的状态发生改变时(1、执行成功2、取消执行3、进度更新),工作线程会向UI线程的Handler传递消息。Handler要处理其他线程传递过来的消息。在AsyncTask中,InternalHandler是在UI线程上创建的,它接收来自工作线程的消息,并调用相关的回调函数。

AsyncTask的调用规则:

 1.必须在UI线程中创建Task实例

 2.必须在UI线程执行execute

 3.不能手动调用onPreExecute()、onPostExecute、doInBackground、onProgressUpdate

 4.只能调用一次execute,如果尝试重复调用,则会抛出异常( 当任务正在执行或者已经完成,会抛出IllegalStateException)

 

AsyncTask总结:

1、 AsyncTask的本质是一个静态的线程池,AsyncTask派生出的子类可以实现不同的异步任务,这些任务都是提交到静态的线程池中执行。

2、线程池中的工作线程执行doInBackground(mParams)方法去执行异步任务

3、当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler接收这些消息,并调用相关的回调函数进行处理

 

Thread+handler 非UI线程更新界面

本质是子线程发送消息到UI线程,通知UI线程进行界面更新,由UI线程中的handler对象负责相应的处理。

需注意:不能直接在Thread中操作UI,如

[代码]java代码:

?

1

2

3

4

5

6

7

8

9

new Thread(new Runnable){

 

public void run(){

 

view.invalidate();//违背了单线程模型

 

}

 

}.start();

 

Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。如果在非UI线程直接对UI进行了操作,

报错:CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views

此时,我们可以使用handler对象的post(Runnable)方法发送一个Runnable对象到主线程中,Handler的handleMessage方法实际是由关联有该消息队列的UI thread调用,因此,并不违反单线程模型

如:

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

    handler.post(new Runnable() {

 

@Override

 

public void run()   {

 

// TODO Auto-generated method stub

 

view.invalidate();  //在与handler相关联的UI线程中操作UI

 

}

 

});



Thread : Thread.start()方法,开启新的线程,当前线程(主线程)并发执行;run()方法会被新开启的线程运行

   Thread.run(),用于封装线程运行的任务代码,直接用创建的线程对象调用,并没有产生新的线程,在当前正在运行的线程(如,主线程)执行run方法

Handler:接收子线程发送的对象,并负责处理该对象,用来更新界面,连接其他线程与主线程的桥梁,实现线程间通信。提供异步消息处理机制,包含两个队列,一个是线程队列,一个是消息队列。使用post方法将线程对象添加到线程队列中,使用sendMessage(message)方法将消息添加到消息队列中。

非UI线程发送消息到UI线程分为两部:

子线程发送消息到UI线程的消息队列

处理发送到UI线程的消息,子类化handler,实现public void handleMessage (Message msg)方法

Handler中分发消息的方法:(long对象为时间)

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

post(Runnable) //立即执行runnable对象

 

       postAtTime(Runnable,long)     //指定时间执行

 

       postDelayed(Runnable   long)  //延时执行

 

       sendEmptyMessage(int)     //发送只包含what值的消息

 

       sendMessage(Message)      //立即发送

 

       sendMessageAtTime(Message,long)   //指定时间发送

 

       sendMessageDelayed(Message,long)   //延时发送

 

 以上post类方法发送一个Runnable对象到主线程队列中,sendMessage类方法, 发送一个Message对象到队列中,等待UI线程处理,

使用post方法将Runnable对象放到Handler的线程队列中,该Runnable的执行其实并未单独开启线程,而是仍然在当前Activity的UI线程中执行,Handler只是调用了Runnable对象的run方法。

handler:默认运行在当前的Looper对象中,一个Looper只有在处理完一个message对象后,才会处理下一个,因此,如需并发处理,可考虑使用不同的Looper

message与runnable:

message是消息,可以传递一些参数,Handler获取这些信息并做出处理,而Runnable则是直接给出处理的方法

停止线程:调用Handler对象的removeCallbacks(Runnable r)从线程队列中移除线程对象,使线程停止执行

清除消息:Handler对象的handler.removeMessages(what)/handler.removeMessages(what,object)

当我们不清楚在UI线程还是在其他线程执行的时候,可以使用runOnUiThread(action);//....

Thread+handler于AsyncTask

两者的区别在于:

AsyncTask可以通过实现onPostExecute方法直接操作UI,该方法在UI线程调用,耗时操作在后台线程doInBackground,不适合无限的循环

Thread.start(),开启一个新的线程与UI线程并行,并不能直接操作UI(由于单线程模型的原因),因此,发送handler对象的

message/runnable对象到消息队列/主线程队列中,由handler接收消息并进行处理,而runnable对象则直接进行处理

原文链接:http://www.apkbus.com/blog-35555-68766.html

0人推荐
随时随地看视频
慕课网APP