【Android】之【Handler】
创始人
2025-05-31 21:10:31
0

一、面试必问

1、Handler是什么

Handler是Android中的异步消息处理机制。当发送一个消息之后,这个消息是进入一个消息队列(MessageQueue),在消息队列中通过Looper去循环的获取队列中的消息,然后将消息分派给对应的处理者进行处理。

线程一旦被创建就会生成一个Looper对象,有且仅有一个。每个应用在运行的时候都会创建一个主线程(mainThread)。
主线程不能做耗时操作,子线程不能更新UI。

UIThread通常就是mian thread,而Android启动程序的时候就会替它建立一个MessageQueue。

2、Handler四模块

Message:存储需要处理操作的信息
MessageQueue:先进先出,存储handler发送过来的消息
Looper:循环器,它是消息队列和handler的通信媒介,
1:循环的取出消息队列中的消息;
2:将取出的消息发送给对应的处理者
Handler:主线程和子线程的通信媒介,
1:添加消息到消息队列;
2:处理循环器分派过来的消息

3、为什么不可在子线程中更新UI?

因为Android的UI控件不是线程安全的,如果在多线程并发访问UI控件可能会导致不可预期的状态。

4、为什么不对UI控件添加安全锁机制?

锁机制会使访问UI逻辑复杂,并且降低UI的访问效率,锁会阻塞某些线程的执行。

5、Handler为何使用管道?

同进程线程间内存共享,通过handler通信,消息的内容是不需要从一个线程拷贝到另一个线程,因为两个线程间可使用的内存是同一个区域。(注意:线程私有区域ThreadLocal)

管道的作用就是当一个线程准备好Message,并放入消息池,这时需要通知了一个线程B去处理这个消息。线程A向管道的写端写入数据,管道有数据便会唤醒线程B去处理消息。管道的作用是用于通知另一个线程的,这便是最核心的作用。

6、Handler为何使用管道而非binder

从内存角度,通信过程中binder涉及到一次内存拷贝,handler机制中的Message根本不需要拷贝,本身就是在同一片内存。

从CPU角度,为了Binder通信底层驱动还需要创建一个binder线程池,每次通信涉及binder线程的创建和内存的分配等比较浪费CPU资源

7、handler导致内存泄漏的原因

原因:handler发送的消息在当前handler的消息队列中,如果此时activity被finish掉了,那么消息队列的消息依旧由handler进行处理,若此时handler申明为内存类(非静态内部类),内部类持有外部类的实例引用,这样在GC垃圾回收时发现Activity还有其他引用存在,因而就不会去回首这个Activity,进而导致Activity泄漏。

方法:使用静态内部类,并且使用WeakReference包裹外部类的对象。首先静态内部类不持有外部类的引用,使用静态的handler不会导致activity的泄漏,handler定义static的同时,还要用WeakReference包裹外部类的对象。

MyHandler handler = new MyHandler(this);public static class MyHandler extends Handler {private WeakReference reference;public MyHandler(MainActivity activity) {reference = new WeakReference(activity);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:Log.i("test",textView.getText().toString());break;default:break;}}
}@Override
protected void onDestroy() {super.onDestroy();//避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列handler.removeCallbacks(postRunnable);   //清除runnable对应的message
}

8、Looper死循环为什么不会导致应用卡死?

  • 卡死就是ANR,产生的原因有2个:
    1、在5s内没有响应输入的事件(例如按键,触摸等),
    2、BroadcastReceiver在10s内没有执行完毕。
  • 事实上我们所有的Activity和Service都是运行在loop()函数中,以消息的方式存在,所以在没有消息产生的时候,looper会被block(阻塞),主线程会进入休眠,一旦有输入事件或者Looper添加消息的操作后主线程就会被唤醒,从而对事件进行响应,所以不会导致ANR
  • 简单来说looper的阻塞表明没有事件输入,而ANR是由于有事件没响应导致,所以looper的死循环并不会导致应用卡死。

9、子线程中维护的Looper,如何终止消息循环?有什么用?

  • 如果不处理的话,会阻塞线程,处理方案是调用Looper的quitSafely();
  • quitSafely()会调用MessageQueue的quit()方法,清空所有的Message,并调用nativeWake()方法唤醒之前被阻塞的
  • nativePollOnce(),使得方法next()方法中的for循环继续执行,接下来发现Message为null后就会结束循环,Looper结束。如此便可以释放内存和线程。

二、代码示例

使用 Handler 机制,首先需要创建一个Handler 对象,可以直接使用Handler 无参构造函创建Handler 对象,或者是继承Handler类,重写 handleMessage(Message msg)方法来创建handler对象。Google 官方提供了一个推荐的使用方式,代码如下∶

Class LooperThread extends Thread {public Handler mHandler;public void run(){Looper.prepare();mHandler = new Handler(){public void handleMessage(Message msg){// process incoming messages here}};Looper.loop();}
}

通过上一部分的分析,读者应该能够很容易理解上面这种方式。但是在实际的开发实践中,大部分的Handler 对象都是在主线程中创建的,此时已经存在了Looper 对象,并不需要调用Loopr.prepare()与 Looper.loop()方法,直接构建一个Handler 对象即可

    //调用new Thread(new MyThread()).start();// handle private final  Handler handler = new Handler(){ public void handleMessage(Message msg){  switch (msg.what) {  case 1://更新UI}}  super.handleMessage(msg);  }  };  // threadprivate class MyThread implements Runnable{@Override  public void run(){  while(true){  try{  	Thread.sleep(60*1000);//延时操作Message message = new Message();  message.what = 1;  handler.sendMessage(message);  }catch (Exception e) {  }  }  }  }

获取 Message 大概有如下几种方式。推荐使用前两种方式进行创建,原因是不需要重复去新建Message,可以节省内存空间。

Message message = myHandler.obtainMessage(); 		  //通过 Handler 实例获取
Message message1 = Message.obtain();   			      //通过 Message 获取
Message message2 = new Message();      				  //直接创建新的 Message 实例

三、参考

  • Android中handler的用法总结
  • Android Handler使用详解
  • Android中的Handler机制
  • Android——Handler详解

相关内容

热门资讯

裤腰的意思及造句 裤腰的意思及造句  裤腰拼音  【注音】: ku yao  裤腰解释  【意思】:裤子的最上端,系腰...
绿油油如何造句 绿油油如何造句  1.百合花的叶子十分漂亮,绿油油的,衬托着花朵。绿叶不可说四季常青,但绿起来却绿着...
三年级与众不同意思及造句   桂林的水独一无二,桂林的山与众不同。下面是小编为你带来的三年级与众不同意思及造句,欢迎阅读。  ...
暴厉恣睢的反义词 暴厉恣睢的反义词有:慈眉善目,暴厉恣睢[bào lì zí suī]的意思:暴:残暴;恣睢:横行霸道...
入超拼音解释及造句 入超拼音解释及造句  入超拼音  【注音】: ru chao  入超解释  【意思】:在一定时期(一...
今非昔比的反义词 今非昔比的反义词有:今不如昔,厚古薄今,慕古薄今,今非昔比[jīn fēi xī bǐ]的意思:昔:...
独具只眼的反义词 独具只眼的反义词有:一无所长,愚昧无知,独具只眼[dú jù zhī yǎn]的意思:具有独到的见解...
不以为然的反义词 不以为然的反义词有:五体投地,仰承鼻息,理所当然,顶礼膜拜,不以为然[bù yǐ wéi rán]的...
令人神往的反义词 令人神往的反义词有:令人作呕,令人切齿,令人痛心,令人神往[lìng rén shén wǎng]的...
补残守缺的反义词 补残守缺的反义词有:标新立异,补残守缺[bǔ cán shǒu quē]的意思:残:残缺,不完整;缺...
石沉大海的反义词 石沉大海的反义词有:一封家书,死灰复燃,石沉大海[shí chén dà hǎi]的意思:石头沉到海...
心胸开阔的反义词 心胸开阔的反义词有:小心眼儿,心胸狭隘,心胸开阔[xīn xiōng kāi kuò]的意思:心胸:...
毫不在意的反义词 毫不在意的反义词有:耿耿于怀,毫不在意[háo bù zài yì]的意思:丝毫不在乎或不介意出自:...
直来直去的反义词 直来直去的反义词有:拐弯抹角,曲尽其妙,绕脖子,直来直去[zhí lái zhí qù]的意思:指来...
高瞻远瞩的反义词   高瞻远瞩:站得高,看得远。比喻眼光远大。出自 汉·王充《论衡·别通篇》。  一、【反义词】  苟...
粗衣淡饭的反义词 粗衣淡饭的反义词有:锦衣玉食,粗衣淡饭[cū yī dàn fàn]的意思:粗:粗糙、简单;淡饭:指...
筋疲力尽的反义词 筋疲力尽的反义词有:容光焕发,精力充沛,精神抖擞,精神焕发,筋疲力尽[jīn pí lì jìn]的...
未老先衰的反义词 未老先衰的反义词有:人老心不老,老当益壮,返老还童,鹤发童颜,未老先衰[wèi lǎo xiān s...
男女有别的反义词 男女有别的反义词有:亲密无间,男女有别[nán nǚ yǒu bié]的意思:指男女之间有所分别出自...
二话不说的反义词 二话不说的反义词有:喋喋不休,二话不说[èr huà bù shuō]的意思:不说任何别的话。指立即...