!个人研究探索整理
异步处理
目的:提高程序响应性
体现:
耗时操作
网络数据(图片)加载
数据库查询
复杂业务逻辑处理
开发中涉及UI的操作必须遵守单线程模型的原则,因为Android的UI操作并非线程安全,并且这些操作必须在UI线程中执行。
单线程模型的两条法则:
1. 不要阻塞UI线程
2. 确保只在UI线程中访问Android UI工具包
几种其他线程访问主线程的方法
[代码]java代码:
1 2 3 4 5 6 7 |
|
当需要实现一些很复杂的操作并需要频繁地更新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 |
|
//线程池是静态变量,所有的异步任务都会放到这个线程池的工作线程中执行
我们在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 |
|
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 |
|
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 |
|
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对象到主线程队列中,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对象则直接进行处理