interView - handler相关
来源:互联网 发布:mac更新系统进度条不动 编辑:程序博客网 时间:2024/06/08 14:37
为什么要使用handler?
因为与UI相关的操作(例如创建UI,刷新UI,处理UI)都必须在主线程中进行。如果说在主线程进行耗时操作,就会被阻塞在当前状态,界面出现假死,此时就会忽略用户操作,这时如何用户的操作在5秒内没响应就会出现ANR异常,所以需要把耗时操作放在子线程里操作,让子线程处理耗时操作,主线程来响应用户的操作,但是子线程在执行完任务后又无法去操作UI,所以为了解决这个问题Handler就出现了,handler是在多线程之间使用的,用于线程间通信,通过在主线程中创建一个Hanlder对象,并且复写其HandlerMessage方法,在创建的工作线程中创建一个Message对象,设置其响应的属性,使用此Handler对象将这个Message对象发送到主线程的消息队列中,主线程的中的Looper对象会不断的监听消息队列中的内容,当监听到消息队列中有新的内容时,就会从消息队列中拿到消息,并且交给这个发送对象的Handler对象来进行处理,就是在HandlerMessage里处理消息来操作UI。
handler机制的原理?内部是如何实现的?
android启动应用程序时会替主线程创建一个消息队列,用来存放线程放入的消息,一个线程可以产生一个looper对象(默认情况下android中新诞生的线程是没有开启消息循环的,主线程除外,系统会自动为主线程创建looper对象,开启消息循环),由它来管理此线程里的MessageQueue(消息队列),
handler对象可以用来与looper沟通,以便push新消息到Message Queue里,或者接收Looper从Message Queue取出所送进来的消息。
消息处理机制?如何终止Looper?
1.获得消息 通过Message.obtain()获得消息,内部是从消息池获取消息,如果消息池没有消息,则创建一个消息对象返回。
2.发送消息 通过sendMessage()发送消息,内部是调用MessageQueue.enqueueMessage(Message msg,long when)方法,依据消息传入的时间入队。
3.取出消息 在Looper.loop()这个轮询消息的方法中,获取MessageQueue对象后,从中取出消息(Message msg = queue.next())。
4.分发消息 从消息队列中取出消息后,调用msg.target.dispatchMessage(msg);进行消息的分发。
5.处理消息 在handlerMessage方法中进行消息的处理。
6.回收消息 在消息使用完毕后,在Looper.loop()方法中调用msg.recycle(),将消息进行回收,即将消息的所有字段恢复为初始状态。
过程如下:
(1)获得消息
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
(2) 发送消息
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
(3)取出消息(4)分发消息(6)回收消息
public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
long wallStart = 0;
long threadStart = 0;
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
wallStart = SystemClock.currentTimeMicro();
threadStart = SystemClock.currentThreadTimeMicro();
}
msg.target.dispatchMessage(msg);
if (logging != null) {
long wallTime = SystemClock.currentTimeMicro() - wallStart;
long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
if (logging instanceof Profiler) {
((Profiler) logging).profile(msg, wallStart, wallTime,
threadStart, threadTime);
}
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
}
Handler对象维护一个消息队列,有新的消息送来(sendMessage())的时候就把它放到队尾,之后排队到处理(handlerMessage)该消息的时候,由主线程的Handler对象来处理,整个过程是异步的。
Looper.loop() 让looper开始工作,从消息队列里取消息,处理消息,写在Looper.loop之后的代码不会被执行,这个函数内部是一个循环,当调用mHandler.getLoop().quit()后,Looper才会终止,其后的代码才能够执行。
如何在子线程中创建handler?
可以使用HandlerThread,HandlerThread本质就是一个Thread,它与普通Thread的差别在于它有个Looper的成员变量,这个Looper其实就是对消息队列以及队列处理逻辑的封装,简单说就是消息队列+消息循环。
代码如下:
public class HandlerThreadDemoActivity extends Activity {
private Handler childHandler;
private HandlerThread handlerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText("HandlerThreadDemo");
setContentView(tv);
handlerThread = new HandlerThread("handler_thread");
handlerThread.start();
childHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
System.out.println("handleThread收到了消息");
}
super.handleMessage(msg);
}
};
Message msg = Message.obtain();
msg.what = 1;
childHandler.sendMessage(msg);
}
@Override
protected void onDestroy() {
super.onDestroy();
childHandler.getLooper().quit();
}
}
也可以直接创建Thread,再创建Looper来实现,
代码如下:
private Handler childHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText("HandlerThreadDemo");
setContentView(tv);
new Thread(new MyThread()).start();
}
private class MyThread implements Runnable{
@Override
public void run() {
Looper.prepare();
childHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
System.out.println("handleThread收到了消息");
}
}
};
Message msg = Message.obtain();
msg.what = 1;
childHandler.sendMessage(msg);
Looper.loop();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
childHandler.getLooper().quit();
}
hanlder.post()这个方法干嘛用的?
Activity类有一个runOnUiThread方法,此方法的参数是一个Runnable对象,在run方法中可以直接更新UI,它内部就是通过调用Handler.post(),那么Handler.post究竟干了些什么, ?
查看源码: public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
这里面有个关键的方法getPostMessage(r),它将Runnable转成一个Message,它内部到底干了什么?看一下它的源码:
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
这里面就是将Runnable转化成一个message,内部跟sendMessage一样都是通过发送消息来通知主线程更UI,只是post在代码上简化了操作,如果在一个类只有个单个消息处理,那么使用handler.post更加方便。也可以用handler.postDelayed来执行定时器的操作。
handler在Activity中的释放问题!
handler可能会处理延迟消息,这时如果Activity已经finish了,mHandler释放,执行消息则会导致程序崩溃,所以要在onDestroy中执行removeMessages()方法来清除消息。如果是子线程创建的looper还需要mHandler.getLooper().quit();
handler造成内存泄漏的分析与解决
什么是内存泄露?
Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。
android中使用Handler造成内存泄露的原因
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mImageView.setImageBitmap(mBitmap);
}
}
上面是一段简单的Handler的使用。当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。
内存泄露的危害
只有一个,那就是虚拟机占用内存过高,导致OOM(内存溢出),程序出错。对于Android应用来说,就是你的用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制,FC。
使用Handler导致内存泄露的解决方法
方法一:通过程序逻辑来进行保护。
1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
2.如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。
方法二:将Handler声明为静态类。
静态类不持有外部类的对象,所以你的Activity可以随意被回收。代码如下:
static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
mImageView.setImageBitmap(mBitmap);
}
}
但其实没这么简单。使用了以上代码之后,你会发现,由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference):
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
将代码改为以上形式之后,就算完成了。
延伸:什么是WeakReference?
WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了。
- interView - handler相关
- handler相关
- Handler相关
- Handler相关
- handler相关
- android handler相关资料
- android handler相关资料
- Handler消息应用相关
- Handler Message相关说明
- Handler 相关的问题
- Handler相关面试题
- Handler 之 ThreadLocal 相关
- Handler的相关知识
- Handler相关笔记
- handler相关1
- Android异步相关-Handler
- Handler相关概念简介
- Interview
- linux 根据进程名称kill进程
- Introduction to CMMI培训总结
- 太阳与十二宫
- Java lombok-利用注解来帮你生成方法
- Windows XP 共享 Workgroup无法访问.您可能没有权限使用网络资源
- interView - handler相关
- 一个整型数组里除了一个或者两个或者三个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)
- 《花千骨》绝美外景剧照 赵丽颖霍建华仙气足
- C语言 内存管理详解
- CAS SSO NET client 搭建
- Android中调用系统所装的软件打开文件
- 使用wireshark来分析tcp的三次握手和四次断开连接
- Android屏幕density, dip等相关概念总结
- Tomcat配置,跳转,安全