Android多线程
一个Android的应用程序运行在一个独立的进程中,运行在一个独立的虚拟机(dvk)上。(进程名为包名)
Android应用程序开启后,默认开启一个主线程(UI线程)
Activity,Service,BroadcastReceive组件运行在主线程中
Android 应用程序退出后,保留空UI线程,可以加快应用程序启动速度。
用户不能再UI主线程中做耗时的操作,一旦操作超过5s,应用程序抛出一个ANR(application not respond)
如何避免ANR错误?
将耗时的操作放入到子线程中。(耗时的操作包括:长时间的休眠,计数,联网,复杂的运算)
只有主线程才能操作Widget控件。
如果在子线程中出现操作Widget控件,系统抛出CalledFromWrongThreadException异常。
系统为什么要这么做?
避免出现同步问题。
Handler机制
作用
主要为了解决非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容器)
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
/**
* @author Administrator
* 事件驱动的应用程序
*/
public class MainActivity extends Activity implements OnClickListener {
Button btnStart;
TextView tv;
int count = 0;
boolean isRun;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStart = (Button) findViewById(R.id.button1);
btnStart.setOnClickListener(this);
findViewById(R.id.button2).setOnClickListener(this);
tv = (TextView) findViewById(R.id.textView1);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v.getId() == R.id.button1){
btnStart.setEnabled(false);
count = 0;
isRun = true;
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
// 修改Widget控件的值
// tv.setText(String.valueOf(count));
while (isRun) {
count++;
// 创建消息对象
Message msg = new Message();
msg.arg1 = count;
//设置消息类型
msg.what = 2;
// 发送消息 (发给底层的消息队列)
handler.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
handler.sendEmptyMessage(1);
}
}).start();
}else if(v.getId() == R.id.button2){
isRun = false;
}
}
Handler handler = new Handler(){
//如果消息队列中有消息,系统自动调用该方法处理消息
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if(msg.what == 2){
int count = msg.arg1;
tv.setText(String.valueOf(count));
}else if(msg.what == 1){
//修改按钮属性
btnStart.setEnabled(true);
}
}
};
}
向消息队列发送消息,1000毫秒后执行Runnable对象中的代码。
myHandler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Log.e("Test", "thread name = "+Thread.currentThread().getName());
}
}, 1000);
异步任务(Async Task)
概念
封装多线程和Handler机制。给用户提供重写接口的方式,不需要用户手动创建子线程和Handler对象。
异步任务的优点
Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI主线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但是也有缺点,代码臃肿,在多个任务同时执行时,不易对线程进行精确的控制。为了简化操作,Android1.5提供了一个工具类AsyncTask,它是创建异步任务变的更加简单,不再需要编写任务线程和Handler实例就可完成任务。
异步任务的局限性
多个异步任务不能同时执行,在某个时间内,只能执行一个异步任务。
执行异步任务的步骤:
- 1 execute(Params … params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。
- 2 onPreExecute(),在execute(Params… params
)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。 - 3 doInBackground(Params…
params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接受输入参数和返回计算结果在执行过程中可以调用publishProgress(Progress…values)来更新进度信息。作用在非UI线程上。 - 4 onProgressUpdate(Progress… values),在调用publishProgress(Progress…
values)时,此方法被执行,直接将进度信息更新到UI组件上。 - 5 onPostExecute(Result
result)当后台操作结束时,此方法将被调用,计算结果讲座为参数传递到方法中,直接将结果显示到UI组件上。
import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;
//Params 执行异步任务时,传入的参数
//Progress 异步任务的进度
//Result 结果
public class MyTask extends AsyncTask<Void, Integer, Void> {
TextView tv;
public MyTask(TextView tv) {
// TODO Auto-generated constructor stub
this.tv = tv;
}
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
Log.d("Test", "onPre");
}
//该方法运行在非UI线程中
//该方法中放耗时操作
@Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
int count = 0;
while(!isCancelled() && count < 10){
count++;
//发布进度
//触发系统自动调用onProgressUpdate
//相当于handler.sendMessage()
publishProgress(count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
//相当于handlerMessage()
//该方法运行在UI线程中
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
if(isCancelled()){
return;
}
//操作UI控件
int count = values[0];
tv.setText(String.valueOf(count));
}
//该放在在取消异步任务之后运行
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
Log.d("Test", "onPost");
}
}