【Android】之【Handler】

一、面试必问

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<MainActivity> reference;

	public MyHandler(MainActivity activity) {
		reference = new WeakReference<MainActivity>(activity);
	}

	@Override
	public 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);  
        }  
    };  
  
    // thread
    private 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 实例

三、参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Crazy程序猿2020

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值