和Java一样,当我们要执行一些耗时的操作时(例如:发送网络请求),如果不将此类操作放在子线程去执行,就会导致主线程被阻塞,影响程序的正常运行。这个时候需要用到多线程技术,那么我们就来对多线程做进一步的了解吧。
多线程的基本用法
Android的多线程和Java使用相同的语法。定义一个线程,只需要新建一个类继承自Thread,然后重写run()方法,并在方法中编写耗时的操作。启动线程的话直接调用Thread的start()方法即可。
//定义线程
class MyThread extends Thread{
@Override
public void run() {
super.run();
}
}
new MyThread().start(); //启动线程
另外实现Runnable接口的方式也可以定义一个线程。
class MyThread implements Runnable{
@Override
public void run() {
}
}
//使用线程
MyThread mythred = new MyThread();
new Thread(mythred).start();
Thread的构造函数接收一个Runnable参数,而我们new出来的MyThread正是一个实现了Runnable接口的对象,所以可以直接将它传入到Thread的构造函数里。接着调用Thread的start()方法。run() 中的代码就会在子线程中执行了。
另外,匿名类的方式 也能实现线程定义。
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
在子线程中更新UI
我们给一个按钮添加监听,点击按钮的时候提示一个文本信息,在子线程中执行提示操作。代码如下:
button06.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"提示",Toast.LENGTH_LONG).show();
}
}).start();
}
});
以上代码并不能顺利的执行。因为Android是不能在子线程执行UI操作的。那么怎么办呢?这个时候我们就要用Handler来解决。
具体用法:
创建Handler对象,重写父类的handleMessage()方法,这handleMessage()方法中对具体的Message信息进行处理。
private static final int UPDATE_01 = 1;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case UPDATE_01:
Toast.makeText(MainActivity.this,"提示了一下",Toast.LENGTH_LONG).show();
break;
default:
break;
}
}
};
button06.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = UPDATE_01;
handler.sendMessage(message);
}
}).start();
}
});
异步消息处理机制
Android的异步消息处理主要有4个部分组成:
1、Message: 是在线程之间进行传递的消息 消息内部可以携带少量的数据 用于在不同的线程之间进行数据交换。我们前面用到了what字段 其实消息内部还有一些别的字段,我们可以在实际的场景中合理选择。
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
public int sendingUid;
public int what;
2、Handle: 处理消息 一般的做法就是调用sendMessage()方法把消息发送出去,传递到handleMessage()方法中。
3、MessageQueue: 消息队列 存放消息 通过 Handle的sendMessage()发送出去的消息在消息被处理之前 会被存在消息队列中的知道被执行为止。每个线程只有一个MessageQueue。
4、Looper: 是每个线程MessageQueue的管家。执行run()操作后处理循环执行的状态。当发先消息队列中有消息的时候就会把消息取出,调起Handle的handleMessage()方法。每个线程只有一个Looper。
各个部分的关系如图所示:

AsyncTask的使用
AsyncTask是一个抽象类 我们要想使用它 必须新建一个类继承它。在继承的时候 我们需要指定3个范型的参数。
1、Params在执行AsyncTask时候需要传入的参数 在后台任务中使用
2、Progress 后台任务执行的时候 显示进度 需要此参数作为进度单位
3、Result当任务执行完毕 如果要对结果进行返回 则在此指定返回值对类型
请看下面的一个代码实例:
class DownLoadTask extends AsyncTask<Void,Integer,Boolean>{
/*
* 这个方法会在后台开始执行人物之前调用 用于进行一些界面上的初始化操作 比如显示一个进度条对话框
* */
@Override
protected void onPreExecute() {
super.onPreExecute();
//显示进度条
}
/*
* 这个方法中虽有的人物都会在子线程执行,处理所有耗时的操作。任务一旦完成就通过return语句返回结果
* 如果AsyncTask第三个范型参数指定的是Void 就可以不反悔任何的结果。
* 这个方法中是不进行任何的UI操作的 如果要进行任何的UI操作,比如说返回下载的进度 可以调用onProgressUpdate()
*
* */
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
//关闭进度提示元素
if (aBoolean) {
//任务完成
}else {
//任务失败
}
}
/*
* 当在后台任务中调用了onProgressUpdate()方法后,此方法就会很快被执行
* 该方法携带的参数 就是在后腰任务中传递过来的 在这个方法中对UI进行操作 利用参数中的数值对界面元素进行调整
*
* */
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//在这里更新下载进度
}
/*
* 当后台任务之ing完毕后 并通过return方法返回时 这个方法很快被调用 返回的参数传递到此方法中 可以利用参数进行页面操作
* 比如 结束的时候 关闭进度条
* */
@Override
protected Boolean doInBackground(Void... voids) {
try {
while (true){
int downloadProgress = download();//从此方法中完成操作 并取到进度数值
publishProgress(downloadProgress);
if (downloadProgress >= 100){
break;
}
}
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
}
在这里将第一个参数指定为void表示后台在执行AsyncTask对时候不需要传入参数
第二个参数为Integer类型 表示使用整形数据来作为进度表示对基本单位
第三个参数Boolean 表示 程序执行后的返回结果是布尔类型
网友评论