安卓中的Handler是怎样的

一:Handler认识

Handler: 是实现android中不同线程间通信的一种机制,它允许主线程UI线程通过发送message和runable对象到handler,在其handler关联线程中处理这些消息和runnable、      其常用于执行异步任务处理,网络请求/文件读写等,将耗时操作放在单独线程中执行并通过handler进行返回UI主线程,可以避免阻塞UI主线程,提高响应速度。。
    <<<<//  一个handler只与创建它的线程相关联、但同一个线程可以有无数个handler对象、、这些handler只能处理此线程的MessageQueue消息队列中的消息。    对应的每个线程只能有一个Looper对象,而Looper对象与消息队列相关联。所以这个线程中的这些handler共享一个Looper和消息队列。//>>>>>
Handler:  处理消息的对象,它负责发送和处理消息、sendMessage()/post()
MessageQueue:  消息队列-存储待处理的消息-顺序执行,先进先出
Looper:        消息循环器,用于管理线程的消息队列,每个线程只能有一个Looper对象,负责循环队列中的消息,并将消息传递给对应handler进行处理、

        Android线程的生命周期受限于Activity或Service的生命周期。当Activity或Service被销毁时,与之关联的线程也会被销毁

问题:

1.  为什么Android少有多线程问题,如何实现多线程间通信?                        handler核心

        handler,它允许你在一个线程中发送消息到另一个线程的Looper,可以在UI主线程中创建一个Handler实例,然后在其他线程中使用此handler实例的sendMessage()post()方法发送消息或Runnable对象到主线程UI进行处理操作

2. 生产者-消费者设计模式、内存共享原理、ThreadLoacl原理?                    架构师眼中的handler

        如何管理那块共享内存的?    可以从中学到怎样的设计思维?

        a. 多个线程同时访问共享内存时,需要通过加锁、信号量等机制来保证数据的一致性和完整性

        b. 在共享内存的使用中,异步处理同样重要。通过异步地读取或写入共享内存,可以避免因为等待I/O操作完成而导致的线程阻塞。

        c. Handler通过发送消息的方式实现了线程间的解耦,使得线程间不需要直接调用对方的方法,从而降低了代码的耦合度。

        解耦与封装‌、异步处理-提升性能、消息队列与事件驱动、线程安全

3. 为什么Block不会卡死线程?架构师是如何共享内存,防止内存抖动?        Handler延时的根本

        Block不会卡死线程主要是因为它在GPU中的并行执行模型和内存管理机制,

        Block在GPU中是由多个线程组成的集合,这些线程在执行时可以并行工作。由于GPU擅长并行处理,Block中的线程可以高效地同时运行,从而避免了单个线程因等待资源而导致的卡死现象。

         架构师通过合理的内存管理和并发控制策略来共享内存并防止内存抖动。

内存池-通过预先分配一块较大的内存作为内存池,然后在需要时从内存池中分配内存

合理容量规划-在创建共享数据结构(如阻塞队列)时,设置一个合理的容量

并发控制‌:通过合理的并发控制策略,如使用锁、信号量或原子操作等,来确保多个线程在访问共享内存时的安全性和一致性

重点:Handler在Android中的定位:

难点一:Handler事件阻塞的原理?

  • 当消息队列中没有消息或Runnable任务时,Looper会调用nativePollOnce()方法,并传入一个超时时间(如果有消息即将到期,则为消息到期时间与当前时间的差值;否则为无限长)。
  • nativePollOnce()方法最终会调用epoll_wait()系统调用,将线程挂起,等待新消息的到来。在这个过程中,线程不会占用CPU资源。
  • 当有新消息被添加到消息队列中时,系统会唤醒阻塞的Looper线程,Looper线程会从消息队列中取出消息或Runnable任务进行处理。
  • 每个线程可以有一个Looper对象,用于管理该线程的消息循环。Looper会不断地从消息队列中取出消息或Runnable任务进行处理。
  • 当消息队列为空时,Looper会调用nativePollOnce()方法进入阻塞状态,等待新消息的到来。这个阻塞过程是由底层的epoll机制实现的,它允许线程在等待I/O事件时释放CPU资源。
    • 阻塞机制允许线程在等待新消息时释放CPU资源,提高系统的整体性能和响应能力。

难点二:nativePollOnce  nativeWake

什么是epoll?
        epoll是一种高效且可扩展的事件通知机制,适用于高性能的网络编程。
  • epoll通过在内核与用户空间之间共享一个事件表来工作。这个事件表中存放着所有需要监控的文件描述符以及它们的状态。当文件描述符的状态发生变化时,内核会将这个事件通知给用户空间,用户空间再根据事件类型进行相应的处理。
  • epoll是一种通过监听文件描述符的机制,进行内核与用户空间之间通信。   是一种事件通知机制,,是linux内核为处理大批量文件描述符而改进的I/O多路复用机制。
消息机制之同步屏障
        Android的消息机制之同步屏障是一种在消息队列中插入的特殊消息,用于临时忽略同步消息,以便优先处理异步消息‌。
        同步屏障消息在消息队列中插入后,会形成一个屏障,使得屏障之后的所有普通同步消息都被暂时忽略,不能被处理。但是,异步消息却例外,它们可以越过屏障被优先处理。这样,通过设置同步屏障,系统可以确保异步消息的优先级,减少因同步消息处理而导致的延迟。
        是为了优先处理异步消息,通过设置异步消息优先级,减少延迟。
跨进程的核心原理        
  1. Binder机制的基本思想‌:

             在服务端进程中,有一个Binder驱动和一个Binder服务。客户端进程则通过这个Binder驱动与服务端进程进行沟通。这种跨进程通信的方式不仅高效,而且线程安全。
  2. Binder驱动的作用‌:      Binder驱动在内核空间运行,它负责进程间的数据交换和通信转发。通过Binder驱动,不同进程间的内存空间被有效地隔离开来,保证了通信的安全MessageQueue源码分析
Looper源码分析
Handler:主要函数

问题归纳:

1. 一个线程有几个handler?                      可以无数个,但只有一个Looper和MessageQueue

2. 一个线程有几个Looper,如何保证        1个

        Looper.prepare()进行创建Looper实例,会进行检查-有则不创建,,Looper类中有一个静态ThreadLocal变量用于存储每个线程的Looper实例,,创建并初始化Looper实例后,调用Looper.loop()启动消息循环,从循环中取出消息进行处理。。

        一个线程可以通过使用LooperThreadLocal来确保该线程中只有一个Looper实例。

3. handler内存泄漏原因?为什么其它的内部类没有说过这个问题?

        这主要是因为其他内部类通常不会涉及到与Looper线程和消息队列的长期关联。在Android开发中,Handler通常用于在主线程中处理UI相关的操作,或者在不同的线程间传递消息。由于Handler可能会持有外部类(如Activity或Fragment)的引用,并且与Looper线程相关联,因此如果Handler在外部类被销毁后仍然存活(例如,在一个静态内部类或匿名内部类中),就会导致内存泄漏。       handler在内部类中,外部类Activity被销毁(例如,用户旋转了屏幕导致Activity重建)后handler仍然存活就会导致内存泄漏。

        外部类实例(Activity或Fragment)被销毁后无法被垃圾回收,因为其中的Handler与Looper线程(通常是UI线程)相关联,如果Looper线程持续运行(如UI线程),那么与之关联的Handler也将持续存在

        其它内部类不像handler,是因为多半会被垃圾回收器回收,不持有Looper线程和消息队列的长期关联。

4 为什么主线程可以new handler,子线程要new handler要做哪些准备?

        主线程在Android应用启动时,由ActivityThread负责创建并启动Looper,具体是在Looper.prepareMainLooper()中准备Looper,并在之后的某个时刻调用Looper.loop()进入消息循环。因此,主线程已经有了Looper对象,并且处于消息循环中,所以可以直接创建Handler。

         子线程要new Handler,需要先手动创建Looper对象,并调用Looper.prepare()Looper.loop()方法来准备和开启消息循环‌。

5. 子线程中维护的looper,消息队列无消息的时候处理方案是如何的,有什么作用?

        在子线程中维护的Looper,当消息队列无消息时的处理方案通常是调用Looper.quitSafely()方法。这个方法的作用是安全地退出Looper的消息循环,释放资源并释放线程。通过调用quitSafely()方法,可以确保Looper在完成所有已排队消息的处理后,安全地退出消息循环,从而释放与该Looper关联的资源,并让线程能够顺利终止。        避免死锁,提升性能。

6. 既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个handler可能处于不同线程),那它内部是如何确保线程安全的?

        虽然可以存在多个Handler往同一个MessageQueue中添加数据,但MessageQueue内部通过一些机制来确保线程安全。1. 锁机制、2同步块如:enqueueMessage,next等被包裹、3.线程局部变量(ThreadLocal)确保每个线程有独立的Looper/MessageQueue、4. 单线程模型:独立的,单线程访问MessageQueue、5.FIFO方式先进先出顺序执行  6.MessageQueue还使用了条件变量(Condition Variable)来管理消息的等待和唤醒。当Looper线程在没有消息时进入等待状态,如果有新消息插入到队列中,MessageQueue会唤醒等待的线程,使其继续处理消息。
7:我们使用Message时应该如何创建它?

        1. 从handler的实例获取:myHandler.obtainMessage();   2.Messageobtain方法‌从message线程池获取  3. 不建议new Message()
8:Looper死循环为什么不会导致应用卡死?

        Looper.loop()方法通过高效的消息处理机制保证了应用的响应能力和流畅性。当消息队列为空时,Looper会进入阻塞状态,进行等待、、有消息时MessageQueue消息队列会进行唤醒线程进行处理。。。


问题:    数据通信会带来开发中什么样问题?

1. 线程间如何通信?

2.为什么线程间不会干扰?

3.为什么wait和notify用武之地不大?

答:

1. Handler通信实现的方案实际上是内存共享的方案。

2.内存管理设计思路优秀。

3. 因为handler已经将需要这部分的功能/位置进行了linux层封装。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值