1、先来了解下下面的几个重要的组件:
- Handler:收发消息工具。
- Message:Hander接收和处理消息对象。
- Looper:每一个线程拥有一个looper,创建消息队列,循环取队列里的消息进行分发。
- MessageQueue:消息队列,采用先进先出的方法俩管理Message对象,程序创建Looper对象 时,会在它的构造器中创建MessageQueue对象。
2、在子线程中创建Handler:
方法1:在new Handler()前后加上 Looper.prepare()和 Looper.loop()
//子线程创建handler,需要创建looper
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
@SuppressLint("HandlerLeak")
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Looper.loop();
}
}).start();
方法二:传入主线程的Looper来实现
//将主线程的looper传进来
new Thread(new Runnable() {
@Override
public void run() {
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
}).start();
3、handler之内存泄漏:
造成泄露原因:
- Handler 的生命周期与Activity 不一致:当Android应用启动的时候,会先创建一个UI主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。当在主线程中初始化Handler时,该Handler和Looper的消息队列关联(没有关联会报错的)。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。
- handler 引用 Activity 阻止了GC对Acivity的回收在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。如果外部类是Activity,则会引起Activity泄露 。当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。
4、如何避免内存泄露:
- 通过程序逻辑来进行保护
在Activity销毁时,将消息从消息队列中移除
removeMessages(int what)
removeCallbacks(Runnable r)
removeCallbacksAndMessages(null); - 将Handler声明为静态类 (见如下代码)
private static class ActivityHandler extends Handler {
private final WeakReference<AsyncActivity> mActivity;
public ActivityHandler(AsyncActivity activity) {
this.mActivity = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
if (this.mActivity != null) {
AsyncActivity activity = this.mActivity.get();
if (activity != null && !activity.isFinishing()) {
activity.handleMessage(msg);
}
}
}
}
public void handleMessage(Message msg) {
}
5、Looper能创建多个实例嘛?能自己创建Looper实例嘛?
Looper只能创建一个实例,可以自己调用Looper.prepare()方法创建实例。
先来看Looper的prepare()方法,调用多次prepare方法创建实例,会抛出异常。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
ThreadLocal类的特性:ThreadLocal是线程内部的数据存储类,当使用ThreadLocal维护变量的时候,它会为每个使用该变量的线程提供一个独立的变量副本,这个变量副本是该线程独有,不受其他线程影响。
Looper的主要作用是与当前线程形成一种绑定的关系,同时创建一个MessageQueue,这样保证一个线程只能持有一个Looper和MessageQueue,同时Looper使得MessageQueue循环起来。
6、handler,asyncTask有什么区别?
Android的AsyncTask比Handler更轻量级一些(只是代码上轻量一些,而实际上要比handler更耗资源),适用于简单的异步处理。
AsyncTask的重写方法:
- doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用+ publicProgress(Progress…)来更新任务的进度。
- onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回。
- onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
- onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
- onCancelled() 用户调用取消时,要做的操作。
使用AsyncTask类,以下是几条必须遵守的准则:
- Task的实例必须在UI thread中创建;
- execute方法必须在UI thread中调用;
- 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
- 该task只能被执行一次,否则多次调用时将会出现异常;
AsyncTask优缺点
优点:简单,快捷, 过程可控。
缺点:在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来,最大并发数不超过5。
Handler优缺点:
优点:结构清晰,功能定义明确,对于多个后台任务时,简单,清晰。
缺点:在单个后台异步处理时,显得代码过多,结构过于复杂(相对性)。
网友评论