多线程异步机制Handler以及AsyncTask

时间:2022-08-27 14:46:20

Android当中多线程的写法跟JAVA当中相差不了太多,只需要新建一个类继承自Thread类即可。然后重写父类的run方法。并在里面写耗时逻辑即可

class MyThread extends Thread {
@Override
public void run() {
// 处理具体的逻辑
}
}

启动线程

new MyThread().start();

当然也可以像下面这样写,这样的写法更加适合,因为使用继承的方式耦合性有点高

class MyThread implements Runnable {
@Override
public void run() {
// 处理具体的逻辑
}
}

启动线程也发生了相应的改变:

MyThread myThread = new MyThread();
new Thread(myThread).start();

不过我们通常会这样写:

new Thread(new Runnable() {
@Override
public void run() {
// 处理具体的逻辑
}
}).start();

由于Android更新UI界面的操作不能放在子线程当中运行,只能放在主线程当中运行,和许多其他的 GUI 库一样,Android的 UI 也是线程不安全的。也就是说,如果想要更新应用程序里的 UI元素,则必须在主线程中进行,否则就会出现异常。比如:

    @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.change_text:
new Thread(new Runnable() {
@Override
public void run() {
text.setText("Nice to meet you");
}
}).start();
break;
default:
break;
}
}

就会出现如下异常
多线程异步机制Handler以及AsyncTask

有的时候我们必须要在子线程里面做一些耗时操作,来更新UI,比如要写一个网速实时更新,那怎么做呢。Android为我们提供了Handler,异步消息处理机制。我们这样写:

public class MultithreadActivity extends Activity {
private Button change;
private TextView text;
private static final int CHANGETEXT = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.multithreadactivity);
change = (Button) findViewById(R.id.change);
text = (TextView) findViewById(R.id.text);
change.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {

@Override
public void run() {
Message message = new Message();
message.what = CHANGETEXT;
handler.sendMessage(message);
}
}).start();
}
});
}

@SuppressLint("HandlerLeak")
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case CHANGETEXT:
text.setText("JAVA");
break;
default:
break;
}
}
};
}

这样就不会报错。以上我们就是利用了Handler来异步处理UI更新操作。

我们来看看异步消息处理机制是怎样的?

1,Message是现成之间传递的消息,它可以携带少量信息,用于在不同线程之间交换数据
2,Handler顾名思义就是处理者的意思,发送和处理消息的,发送一般就是用sendmessage方法,最后传到handlemessage当中处理
3,MessageQueue 是消息队列的意思,用于存放handler发送的消息,这一部分消息一直存在消息队列中等待被处理,每个线程当中只会有一个MessageQueue。
4,Looper Looper是每个线程中的 MessageQueue的管家,调用 Looper的 loop()方法后,就会
进入到一个无限循环当中,然后每当发现 MessageQueue中存在一条消息,就会将它取
出, 并传递到 Handler的 handleMessage()方法中。 每个线程中也只会有一个 Looper对象。

下面是整个异步流程处理机制示意图:

多线程异步机制Handler以及AsyncTask

下面我们来看下一个服务Service与Handler结合,周期性实时获取网速服务的例子:

public class Net_Service extends Service {

private long total_data = TrafficStats.getTotalRxBytes();
// private Handler mHandler;
// 几秒刷新一次
private final int count = 2;
private Runnable mRunnable;

@Override
public void onCreate() {
super.onCreate();
/**
* 定义线程周期性地获取网速
*/

mRunnable = new Runnable(){
@Override
public void run() {
// 定时器
ExampleApplication.getHandle().postDelayed(mRunnable, count * 1000);
Message msg = ExampleApplication.getHandle().obtainMessage();
msg.what = 1;
msg.arg1 = getNetSpeed();
ExampleApplication.getHandle().sendMessage(msg);
}
};
Log.i("NetService", "创建了服务");
}

/**
* 核心方法,得到当前网速
*
* @return
*/

private int getNetSpeed() {
long traffic_data = TrafficStats.getTotalRxBytes() - total_data;
total_data = TrafficStats.getTotalRxBytes();
return (int) traffic_data / count;
}

/**
* 启动服务时就开始启动线程获取网速
*/

@Override
public void onStart(Intent intent, int startId) {
Log.i("NetService", "开始了服务");
ExampleApplication.getHandle().postDelayed(mRunnable, 0);
};

/**
* 在服务结束时删除消息队列
*/

@Override
public void onDestroy() {
Log.i("NetService", "停止了服务");
ExampleApplication.getHandle().removeCallbacks(mRunnable);
super.onDestroy();
};

@Override
public IBinder onBind(Intent intent) {
return null;
}

}

ExampleApplication中代码:

public static Handler getHandle(){
return mHandler;
}


public static void setmHandler(Handler mHandler) {
ExampleApplication.mHandler = mHandler;
}

在到Activity写一个Handler更新UI就好了,把那个Handler set到ExampleApplication的handler,拿到msg.arg1。

AsyncTask的基本介绍

不过为了更加方便我们在子线程中对 UI进行操作, Android还提供了另外一些好用的工
具,AsyncTask就是其中之一。借助 AsyncTask,即使你对异步消息处理机制完全不了解,
也可以十分简单地从子线程切换到主线程。当然,AsyncTask背后的实现原理也是基于异步
消息处理机制的,只是 Android帮我们做了很好的封装而已。

public class YiBuAncyTask extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.asyncactivity);
new Test().execute();//启动这个异步任务
}

/**
* 创建一个子类去继承AsyncTask,重写AsyncTask里面的方法 ,并指定三个泛型参数
* (第一个参数表示)Params在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
* (第二个参数)Progress在执行后台任务时,如果需要在界面显示当前进度, 则使用这里指定的泛型作为进度单位,指定进度单位的类型
* (第三个参数)Result 指定返回结果的类型
*/

class Test extends AsyncTask<Void, Integer, Boolean> {
// Void表示在执行的时候不需要传入参数给后台任务
// Integer表示用整型数据用来做进度显示单位
// Boolean表示用布尔来反馈执行结果

// 最常用的方法有以下方法

/**
* 这个方法是在子线程当中运行的,所有的耗时操作应当在这个方法当中运行 如果指定AsyncTask第三个泛型参数指定的是Void,
* 就可以不返回
* 这个方法当中不能进行UI操作如果需要更新 UI元素,比如说反馈当前任务的执行进度,可以调 用
* publishProgress(Progress...)方法来完成。
*/

@Override
protected Boolean doInBackground(Void... params) {
return true;
}
/**
* 在执行后台任务之前进行一些初始化操作,比如显示一个进度条对话框等等
*/

@Override
protected void onPreExecute() {
super.onPreExecute();
}

/**
* 在调用了publishProgress方法之后 ,这个方法马上会被调用
* 携带的这个参数就是后台任务中传递过来的,在这个方法中可以对UI进行操作
* 利用参数中的数值就可以对界面元素进行相应的更新
*/

@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}

/**
* 当后台执行任务完毕,并返回结果时,这个方法会被调用,返回的数据会将结果传递到该参数当中,
* 可以利用该参数来完成一些UI操作
*/

@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
}
}
}

下面我们来看个AsyncTask异步获取一个本地数据库题库的例子:

// 先读取数据库中的缓存数据量较多比较耗时使用AsyncTask
private class QueryTask extends AsyncTask<Void, Void, ArrayList<CauseInfo>> {

@Override
protected ArrayList<CauseInfo> doInBackground(Void... params) {
String CATEID = getIntent().getStringExtra("cateId");
String chapter = getIntent().getStringExtra("chapter");
list = DBManager.getInstance(OrderActivity.this).querys(AnswerColumns.TABLE_NAME, CATEID, chapter);
Log.e("chapter", ""+chapter+"id"+CATEID);
return list;
}

@Override
protected void onPostExecute(ArrayList<CauseInfo> list) {
if (list.size() == 0){
subjectTop.setText("0/0");
selectOne.setText("");
selectTwo.setText("");
selectThree.setText("");
selectFour.setText("");
selectFive.setText("");
selectSix.setText("");
return;
}
int lastSelect;
if (chapter.equals("")) {
lastSelect = ConfigPreferences.getInstance(OrderActivity.this).isLastSelectOrder(CATEID);
} else {
lastSelect = ConfigPreferences.getInstance(OrderActivity.this).isLastSelectOrder(chapters);
}
i = lastSelect - 1;
CauseInfo myData = list.get(lastSelect - 1);
title.setText(lastSelect + "." + myData.title);
subjectTop.setText(lastSelect + "/" + list.size());
if (myData.q_type == 1) {
type.setText("题型:单选题");
submit.setVisibility(View.GONE);
selectOne.setText(myData.optionA);
selectTwo.setText(myData.optionB);
selectThree.setText(myData.optionC);
selectFour.setText(myData.optionD);
selectFive.setText(myData.optionE);
selectSix.setText(myData.optionF);
if (myData.optionE.equals("")) {
relFive.setVisibility(View.GONE);
} else {
relFive.setVisibility(View.VISIBLE);
}
if (myData.optionF.equals("")) {
relSix.setVisibility(View.GONE);
} else {
relSix.setVisibility(View.VISIBLE);
}
} else if (myData.q_type == 2) {
submit.setVisibility(View.VISIBLE);
selectOne.setText(myData.optionA);
selectTwo.setText(myData.optionB);
selectThree.setText(myData.optionC);
selectFour.setText(myData.optionD);
selectFive.setText(myData.optionE);
selectSix.setText(myData.optionF);
type.setText("题型:多选题");
if (myData.optionE.equals("")) {
relFive.setVisibility(View.GONE);
} else {
relFive.setVisibility(View.VISIBLE);
}
if (myData.optionF.equals("")) {
relSix.setVisibility(View.GONE);
} else {
relSix.setVisibility(View.VISIBLE);
}
} else if (myData.q_type == 3) {
submit.setVisibility(View.GONE);
selectThree.setVisibility(View.GONE);
selectFour.setVisibility(View.GONE);
imageThree.setVisibility(View.GONE);
imageFour.setVisibility(View.GONE);
relFive.setVisibility(View.GONE);
relSix.setVisibility(View.GONE);
selectOne.setText("正确");
selectTwo.setText("错误");
type.setText("题型:判断题");
if (myData.optionE.equals("")) {
relFive.setVisibility(View.GONE);
} else {
relFive.setVisibility(View.VISIBLE);
}
if (myData.optionF.equals("")) {
relSix.setVisibility(View.GONE);
} else {
relSix.setVisibility(View.VISIBLE);
}
}
// 判断是否有图片
Log.i("Imageurl", "" + myData.getImage());
if (myData.getImage().equals("")) {
iv_picture.setVisibility(View.GONE);
} else {
iv_picture.setVisibility(View.VISIBLE);
String imageurl = myData.getImage();
Bitmap bitmap = asyncbitmap.loadBitmap(iv_picture, imageurl, new ImageCallBack() {
@Override
public void imageLoad(ImageView imageView,Bitmap bitmap){
imageView.setImageBitmap(bitmap);
}
});
iv_picture.setImageBitmap(bitmap);
}
}
}