我们在平时的项目开发中,肯定会遇到处理异步任务的场景。因为Android中的UI线程是不安全的,我们需要更新ui的话就必须在ui线程上进行操作。否则就会抛异常。
这个时候我们就需要用到异步消息处理了
比如,在子线程中请求数据,拿到数据后告诉ui线程去更新界面,
在子线程下载文件告诉ui线程下载进度,ui线程去更新进度等等。
我们常用的方式就是通过Handler, AnyncTask或者IntentService来处理异步消息。
什么是Handler
handler是Android中用来处理消息的一种机制,我们可以简单的理解为handler就是用来更新ui的。
handler的使用方法
1.handler.post(runnable)**
1.在ui线程中创建Handler,注意Handler在哪个线程中创建,就会与哪个线程进行绑定。所以我们一般都在主线程上创建Handler。
private static Handler handler = new Handler();
2.在子线程中,通过handler.post()来更新ui
class WorkThread extends Thread {
@Override
public void run() {
/* runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("aaa");
}
});
tv.post(new Runnable() {
@Override
public void run() {
tv.setText("ccc");
}
});*/
handler.post(new Runnable() {
@Override
public void run() {
tv.setText("bbb");
}
});
}
}
2.handler.send(Message)
1.在ui线程中创建Handler并重写handleMessage方法,根据msg.what处理消息
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
tv.setText("aaa");
break;
}
}
};
2.再需要传递消息的地方创建Message,并通过handler.sendMessage()发送消息
Message msg = new Message();
msg.what = 1;
handler.sendMessage(msg);
handler机制的原理
实际上在我们在创建Handler的时候,就已经把handler和所在线程绑定到一起了。然后我们通过handler发送的消息就会在该线程中获取到消息并进行处理。
来看看源码:
这是Handler构造方法的源码。
可以看到,Handler在构造方法中通过Looper.myLooper()获取了一个Looper对象,并通过Looper对象获取到一个消息队列。
可以看到,myLooper()返回的是当前线程的Looper对象,注意:每一个线程只有一个Looper对象。
而myQueue返回了Looper中的MessageQueue.可以看到消息队列实在Looper的构造方法中被创建出来的。
那么问题来了
这个Looper对象是什么时候放进去的呢?
我们来看看Loopper类中的这个方法
可以看到,实际上是在perpare这个方法中设置了Looper
这么一来,就清楚是怎么回事儿了。
Handler在那个线程中创建,就会跟哪个线程绑定,并且与Looper和Message相关联。
要记住的方法:
Lopper.prepaer()
Looper.loop() 实际上源码就是创建了个死循环,去消息队列中获取消息。获取到消息就通过handler.dispatchMessage(msg)将消息发送出去。
handleMessage: 处理接收到的消息
handler引起的内存泄漏及解决办法
原因:静态内部类持有外部类的匿名引用,导致外部的activity无法释放。
解决办法:将handler声明为static即可,这样的话,handler对象由于是静态类型无法持有外部activity的引用,但是这样Handler不再持有外部类的引用,导致程序不允许在Handler中操作activity中的对象了,这时候我们需要在Handler对象中增加一个对activity的弱引用;
如果使用的是handler.post()的话,就在activity的onDestory()方法中调用 handler.removeCallbacks();
static class MyHandler extends Handler {
WeakReference mActivityReference;
MyHandler(MainActivity activity) {
mActivityReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
final MainActivity activity = mActivityReference.get();
if (activity != null) {
activity.tv.setText("aaa");
}
}
}
private MyHandler handler = new MyHandler(this);
什么是AsyncTask
AsyncTask是Android提供的一个轻量级的异步任务处理类,本质是对线程池和Handler的封装,方便我们使用。
AsyncTask的使用方法
AsyncTask本身是一个抽象类,我们使用时直接继承自AsyncTask并实现相应的方法即可。
AsyncTask定义了三种泛型类型 Params,Progress和Result。
Params 启动任务执行的输入参数,
Progress 后台任务执行的百分比。
Result 后台执行任务最终返回的结果
AsyncTask有五个常用方法:
onPreExecute() 在耗时操作执行之前调用,主要是用来做一些初始化操作,实在ui线程上调用。
doInBackground(Params…) 在onPreExecute之后调用,用来执行耗时操作,在工作线程中执行
publicProgress(Progress…) 在doInBackground中调用,可以用来更新任务的进度。
onProgressUpdate(Progress…) 运行在主线程,可以用来更新进度
onPostExecute(Result) 运行在主线程,返回任务执行的结果
onCancelled() 取消任务
package com.yzq.handler;
import android.os.AsyncTask;
/**
* Created by yzq on 2017/5/26.
*/
public class task extends AsyncTask {
/*任务执行前调用 运行在主线程 */
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/*执行耗时任务 运行在后台线程*/
@Override
protected String doInBackground(Integer... params) {
publishProgress();//更新进度
return null;
}
/*运行在主线程 更新进度*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
/*运行在主线程 返回执行完成后的结果*/
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
/*取消任务时调用*/
@Override
protected void onCancelled() {
super.onCancelled();
}
}
AsyncTask的机制原理
1.AsyncTask本质上是一个静态的线程池,其派生出来的子类可以实现不同的异步任务,这些任务都会提交到线程池中去执行
2.耗时操作是在doInBackground中执行的
3.当任务状态(pendding,running,finished)改变后,工作线程会向ui线程发送消息,AsyncTask内部的InternalHandler会响应这些消息并执行相关回调函数
AsyncTask注意事项
1、AsyncTask不适合特别耗时的任务
AsyncTask的生命周期和Activity的生命周期不同步,Activity销毁了但是AsyncTask中的任务还是会继续执行完毕,一个最典型的例子就是Activity的横竖屏切换,AsyncTask中引用的Activity不是当前的Activity,onPostExecute()中执行的仍然是上一个Activity。还有一个原因是因为AsyncTask在执行长时间的耗时任务时也会持有一个Activity对象,即使这个Activity已经不可见了,Android也无法对这个Activity进行回收,导致内存泄露。
2、AsyncTask只能在主线程中创建以及使用
AsyncTask被用于执行异步任务,然后更新UI,所以最后的onPostExecute()方法执行在创建该AsyncTask对象的线程中,如果不在主线程中创建以及使用,就达不到更新UI的目的。
3、一个AsyncTask对象只能执行一次
一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常
4、AsyncTask在不同的Android版本下的并行和串行问题
关于AsyncTask的并行和串行问题,在不同的API下是有不同的。在Android1.6之前,AsyncTask是串行执行任务的;到了Android1.6时,开始采用线程池来并行执行任务;在Android3.0之后的版本中,AsyncTask又开始用一个线程串行执行任务。虽然Android3.0之后采用串行方式执行任务,但我们可以通过AsyncTask的executeOnExecutor(exe,params),自定义一个线程池来并行执行任务。
什么是HandlerThread
在Android我们经常会遇到耗时操作,一般我们都是开启一个子线程执行耗时操作。执行完成后GC会自动销毁线程,如果再有耗时操作的话再去创建线程,然后再销毁线程。这样是很消耗系统资源的。
还有Android中,子线程默认是没有开启Looper循环的,也就是说,如果我们想在子线程中使用Handler的话,我们需要手动调用Looper.prepare()启用Looper。这样一来我们就需要手动去维护Looper,很麻烦。
所以,谷歌公司给我们封装好了一个类就是HandlerThread以便于我们使用。
HandlerThread本质上是一个线程类,继承自Thread。
HandlerThread本身有自己的Looper对象。
讲HandlerThread中的Looper对象获取后传给Handler,就可以在Handler中的HandleMessage中处理异步消息。
优点与缺点
优点:不会有阻塞,减少对性能的消耗
缺点:不能同时进行多任务处理,效率不高。HandlerThread是一个串行队列,内部只有一个线程。
HandlerThread的作用
一般是用在子线程跟子线程进行通信
IntentService是什么
IntentService本质上就是继承自Service,其内部封装了HandlerThread,IntentService和Service的生命周期一样,且启动方式也一样,当IntentService中的耗时任务执行完毕后,IntentService会自动停止,多次启动IntentService时。耗时任务会以队列的形式存在于IntentService中,并在onHandlerIntent方法中一个一个的执行。
使用方法
1,继承自IntentService并重写其构造方法和onHandleIntent()方法
2.在HandlerIntent()方法中处理耗时操作
public class MyIntentService extends IntentService {
public MyIntentService() {
super("yzq");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
L.i("onHandleIntent"+intent.getStringExtra("i"));
}
}
3.启动service,注意要在清单配置文件中生命service
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i=0;i<10;i++){
Intent intent=new Intent(MainActivity.this,MyIntentService.class);
intent.putExtra("i",i+"");
startService(intent);
}
}
}
如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!