Android多线程
-
一个Android的应用程序运行在一个独立的进程中,运行在一个独立的虚拟机(dvk)上。 (进程名为包名)
-
Android应用程序开启后,默认开启一个主线程(UI线程)
Activity,Service,BroadcastReceiver组件运行在主线程中
Android应用程序退出后,保留空UI线程,可以加快应用程序启动速度 -
用户不能在UI主线程中做耗时的操作,一旦该操作超过5s,应用程序抛一个ANR异常(Application not respond)。
如何避免ANR错误?
将耗时的操作放入子线程中。(耗时的操作包括:长时间的休眠,计数,联网,复杂的运算。) -
只有主线程才能操作Widget控件。
如果在子线程中操作Widget控件,系统抛出CalledFromWrongThreadException异常。 -
系统为什么要这么做?
避免出现同步问题。
Handler机制。
-
Goolge为什么设计这套机制?
主要是为了解决非UI线程中不能更新Widget控件的问题 -
Handler机制剖析
子线程发送消息给底层的消息队列。
handler.sendMessage(msg)
主线程查询消息队列,处理消息对象。
handlerMessage(msg)
MessageQueue 消息队列
负责存储消息对象 -
Looper
给UI线程安排代码,一个UI线程只能有一个Looper对象,否则多个Looper对象都在UI线程上安排代码,解决冲突就是个大问题。 Looper对象会线性安排在UI线程上执行的代码,它通过一个队列管理各个Handler对象提交的代码。 -
Message消息对象
//从消息池中获取消息对象
Message msg = handler.obtainMessage();
//在消息对象上绑定int类型数据
msg.arg1 = count;
//在消息对象上绑定其它类型数据
msg.setData(Bundle); //Bundler为数据集(类似于HashMap容器)
- 向消息队列发送消息,1000毫秒后执行Runnable对象中的代码。
myHandler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Log.e("Test", "thread name = "+Thread.currentThread().getName());
}
}, 1000);
异步任务(AsyncTask)
-
概念
封装多线程和Handler机制。给用户提供重写接口的方式,不需要用户手动创建子线程和Handler对象。 -
异步任务的优点
Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI主线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但是也有缺点,代码臃肿,在多个任务同时执行时,不易对线程进行精确的控制。为了简化操作,Android1.5提供了一个工具类AsyncTask,它是创建异步任务变的更加简单,不再需要编写任务线程和Handler实例就可完成任务。 -
异步任务的局限性
多个异步任务不能同时执行,在某个时间内,只能执行一个异步任务。 -
执行异步任务的步骤:
- execute(Params... params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。
- onPreExecute(),在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。
- doInBackground(Params... params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调publishProgress(Progress... values)来更新进度信息。
- onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。
- onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。
- 取消异步任务
/**
*cancel(true) 取消当前的异步任务,传入的true,表示当中断异步任务时继续已经运行的线程的操作,
*但是为了线程的安全一般为让它继续设为true
**/
mTask.cancel(true);
/**
* 但是重新运行后会发现还是不能起到效果,
* 注意:这是因为cancel方法只是发出一个请求取消异步任务的信号,
* 将对应当前的异步任务标记为CANCEL状态,而并不是真正取消线程的执行,
* 而此时异步任务中的线程仍然在执行并没有结束
* 所以效果依然是这样的,并且在java中我们是无法直接暴力将一个线程给停止掉
* 既然我们知道无法去取消一个已经正在运行的线程,但是我们如何去解决这个BUG呢?
* 在异步任务中还给我们提供一个isCanceled的回调方法,也就是当我已经给当前的异步任务
* 调用了cancel(true)方法,发出一个请求取消异步任务的信号,那么此时的isCanceled的回调方法
* 会直接返回一个true,那么我们就可以通过判断当前异步任务isCanceled是否为true,来终止
* 线程中的操作而不是去终止线程,从而达到了界面显示好像线程中的操作被终止了,而实际上
* 该线程依然在运行
* */
- 注意
- 异步任务对象只能执行一次。
- 异步任务对象必须在UI主线程中创建。execute(Params... params)方法必须在UI线程中调用。
- 不要手动调用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)这几个方法。
- 不能在doInBackground(Params... params)中更改UI组件的信息。
网友评论