一.多线程
1.为什么要在Android中使用多线程
提高用户体验或避免ANR
- 在事件处理代码中需要使用多线程,响应时间超过5s,即会出现ANR(Application is not responding),并因为响应较慢导致用户体验很差
2.ANR详解
- Android的main线程负责处理UI的绘制,为了防止应用程序反应较慢导致系统无法正常运行做如下处理
- 当用户输入事件(Activity)在5秒内无法得到响应,那么系统会弹出ANR对话框
- BroadcastReciever超过10秒没执行完也会弹出ANR对话
- 事件处理的原则:所有可能耗时的操作都放到其他线程去处理
3.异步
应用中有些情况下并不一定需要同步阻塞去等待返回结果,可以通过多线程来实现异步
例如:某个Activity需要从云端获取一些图片,加载图片比较耗时,这时需要使用异步加载,加载完成一个图片刷新一个
4.主线程Activity与子线程
- 默认启动的第一个Activity成为主线程
- 由此Activity创建的线程(子线程)无法对主线程控制的内容进行修改
public class MainActivity extends AppCompatActivity {
private Button button;
private TextView textView;
private int count;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
button=(Button) findViewById(R.id.button);
textView=(TextView) findViewById(R.id.textView);
button.setOnClickListener(new ClickEvent());
count=0;
}
public class ClickEvent implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
new Thread(){
@Override
public void run() {
while(count<1000){
count+=10;
textView.setText("你的count值为"+count);
try {
Thread.sleep(1000);
}catch (Exception e){
}
}
}
}.start();
break;
}
}
}
}
你如果用非UI线程来更新UI,如上面这段代码,就会出现停止运行的错误
- 所以只有UI线程才能更新UI
解决方式
- 方式1:其他线程委托UI线程更新UI
- 方式2:通过Handler发送Message给UI线程,令UI线程根据Message消息更新UI
- 方式3:使用Android提供的AsyncTask
二.其他线程委托UI线程
- Activity.runOnThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable,long)
此种方式最简单,只适用较简单的情况 - 就拿倒计时的例子来讲
public class MainActivity extends AppCompatActivity {
private Button button;
private TextView textView;
private int count;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
button=(Button) findViewById(R.id.button);
textView=(TextView) findViewById(R.id.textView);
button.setOnClickListener(new ClickEvent());
count=1000;
}
public class ClickEvent implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
new Thread(new Runnable() {
@Override
public void run() {
while(count>0){
count-=10;
textView.post(new Runnable() {
@Override
public void run() {
textView.setText("你的count的值为"+count);
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
break;
}
}
}
}
三.Handler
1.Handler线程通讯模型
2.Handler线程间通讯
- Handler
- Handler在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯
- Looper
- Looper负责管理线程的消息队列和消息循环
- Message
- Message是线程间通讯的消息载体,两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想要传递的消息
- MessageQueue
- MessageQueue是消息队列,先迚先出,它的作用是保存有待线程处理的消息
3.Message
- 通过obtain获取Message对象,利用Android的内存回收机制,提高效率
Message msg = Message.obtain()
Message msg=hanler.obtainMessage();
Message传递大量数据
- 若线程间通讯的数据比较复杂,比如需要使用键值对来存放大量数据,此时需要使用setData()和getData()方法,用Bundle对象来封装数据
Message msg = Message.obtain();
msg.what = 101;
Bundle bundle = new Bundle();
bundle.putInt("number",12);
bundle.putString("Name","Rice");
bundle.putString("Hobby","Swimming");
msg.setData(bundle);
- 封装好后,使用Handler对象将此Message发送出去
4.Handler的用途
- 将Message或Runnable对象发送给其他线程
- 处理来自其他线程的Message
5.Handler的主要方法
发送Message:sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message,long)、sendMessageDelayed(Message,long)
处理Message:handleMessage(Message)
- Handler Post方法
boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行
boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行
boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行
void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象
Handler Message方法
- 在Handler中,与Message发送消息相关的方法有
Message obtainMessage():获取一个Message对象。
boolean sendMessage():发送一个Message对象到消息队列中,并在UI线程取到消息后,立即执行。
boolean sendMessageDelayed():发送一个Message对象到消息队列中,在UI线程取到消息后,延迟执行
boolean sendEmptyMessage(int what):发送一个空的Message对象到队列中,并在UI线程取到消息后,立即执行
void removeMessage():从消息队列中移除一个未响应的消息
boolean sendEmptyMessageDelayed(int what,long delayMillis):发送一个空Message到消息队列中,延迟执行
6.实例一-Handler实现倒计时
public class MainActivity extends AppCompatActivity {
private Button button;
private TextView textView;
private int count;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0x123:
textView.setText("你的count值为"+msg.arg1);
break;
case 0x1:
textView.setText("倒计时结束");
break;
default:
break;
}
}
};
}
private void initView() {
button=(Button) findViewById(R.id.button);
textView=(TextView) findViewById(R.id.textView);
button.setOnClickListener(new ClickEvent());
count=10;
}
public class ClickEvent implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
new Thread(new Runnable() {
@Override
public void run() {
while(count>0){
count-=1;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count>0) {
Message message = Message.obtain();
message.what = 0x123;
message.arg1 = count;
handler.sendMessage(message);
}else{
handler.sendEmptyMessage(0x1);//发送空的
}
}
}
}).start();
break;
}
}
}
}
7.实例二-Handler更新进度条
public class MainActivity extends AppCompatActivity {
private Button button;
private TextView textView;
private ProgressBar progressBar;
private int count;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0x123:
textView.setText("进度为"+msg.arg1+"%");
progressBar.setProgress(msg.arg1);
break;
case 0x1:
textView.setText("传输结束");
break;
default:
break;
}
}
};
}
private void initView() {
button=(Button) findViewById(R.id.btn_down);
textView=(TextView) findViewById(R.id.tv_down_cotent);
progressBar=(ProgressBar) findViewById(R.id.pb_down);
button.setOnClickListener(new ClickEvent());
count=10;
}
public class ClickEvent implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_down:
new Thread(new Runnable() {
@Override
public void run() {
for(int i=1;i<=10;i++){
Message message=Message.obtain();
message.what=0x123;
message.arg1=i*10;
handler.sendMessage(message);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
handler.sendEmptyMessage(0x1);
}
}).start();
break;
}
}
}
}
四.AsyncTask
1.Thread+Handler 缺陷
- 线程的开销较大.如果每个任务都要创建一个线程,那么程序的效率要低很多
- 线程无法管理,匿名线程创建并启劢后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负
- 另外,在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿
2.AsyncTask概述
- AsyncTask的特点是任务在主UI线程之外运行,而回调方法是在主UI线程中,这就有效地避免了使用Handler带来的麻烦
AsyncTask定义了三种泛型类型 Params,Progress和Result
- Params启动任务执行的输入参数
- Progress后台任务执行的百分比
- Result后台执行任务返回的结果
使用AsyncTask简化多线程开发
- AsyncTask与门用于完成非UI线程更新UI的任务
- 本质上也是开启新线程执行耗时操作,然后将结果发送给UI线程
- 优点:简化代码,减少编写线程间通信代码这一繁琐且易出错的过程
3.AsyncTask要点
- AsyncTask为抽象类,必须先子类化
- onPreExecute():开始执行前的准备工作
- doInBackground(Params …):开始执行后台处理,并调用publishProgress(Progress )方法来更新实时的任务进度
- onProgressUpdate(Progress …):在publishProgress()方法被调用后,UI线程将调用这个方法从而在界面上展示任务的进展情况
onPostExecute(Result):执行完成后的操作,传送结果给UI线程
Task的实例必须在UI 线程中创建
- execute方法必须在UI线程中调用
- 不要手动的调用onPreExecute(),onPostExecute(Result),doInBackground(Params…),onProgressUpdate(Progress…)这几个方法,需要在UI线程中实例化这个task来调用
- 该task只能被执行一次,否则多次调用时将会出现异常
- doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示迚度的参数,第三个为doInBackground返回和onPostExecute传入的参数
(1).构造参数解读
private class task extends AsyncTask<String, String, String>
AsyncTask<>的参数类型由用户设定,这里设为三个String
第一个String代表输入到任务的参数类型,也即是doInBackground()的参数类型,调用execute()方法时传入的参数类型
第二个String代表处理过程中的参数类型,也就是doInBackground()执行过程中的产出参数类型,通过
publishProgress()发消息,传递给onProgressUpdate()一般用来更新界面
第三个String代表任务结束的产出类型,也就是doInBackground()的返回值类型,和onPostExecute()的参数类型
4.AsyncTask实例-更新进度条
public class MainActivity extends AppCompatActivity {
private Button button1,button2;
private ProgressBar progressBar;
private TextView textView;
private MyTask myTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
button1=(Button) findViewById(R.id.btn_download_async);
button2=(Button) findViewById(R.id.btn_cancle_async);
button1.setOnClickListener(new ClickEvent());
button2.setOnClickListener(new ClickEvent());
progressBar=(ProgressBar) findViewById(R.id.pb_async);
textView=(TextView) findViewById(R.id.tv_async);
}
public class ClickEvent implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_download_async:
if(myTask==null){
myTask=new MyTask();
}
myTask.execute(100);
break;
case R.id.btn_cancle_async:
if (myTask != null && myTask.getStatus() == AsyncTask.Status.RUNNING) {
myTask.cancel(true);
myTask = null;
}
break;
}
}
}
public class MyTask extends AsyncTask<Integer,Integer,String>{
// 后面尖括号内分别是
// 参数(例子里是线程休息时间),
// 进度(publishProgress用到),
// 返回值 类型
// 第一个执行方法
@Override
protected void onPreExecute() {
super.onPreExecute();
button1.setEnabled(false);
button2.setEnabled(true);
textView.setText("点击按钮开始下载");
}
// 第二个执行方法,onPreExecute()执行完后执行
// @Override
protected String doInBackground(Integer... params) {
for(int i=0;i<=100;i++){
publishProgress(i);
try {
Thread.sleep(params[0]);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(isCancelled())
return null;
}
return "执行完毕";
}
// 这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数
// 但是这里取到的是一个数组,所以要用progress[0]来取值
// 第n+1个参数就用progress[n]来取值
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if(isCancelled())
return;
textView.setText("当前已下载"+values[0]+"%");
progressBar.setProgress(values[0]);
}
// doInBackground返回时触发,换句话说,就是doInBackground执行完后触发
// 这里的result就是上面doInBackground执行后的返回值,所以这里是"执行完毕"
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
textView.setText(s);
button1.setEnabled(true);
button2.setEnabled(false);
myTask=null;
}
@Override
protected void onCancelled() {
super.onCancelled();
button1.setEnabled(true);
button2.setEnabled(false);
progressBar.setProgress(0);
myTask=null;
}
}
}