全网营销型推广网站建设,中国建设银行什么是网站用户名,企业网设计案例分析,wordpress 父类 id以下的Handler的面试题都是在面试过程中总结出来比较常见的面试题#xff0c;现在分享给大家#xff0c;希望可以帮助你们#xff01;1.Handler的实现原理从四个方面看Handler、Message、MessageQueue 和 Looper Handler:负责消息的发送和处理 Message:消息对象#xff0c;…以下的Handler的面试题都是在面试过程中总结出来比较常见的面试题现在分享给大家希望可以帮助你们1.Handler的实现原理从四个方面看Handler、Message、MessageQueue 和 Looper Handler:负责消息的发送和处理 Message:消息对象类似于链表的一个结点; MessageQueue:消息队列用于存放消息对象的数据结构; Looper:消息队列的处理者用于轮询消息队列的消息对象) Handler发送消息时调用MessageQueue的enqueueMessage插入一条信息到MessageQueue,Looper不断轮询调用MeaasgaQueue的next方法 如果发现message就调用handler的dispatchMessagedispatchMessage被成功调用接着调用handlerMessage()。2.子线程中能不能直接new一个Handler,为什么主线程可以主线程的Looper第一次调用loop方法,什么时候,哪个类 不能因为Handler 的构造方法中会通过Looper.myLooper()获取looper对象如果为空则抛出异常 主线程则因为已在入口处ActivityThread的main方法中通过Looper.prepareMainLooper()获取到这个对象 并通过 Looper.loop()开启循环在子线程中若要使用handler可先通过Loop.prepare获取到looper对象并使用Looper.loop()开启循环3.Handler导致的内存泄露原因及其解决方案原因:1.Java中非静态内部类和匿名内部类都会隐式持有当前类的外部引用 2.我们在Activity中使用非静态内部类初始化了一个Handler,此Handler就会持有当前Activity的引用。 3.我们想要一个对象被回收那么前提它不被任何其它对象持有引用所以当我们Activity页面关闭之后,存在引用关系未被处理 / 正处理的消息 - Handler实例 - 外部类,如果在Handler消息队列 还有未处理的消息 / 正在处理消息时 导致Activity不会被回收从而造成内存泄漏 解决方案: 1.将Handler的子类设置成 静态内部类,使用WeakReference弱引用持有Activity实例 2.当外部类结束生命周期时清空Handler内消息队列4.一个线程可以有几个Handler,几个Looper,几个MessageQueue对象一个线程可以有多个Handler,只有一个Looper对象,只有一个MessageQueue对象。Looper.prepare()函数中知道。在Looper的prepare方法中创建了Looper对象并放入到ThreadLocal中并通过ThreadLocal来获取looper的对象, ThreadLocal的内部维护了一个ThreadLocalMap类,ThreadLocalMap是以当前thread做为key的,因此可以得知一个线程最多只能有一个Looper对象 在Looper的构造方法中创建了MessageQueue对象并赋值给mQueue字段。因为Looper对象只有一个那么Messagequeue对象肯定只有一个。5.Message对象创建的方式有哪些 区别Message.obtain()怎么维护消息池的 1.Message msg new Message(); 每次需要Message对象的时候都创建一个新的对象每次都要去堆内存开辟对象存储空间 2.Message msg Message.obtain(); obtainMessage能避免重复Message创建对象。它先判断消息池是不是为空如果非空的话就从消息池表头的Message取走,再把表头指向next。 如果消息池为空的话说明还没有Message被放进去那么就new出来一个Message对象。消息池使用Message 链表结构实现消息池默认最大值 50。消息在loop中被handler分发消费之后会执行回收的操作将该消息内部数据清空并添加到消息链表的表头。 3.Message msg handler.obtainMessage(); 其内部也是调用的obtain()方法6.Handler 有哪些发送消息的方法sendMessage(Message msg) sendMessageDelayed(Message msg, long uptimeMillis) post(Runnable r)postDelayed(Runnable r, long uptimeMillis) sendMessageAtTime(Message msg,long when)7.Handler的post与sendMessage的区别和应用场景1.源码sendMessage sendMessage-sendMessageAtTime-enqueueMessage。postsendMessage-getPostMessage-sendMessageAtTime-enqueueMessage getPostMessage会先生成一个Messgae并且把runnable赋值给message的callback2.Looper-dispatchMessage处理时public void dispatchMessage(NonNull Message msg) { if (msg.callback ! null) { handleCallback(msg); }else { if (mCallback ! null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }dispatchMessage方法中直接执行post中的runnable方法。而sendMessage中如果mCallback不为null就会调用mCallback.handleMessage(msg)方法如果handler内的callback不为空执行mCallback.handleMessage(msg)这个处理消息并判断返回是否为true如果返回true消息处理结束如果返回false,handleMessage(msg)处理。否则会直接调用handleMessage方法。post方法和handleMessage方法的不同在于区别就是调用post方法的消息是在post传递的Runnable对象的run方法中处理而调用sendMessage方法需要重写handleMessage方法或者给handler设置callback在callback的handleMessage中处理并返回true应用场景post一般用于单个场景 比如单一的倒计时弹框功能 sendMessage的回调需要去实现handleMessage Message则做为参数 用于多判断条件的场景。8.handler postDealy后消息队列有什么变化假设先 postDelay 10s, 再postDelay 1s, 怎么处理这2条消息sendMessageDelayedsendMessageAtTime-sendMessagepostDelayed传入的时间会和当前的时间SystemClock.uptimeMillis()做加和,而不是单纯的只是用延时时间。延时消息会和当前消息队列里的消息头的执行时间做对比如果比头的时间靠前则会做为新的消息头不然则会从消息头开始向后遍历找到合适的位置插入延时消息。 postDelay()一个10秒钟的Runnable A、消息进队MessageQueue调用nativePollOnce()阻塞Looper阻塞 紧接着post()一个Runnable B、消息进队判断现在A时间还没到、正在阻塞把B插入消息队列的头部A的前面然后调用nativeWake()方法唤醒线程MessageQueue.next()方法被唤醒后重新开始读取消息链表第一个消息B无延时直接返回给Looper Looper处理完这个消息再次调用next()方法MessageQueue继续读取消息链表第二个消息A还没到时间计算一下剩余时间假如还剩9秒继续调用nativePollOnce()阻塞 直到阻塞时间到或者下一次有Message进队9.MessageQueue是什么数据结构内部存储结构并不是真正的队列而是采用单链表的数据结构来存储消息列表 这点和传统的队列有点不一样主要区别在于Android的这个队列中的消息是按照时间先后顺序来存储的时间较早的消息越靠近队头。 当然我们也可以理解成它是先进先出的只是这里的先依据的不是谁先入队而是消息待发送的时间10.Handler怎么做到的一个线程对应一个Looper如何保证只有一个MessageQueue ThreadLocal在Handler机制中的作用设计的初衷是为了解决多线程编程中的资源共享问题 synchronized采取的是“以时间换空间”的策略本质上是对关键资源上锁让大家排队操作。 而ThreadLocal采取的是“以空间换时间”的思路 它一个线程内部的数据存储类通过它可以在制定的线程中存储数据数据存储以后只有在指定线程中可以获取到存储的数据 对于其他线程就获取不到数据可以保证本线程任何时间操纵的都是同一个对象。比如对于Handler它要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper。 ThreadLocal本质是操作线程中ThreadLocalMap来实现本地线程变量的存储的 ThreadLocalMap是采用数组的方式来存储数据其中key(弱引用)指向当前ThreadLocal对象value为设的值 通过ThreadLocal计算出Hash key通过这个哈 ThreadLocal对象value为设的值11.HandlerThread是什么 好处 原理 使用场景HHandlerThread本质上是一个线程类它继承了Thread HandlerThread有自己的内部Looper对象通过Looper.loop()进行looper循环 通过获取HandlerThread的looper对象传递给Handler对象然后在handleMessage()方法中执行异步任务优势: 1.将loop运行在子线程中处理,减轻了主线程的压力,使主线程更流畅,有自己的消息队列,不会干扰UI线程 2.串行执行,开启一个线程起到多个线程的作用劣势: 1.由于每一个任务队列逐步执行,一旦队列耗时过长,消息延时 2.对于IO等操作,线程等待,不能并发我们可以使用HandlerThread处理本地IO读写操作数据库文件因为本地IO操作大多数的耗时属于毫秒级别对于单线程 异步队列的形式 不会产生较大的阻塞12.IdleHandler及其使用场景Handler 机制提供的一种可以在 Looper 事件循环的过程中当出现空闲的时候允许我们执行任务的一种机制. IdleHandler在looper里面的message处理完了的时候去调用 怎么使用 IdleHandler 被定义在 MessageQueue中它是一个接口. 定义时需要实现其 queueIdle() 方法。返回值为 true 表示是一个持久的 IdleHandler 会重复使用返回 false 表示是一个一次性的IdleHandler。 IdleHandler 被 MessageQueue 管理对应的提供了addIdleHandler() 和 removeIdleHandler() 方法。将其存入 mIdleHandle addIdleHandler() 和removeIdleHandler() 方法。将其存入 mIdleHandlers 这个 ArrayList 中。 什么时候掉用 就在MessageQueue的next方法里面。 MessageQueue 为空没有 Message MessageQueue 中最近待处理的 Message是一个延迟消息whencurrentTime需要滞后执行 使用场景 1.Activity启动优化onCreateonStartonResume中耗时较短但非必要的代码可以放到IdleHandler中执行减少启动时间2.想要在一个View绘制完成之后添加其他依赖于这个View的View当然这个用View#post()也能实现区别就是前者会在消息队列空闲时执行 优化页面的启动,较复杂的view填充 填充里面的数据界面view绘制之前的话就会出现以上的效果了view先是白的再出现. app的进程其实是ActivityThread,performResumeActivity先回调onResume 之后 执行view绘制的measure, layout, draw,也就是说onResume的方法是在绘制之前在onResume中做一些耗时操作都会影响启动时间 把在onResume以及其之前的调用的但非必须的事件如某些界面View的绘制挪出来找一个时机即绘制完成以后去调用即可。13.消息屏障同步屏障机制what同步屏障只在Looper死循环获取待处理消息时才会起作用也就是说同步屏障在MessageQueue.next函数中发挥着作用。在next()方法中有一个屏障的概念(message.target null为屏障消息), 遇到target为null的Message说明是同步屏障循环遍历找出一条异步消息然后处理。 在同步屏障没移除前只会处理异步消息处理完所有的异步消息后就会处于堵塞 当出现屏障的时候会滤过同步消息而是直接获取其中的异步消息并返回, 就是这样来实现「异步消息优先执行」的功能how1、Handler构造方法中传入async参数设置为true使用此Handler添加的Message都是异步的 2、创建Message对象时直接调用setAsynchronous(true) 3.removeSyncBarrier() 移除同步屏障应用在 View 更新时draw、requestLayout、invalidate 等很多地方都调用了ViewRootImpl#scheduleTraversals(Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障14.子线程能不能更新UI刷新UI 都会调用到ViewRootImpl.Android每次刷新UI的时候最终根布局ViewRootImpl.checkThread()来检验线程是否是View的创建线程。 ViewRootImpl创建的第一个地方从Acitivity声明周期handleResumeActivity会被优先调用到也就是说在OnResume后ViewRootImpl就被创建这个时候无法在在子线程中访问UI了上面子线程延迟了一会handleResumeActivity已经被调用了所以发生了崩溃 不延迟在creae里直接设置不会崩溃 线程更新UI也行但是只能更新自己创建的View15.为什么Android系统不建议子线程访问UI在android中子线程可以有好多个但是如果每个线程都可以对ui进行访问我们的界面可能就会变得混乱不堪这样多个线程操作同一资源就会造成线程安全问题当然需要解决线程安全问题的时候我们第一想到的可能就是加锁但是加锁会降低运行效率所以android出于性能的考虑并没有使用加锁来进行ui操作的控制。16.Android中为什么主线程不会因为Looper.loop()里的死循环卡死MessageQueue#next 在没有消息的时候会阻塞如何恢复他不阻塞的原因是epoll机制他是linux里面的在native层会有一个读取端和一个写入端当有消息发送过来的时候会去唤醒读取端然后进行消息发送与处理没消息的时候是处于休眠状态所以他不会阻塞他。17.Handler消息机制中一个looper是如何区分多个Handler的当Activity有多个Handler的时候怎么样区分当前消息由哪个Handler处理处理message的时候怎么知道是去哪个callback处理的每个Handler会被添加到 Message 的target字段上面Looper 通过调用 Message.target.handleMessage() 来让 Handler 处理消息。18.Looper.quit/quitSafely的区别当我们调用Looper的quit方法时实际上执行了MessageQueue中的removeAllMessagesLocked方法该方法的作用是把MessageQueue消息池中所有的消息全部清空 无论是延迟消息延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息还是非延迟消息。当我们调用Looper的quitSafely方法时实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法通过名字就可以看出该方法只会清空MessageQueue消息池中所有的延迟消息并将消息池中所有的非延迟消息派发出去让Handler去处理quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。19.通过Handler如何实现线程的切换当在A线程中创建handler的时候同时创建了MessageQueue与LooperLooper在A线程中调用loop进入一个无限的for循环从MessageQueue中取消息当B线程调用handler发送一个message的时候会通过msg.target.dispatchMessage(msg);将message插入到handler对应的MessageQueue中Looper发现有message插入到MessageQueue中便取出message执行相应的逻辑因为Looper.loop()是在A线程中启动的所以则回到了A线程达到了从B线程切换到A线程的目的。20.Handler 如何与 Looper 关联的通过构造方法 mLooper Looper.myLooper()-sThreadLocal.get()( sThreadLocal.set)21.Looper 如何与 Thread 关联的Looper 与 Thread 之间是通过 ThreadLocal 关联的这个可以看 Looper#prepare() 方法 Looper 中有一个ThreadLocal 类型的 sThreadLocal静态字段Looper通过它的 get 和 set 方法来赋值和取值。 由于 ThreadLocal是与线程绑定的所以我们只要把 Looper 与 ThreadLocal 绑定了那 Looper 和 Thread 也就关联上了22.Looper.loop()源码for无限循环阻塞于消息队列的next方法 取出消息后调用msg.target.dispatchMessage(msg)进行消息分发23.MessageQueue的enqueueMessage()方法如何进行线程同步的就是单链表的插入操作 如果消息队列被阻塞回调用nativeWake去唤醒。 用synchronized代码块去进行同步。24.MessageQueue的next()方法内部原理next() 是如何处理一般消息的next() 是如何处理同步屏障的next() 是如何处理延迟消息的调用 MessageQueue.next() 方法的时候会调用 Native 层的 nativePollOnce() 方法进行精准时间的阻塞。在Native 层将进入 pullInner() 方法使用 epoll_wait 阻塞等待以读取管道的通知。如果没有从 Native 层得到消息那么这个方法就不会返回。此时主线程会释放 CPU 资源进入休眠状态。25.子线程中是否可以用MainLooper去创建HandlerLooper和Handler是否一定处于一个线程可以的。 子线程中Handler handler new Handler(Looper.getMainLooper());此时两者就不在一个线程中26.ANR和Handler的联系Handler是线程间通讯的机制Android中网络访问、文件处理等耗时操作必须放到子线程中去执行否则将会造成ANR异常。 ANR异常Application Not Response 应用程序无响应 产生ANR异常的原因在主线程执行了耗时操作对Activity来说主线程阻塞5秒将造成ANR异常对BroadcastReceiver来说主线程阻塞10秒将会造成ANR异常。 解决ANR异常的方法耗时操作都在子线程中去执行 但是Android不允许在子线程去修改UI可我们又有在子线程去修改UI的需求因此需要借助Handler。最近整理很多Android面试题一共包含了25个知识点答案也已经整理好了但是每个知识点问题都不多不过都是最近两年比较常见的有需要的可以点击这里或者点击文末小卡片获取。知识点Java基础Android基础Android中高级ActivityServiceBroadcastReceiverContentProviderHandlerView绘制View事件分发Binder性能优化BroadcastReceiverViewpagerFragmentWebView动画BitmapAmsAPP系统启动流程序列化及Art模块化及组件化热修复AOPjetpack开源框架