handler面试必问八大题:如何深挖原理进大厂?1万+字带你详细剖析!(代码片段)

Young-G2333 Young-G2333     2022-12-27     656

关键词:

Handler一直是面试过程中的常客,我们今天来看看围绕Handler究竟能玩出那些花儿来。

Handler机制几乎是Android面试时必问的问题,虽然看过很多次handler源码,但是有些面试官问的问题却不一定能够回答出来,趁着机会下面总结一下面试中所覆盖的Handler知识点。

题目层次

1.简述Handler的实现原理
2.一个线程有几个Handler?一个线程有几个Looper?如何保证?
3.Handler线程是如何切换的?
4.Handler内存泄漏的原因是什么?如何解决?
5.主线程为什么不用初始化Looper?
6.Handler如何保证MessageQueue并发访问安全?
7.Handler的阻塞唤醒机制是怎么回事?
8.能不能让一个Message加急被处理?/ 什么是Handler同步屏障?

更多Android大厂高频面试题及答案解析,点击下面二维码可以看到全部内容。

 

题目详解

1.简述Handler的实现原理

Android 应用是通过消息驱动运行的,在 Android 中一切皆消息,包括触摸事件,视图的绘制、显示和刷新等等都是消息。Handler 是消息机制的上层接口,平时开发中我们只会接触到 Handler 和 Message,内部还有 MessageQueue 和 Looper 两大助手共同实现消息循环系统。

(1)Handler 通过Handler的sendXXX或者postXXX来发送一个消息,这里要注意post(Runnable r)方法也会将Runnable包装成一个Message,代码如下:

    public final boolean post(Runnable r)
        return  sendMessageDelayed(getPostMessage(r), 0);
    
    public final boolean postDelayed(Runnable r, long delayMillis)
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    
private static Message getPostMessage(Runnable r) 
        Message m = Message.obtain();
        m.callback = r;
        return m;
    

从代码中可以看到将Runnable赋值给了Message.callback了。最终sendXXX和postXXX都会调用到sendMessageAtTime,代码如下:

 public boolean sendMessageAtTime(Message msg, long uptimeMillis) 
        MessageQueue queue = mQueue;
        if (queue == null) 
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        
        return enqueueMessage(queue, msg, uptimeMillis);
    

在这个方法中最终调用了enqueueMessage方法,这里注意将this赋值给了Message.target,而此处this就是Handler。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 
        msg.target = this;
        if (mAsynchronous) 
            msg.setAsynchronous(true);
        
        //转到 MessageQueue 的 enqueueMessage 方法
        return queue.enqueueMessage(msg, uptimeMillis);
    

enqueueMessage方法最终调用了MessageQueue的enqueueMessage方法,将消息放入队列。

(2)MessageQueue MessageQueue是一个优先级队列,核心方法是enqueueMessage和next方法,也就是将插入队列,将消息取出队列的操作。 之所以说MessageQueue是一个优先级队列是因为enqueueMessage方法中会根据Message的执行时间来对消息插入,这样越晚执行的消息会被插入到队列的后边。

而next方法是一个死循环,如果队列中有消息,则next方法会将Message移除队列并返回该Message,如果队列中没有消息该方法则会处于阻塞状态。

(3)Looper Looper可以理解为一个消息泵,Looper的核心方法是loop。注意loop方法的第一行会首先通过myLooper来得到当前线程的Looper,接着拿到Looper中的MessageQueue,然后开启一个死循环,它会不断的通过MessageQueue的next方法将消息取出来,并执行。代码如下:

public static void loop() 
        final Looper me = myLooper();// 这里要特别注意,是从ThreadLocal中拿到当前线程的Looper。
        if (me == null) 
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        
        final MessageQueue queue = me.mQueue;

        for (;;) 
            //从 MessageQueue 中取消息
            Message msg = queue.next(); // might block
            if (msg == null) 
                // No message indicates that the message queue is quitting.
                return;
            
            //通过 Handler 分发消息
            msg.target.dispatchMessage(msg);
            //回收消息
            msg.recycleUnchecked();
        
    

可以看到在取出Message后则会调用Message.target调用dispatchMessage方法,这里target就是Handler,它是在Handler的enqueueMessage时赋值的。紧接着将Message进行了回收。 接下来再回到Handler看dispatchMessage,代码如下:

    public void dispatchMessage(Message msg) 
        if (msg.callback != null) 
            //通过 handler.postXxx 形式传入的 Runnable
            handleCallback(msg);
         else 
            if (mCallback != null) 
                //以 Handler(Handler.Callback) 写法
                if (mCallback.handleMessage(msg)) 
                    return;
                
            
            //以 Handler() 内存泄露写法
            handleMessage(msg);
        
    

可以看到,这里最终会调用到我们自己的实现方法。至此完结。

2.一个线程有几个Handler?一个线程有几个Looper?如何保证?

Handler的个数与所在线程无关,可以在线程中实例化任意多个Handler。一个线程中只有一个Looper。Looper的构造方法被声明为了private,我们无法通过new关键字来实例化Looper,唯一开放的可以实例化Looper的方法是prepare。prepare方法的源码如下:

    public static void prepare() 
        prepare(true);
    

    private static void prepare(boolean quitAllowed) 
        if (sThreadLocal.get() != null) 
            throw new RuntimeException("Only one Looper may be created per thread");
        
        sThreadLocal.set(new Looper(quitAllowed));
    

我们知道ThreadLocal是一个线程内部的数据存储类,当某个线程调用prepare方法的时候,会首先通过ThreadLocal检查这个线程是否已经创建了Looper,如果还没创建,则实例化Looper并将实例化后的Looper保存到ThreadLocal中,而如果ThreadLocal中已经保存了Looper,则会抛出一个RuntimeException的异常。那么意味着在一个线程中最多只能调用一次prepare方法,这样就保证了Looper的唯一性。

3.Handler线程是如何切换的?

(1)假设现在有一个线程A,在A线程中通过Looper.prepare和Looper.loop来开启Looper,并且在A线程中实例化出来一个Handler。Looper.prepare()方法被调用时会为会初始化Looper并为ThreadLocal 设置Looper,此时ThreadLocal中就存储了A线程的Looper。另外MessageQueue也会在Looper中被初始化。

(2)接着当调用Loop.loop方法时,loop方法会通过myLooper得到A线程中的Looper,进而拿到Looper中的MessageQueue,接着开启死循环等待执行MessageQueue中的方法。

(3)此时,再开启一个线程B,并在B线程中通过Handler发送出一个Message,这个Message最终会通过sendMessageAtTime方法调用到MessageQueue的equeueMessage方法将消息插入到队列。

(4)由于Looper的loop是一个死循环,当MessageQueue中被插入消息的时候,loop方法就会取出MessageQueue中的消息,并执行callback。而此时,Looper是A线程的Looper,进而调用的Message或者Handler的Callback都是执行在A线成中的。以此达到了线程的切换。

4.Handler内存泄漏的原因是什么?如何解决?

通常在使用Handler的时候回通过匿名内部类的方式来实例化Handler,而非静态的匿名内部类默认持有外部类的引用,即匿名内部类Handler持有了外部类。而导致内存泄漏的根本原因是是因为Handler的生命周期与宿主的生命周期不一致。

比如说在Activity中实例化了一个非静态的匿名内部类Handler,然后通过Handler发送了一个延迟消息,但是在消息还未执行时结束了Activity,此时由于Handler持有Activity,就会导致Activity无法被GC回收,也就是出现了内存泄漏的问题。

解决方式:可以把Handler声明为静态的匿名内部类,但这样一来,在Handler内部就没办法调用到Activity中的非静态方法或变量。那么最终的解决方案可以使用静态内部类 + 弱引用来解决。代码如下:

public class MainActivity extends AppCompatActivity 

    private MyHandler mMyHandler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    

    private void handleMessage(Message msg) 

    

    static class MyHandler extends Handler 
        private WeakReference<Activity> mReference;

        MyHandler(Activity reference) 
            mReference = new WeakReference<>(reference);
        

        @Override
        public void handleMessage(Message msg) 
            MainActivity activity = (MainActivity) mReference.get();
            if (activity != null) 
                activity.handleMessage(msg);
            
        
    

    @Override
    protected void onDestroy() 
        mMyHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    

5.主线程为什么不用初始化Looper?

答:因为应用在启动的过程中就已经初始化主线程Looper了。

每个java应用程序都是有一个main方法入口,Android是基于Java的程序也不例外。Android程序的入口在ActivityThread的main方法中:

public static void main(String[] args) 
    ...
 // 初始化主线程Looper
    Looper.prepareMainLooper();
    ...
    // 新建一个ActivityThread对象
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    // 获取ActivityThread的Handler,也是他的内部类H
    if (sMainThreadHandler == null) 
        sMainThreadHandler = thread.getHandler();
    

    ...
    Looper.loop();
 // 如果loop方法结束则抛出异常,程序结束
    throw new RuntimeException("Main thread loop unexpectedly exited");

main方法中先初始化主线程Looper,新建ActivityThread对象,然后再启动Looper,这样主线程的Looper在程序启动的时候就跑起来了。我们不需要再去初始化主线程Looper。

6.Handler如何保证MessageQueue并发访问安全?

答:循环加锁,配合阻塞唤醒机制。

我们可以发现MessageQueue其实是“生产者-消费者”模型,Handler不断地放入消息,Looper不断地取出,这就涉及到死锁问题。如果Looper拿到锁,但是队列中没有消息,就会一直等待,而Handler需要把消息放进去,锁却被Looper拿着无法入队,这就造成了死锁。Handler机制的解决方法是循环加锁。在MessageQueue的next方法中:

Message next() 
   ...
    for (;;) 
  ...
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) 
            ...
        
    

我们可以看到他的等待是在锁外的,当队列中没有消息的时候,他会先释放锁,再进行等待,直到被唤醒。这样就不会造成死锁问题了。

那在入队的时候会不会因为队列已经满了然后一边在等待消息处理一边拿着锁呢?这一点不同的是MessageQueue的消息没有上限,或者说他的上限就是JVM给程序分配的内存,如果超出内存会抛出异常,但一般情况下是不会的。

7.Handler的阻塞唤醒机制是怎么回事?

答: Handler的阻塞唤醒机制是基于Linux的阻塞唤醒机制。

这个机制也是类似于handler机制的模式。在本地创建一个文件描述符,然后需要等待的一方则监听这个文件描述符,唤醒的一方只需要修改这个文件,那么等待的一方就会收到文件从而打破唤醒。和Looper监听MessageQueue,Handler添加message是比较类似的。

8.能不能让一个Message加急被处理?/ 什么是Handler同步屏障?

答:可以 / 一种使得异步消息可以被更快处理的机制

如果向主线程发送了一个UI更新的操作Message,而此时消息队列中的消息非常多,那么这个Message的处理就会变得缓慢,造成界面卡顿。所以通过同步屏障,可以使得UI绘制的Message更快被执行。

什么是同步屏障?这个“屏障”其实是一个Message,插入在MessageQueue的链表头,且其target==null。Message入队的时候不是判断了target不能为null吗?不不不,添加同步屏障是另一个方法:

public int postSyncBarrier() 
    return postSyncBarrier(SystemClock.uptimeMillis());


private int postSyncBarrier(long when) 
    synchronized (this) 
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;
        // 把当前需要执行的Message全部执行
        if (when != 0) 
            while (p != null && p.when <= when) 
                prev = p;
                p = p.next;
            
        
        // 插入同步屏障
        if (prev != null)  // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
         else 
            msg.next = p;
            mMessages = msg;
        
        return token;
    

可以看到同步屏障就是一个特殊的target,哪里特殊呢?target==null,我们可以看到他并没有给target属性赋值。那这个target有什么用呢?看next方法:

Message next() 
    ...

    // 阻塞时间
    int nextPollTimeoutMillis = 0;
    for (;;) 
        ...
        // 阻塞对应时间 
        nativePollOnce(ptr, nextPollTimeoutMillis);
  // 对MessageQueue进行加锁,保证线程安全
        synchronized (this) 
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            /**
            *  1
            */
            if (msg != null && msg.target == null) 
                // 同步屏障,找到下一个异步消息
                do 
                    prevMsg = msg;
                    msg = msg.next;
                 while (msg != null && !msg.isAsynchronous());
            
            if (msg != null) 
                if (now < msg.when) 
                    // 下一个消息还没开始,等待两者的时间差
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                 else 
                    // 获得消息且现在要执行,标记MessageQueue为非阻塞
                    mBlocked = false;
                    /**
              *  2
              */
                    // 一般只有异步消息才会从中间拿走消息,同步消息都是从链表头获取
                    if (prevMsg != null) 
                        prevMsg.next = msg.next;
                     else 
                        mMessages = msg.next;
                    
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                
             else 
                // 没有消息,进入阻塞状态
                nextPollTimeoutMillis = -1;
            

            // 当调用Looper.quitSafely()时候执行完所有的消息后就会退出
            if (mQuitting) 
                dispose();
                return null;
            
            ...
        
        ...
    

这个方法我在前面讲过,我们重点看一下关于同步屏障的部分,看注释1的地方的代码:

if (msg != null && msg.target == null) 
    // 同步屏障,找到下一个异步消息
    do 
        prevMsg = msg;
        msg = msg.next;
     while (msg != null && !msg.isAsynchronous());

如果遇到同步屏障,那么会循环遍历整个链表找到标记为异步消息的Message,即isAsynchronous返回true,其他的消息会直接忽视,那么这样异步消息,就会提前被执行了。注释2的代码注意一下就可以了。

注意,同步屏障不会自动移除,使用完成之后需要手动进行移除,不然会造成同步消息无法被处理。从源码中可以看到如果不移除同步屏障,那么他会一直在那里,这样同步消息就永远无法被执行了。

有了同步屏障,那么唤醒的判断条件就必须再加一个:MessageQueue中有同步屏障且处于阻塞中,此时插入在所有异步消息前插入新的异步消息。这个也很好理解,跟同步消息是一样的。如果把所有的同步消息先忽视,就是插入新的链表头且队列处于阻塞状态,这个时候就需要被唤醒了。看一下源码:

boolean enqueueMessage(Message msg, long when) 
    ...

    // 对MessageQueue进行加锁
    synchronized (this) 
        ...
        if (p == null || when == 0 || when < p.when) 
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
         else 
            /**
            * 1
            */
            // 当线程被阻塞,且目前有同步屏障,且入队的消息是异步消息
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) 
                prev = p;
                p = p.next;
                if (p == null || when < p.when) 
                    break;
                
                /**
                * 2
                */
                // 如果找到一个异步消息,说明前面有延迟的异步消息需要被处理,不需要被唤醒
                if (needWake && p.isAsynchronous()) 
                    needWake = false;
                
            
            msg.next = p; 
            prev.next = msg;
        

        // 如果需要则唤醒队列
        if (needWake) 
            nativeWake(mPtr);
        
    
    return true;

同样,这个方法我之前讲过,把无关同步屏障的代码忽视,看到注释1处的代码。如果插入的消息是异步消息,且有同步屏障,同时MessageQueue正处于阻塞状态,那么就需要唤醒。而如果这个异步消息的插入位置不是所有异步消息之前,那么不需要唤醒,如注释2。

那我们如何发送一个异步类型的消息呢?有两种办法:

  • 使用异步类型的Handler发送的全部Message都是异步的
  • 给Message标志异步

Handler有一系列带Boolean类型的参数的构造器,这个参数就是决定是否是异步Handler:

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) 
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    // 这里赋值
    mAsynchronous = async;

但是异步类型的Handler构造器是标记为hide,我们无法使用,所以我们使用异步消息只有通过给Message设置异步标志:

public void setAsynchronous(boolean async) 
    if (async) 
        flags |= FLAG_ASYNCHRONOUS;
     else 
        flags &= ~FLAG_ASYNCHRONOUS;
    

但是!!!! ,其实同步屏障对于我们的日常使用的话其实是没有多大用处。因为设置同步屏障和创建异步Handler的方法都是标志为hide,说明谷歌不想要我们去使用他。所以这里同步屏障也作为一个了解,可以更加全面地理解源码中的内容。

至此,关于面试时异步消息机制Handler八大必问 讲解完毕。

最后

如何更好地学习Handler呢?

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是浮于表象,这对我们的知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读Android系统源码,还包括各种优秀的开源库。

最后为了帮助大家深刻理解Handler相关知识点的原理以及面试相关知识,这里还为大家整理了Android开发相关源码精编解析

深入解析 Handler 源码解析

  • 发送消息
  • 消息入队
  • 消息循环
  • 消息遍历
  • 消息的处理
  • 同步屏障机制
  • 阻塞唤醒机制

还有Handler相关面试题解析帮助熟练掌握Handler知识:

然后关于面试复习的路线及方法,如果你觉得自己学习效率低,缺乏正确的指导,可以参考下下面分享我多年工作以来收集整理的学习路线,给大家做个参考:

1、确定好方向,梳理成长路线图

不用多说,相信大家都有一个共识:无论什么行业,最牛逼的人肯定是站在金字塔端的人。所以,想做一个牛逼的程序员,那么就要让自己站的更高,成为技术大牛并不是一朝一夕的事情,需要时间的沉淀和技术的积累。

关于这一点,在我当时确立好Android方向时,就已经开始梳理自己的成长路线了,包括技术要怎么系统地去学习,都列得非常详细。

接下来是每个模块我整理出来的文档学习资料,帮助大家更细致化的学习。

2、看视频进行系统学习

前几年的Crud经历,让我明白自己真的算是菜鸡中的战斗机,也正因为Crud,导致自己技术比较零散,也不够深入不够系统,所以重新进行学习是很有必要的。我差的是系统知识,差的结构框架和思路,所以通过视频来学习,效果更好,也更全面。关于视频学习,个人可以推荐去B站进行学习,B站上有很多学习视频,唯一的缺点就是免费的容易过时。

另外,我自己也珍藏了好几套视频,有需要的我也可以分享给你。

3、通过源码来系统性地学习

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

“编程语言是程序员的表达的方式,而架构是程序员对世界的认知”。所以,程序员要想快速认知并学习架构,读源码是必不可少的。阅读源码,是解决问题 + 理解事物,更重要的:看到源码背后的想法;程序员说:读万行源码,行万种实践。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

《486页超全面Android开发相关源码精编解析》

4、简历准备充分

可以去知乎上面搜搜别人写的有关简历的帖子,怎么准备,吸引hr, 突出自己的优点和能力,或者让朋友帮你看看简历有没有问题,比如过于简单或者浮夸,没有重点等。

尽量把你的亮点总结成一句简练的话,再加上数字来说明它的影响和意义。

其次在简历里中加入了可交互、可展示的内容,更能显出你的能力与众不同。

有很多人经常也会遇到很多关于简历制作,职业困惑、HR经典面试问题回答等有关面试的问题。同样我也搜集整理了全套简历制作、金三银四社招困惑、HR面试等问题解析,有疑问,可以提供专业的解答。

5、刷题备战,直通大厂

面试的前一周时间内,就可以开始刷题冲刺了。请记住,刷题的时候,技术的优先,算法的看些基本的,比如排序等即可,而智力题,除非是校招,否则一般不怎么会问。

关于面试刷题,我个人也准备了一套系统的面试题,帮助你举一反三。

《379页Android开发面试宝典》

以上这些资料都已打包整理完毕:

 

android大厂面试必问的androidframework如何学习,如何深入了解framework层?

...r线程池的工作过程是什么样?(东方头条)5.Handler怎么进行线程通信,原理是什么?(东方头条)6.Handler如果没有消息处理是阻塞的还是非阻塞的?(字节跳动、小米)7.handler.post(Runnable)run... 查看详情

android大厂高级面试必问36题以及算法合集(附:2022年android中高级面试题汇总以及面试题解析)(代码片段)

AndroidBAT高级面试必问36题详细说说Binder通信原理与机制Linux自带多种进程通信方式,为什么Android都没采用二偏偏使用Binder通信进程保活如何做到,你们保活率有多高谈一谈Binder的原理和实现一次拷贝的流程ButterKnife为什么... 查看详情

android大厂面试必问的androidframework如何学习,如何深入了解framework层?

...而且还要精通的技能。高级Android工程师岗位的一些技术面试也离不开Framework。一般会针对下面几个面试题进行提问:1.Android中多进程通信的方式有哪些?2.进程通信你用过哪些?原理是什么?(字节跳动、 查看详情

bat大厂面试官必问的hashmap相关面试题及部分源码分析

BAT大厂面试官必问的HashMap相关面试题及部分源码分析0引言1JDK8中的HashMap与JDK7的HashMap有什么不一样?2HashMap中put方法流程3HashMap的get方法流程4HashMap扩容流程是怎样的?5谈谈你对红黑树的理解为什么HashMap的数组的大小是2的幂次方... 查看详情

80%大厂必问的面试题,附答案

面向对象的三个特征封装,继承,多态多态的好处,代码中如何实现多态,虚拟机中如何实现多态允许不同类对象对同一消息作出相应,好处如下:可替换性:多态对已存在的代码具有可替换性可扩充... 查看详情

java注解原理面试题,跳槽大厂必看!

part1Java基础核心技术总结万事都离不开根基,Java基础对于一些老鸟来说可能已经很熟悉了,但是对于很多开发5年以下的朋友掌握的或许不是很牢固,因此这份笔记更适合温故而知新。Part2并发编程(架构师筑基必... 查看详情

android大厂面试必问framework层面试题(展望2022,备战金三银四)

...inder机制原理?Binder线程池的工作过程是什么样?Handler怎么进行线程通信,原理是什么?Handler如果没有消息处理是阻塞的还是非阻塞的?handler.post(Runnable)runnable是如何执行的?handler的Callback和handleme 查看详情

《我想进大厂》之mq夺命连环11问

...的,什么夺命zookeeper,夺命多线程一大堆,这一次,开始面试题系列MQ专题,消息队列作为日常常见的使用中间件,面试也是必问的点之一,一起来看看MQ的面试题。你们为什么使用mq?具体的使用场景是什么?mq的作用很简单,... 查看详情

最新android大厂高频面试题解析大全

...和flag详解3.Android源码分析-资源加载机制4.Android中Thread、Handler、Looper、MessageQueue的原理分析5.Android源码解析之setContentView6.AndroidAsyncTask源 查看详情

大厂高级工程师面试必问系列:java动态代理机制和实现原理详解(代码片段)

代理模式Java动态代理运用了设计模式中常用的代理模式代理模式:目的就是为其他对象提供一个代理用来控制对某个真实对象的访问代理类的作用:为委托类预处理消息过滤消息并转发消息进行消息被委托类执行后的后续处理通过... 查看详情

最新android大厂高频面试题解析大全

...和flag详解3.Android源码分析-资源加载机制4.Android中Thread、Handler、Looper、MessageQueue的原理分析5.Android源码解析之setContentView6.AndroidAsyncTask源码解析7.AndroidlntentService完全解析当Service遇到Handler8.深入剖析Android中的ArrayMap…第二章性能... 查看详情

备战2022一二线互联网公司android面试题汇总,48份(2010-2021)大厂面试题整理分享

...现原理BroadcastReceiver,LocalBroadcastReceiver区别Bundle机制Handler机制android事件传递机制线程间操作ListApp启动流程,从点击桌面开始动态加载类加载器OSGIHttps请求慢的 查看详情

大佬勿进字节腾讯阿里这些大厂的android岗面试该做哪些准备?(代码片段)

...某某技术。建议看课程后多看源码,常用开源框架源码、Handler、Binder源码、性能优化、事件分发机制……3.常问的技术点大概有:java进阶(jvm、并发)、kotlin基本原理、常用数据结构原理(hashmap.currenthashmap、数组链表lru)、常用设计模... 查看详情

你离进大厂,只差这些面试题!

前阵子,有个搞后端的小伙伴来问我,自己的学历有些欠缺,但还是想进大厂,有没有什么方法可以试试?还是说一点希望都没有了?的确,随着国内程序员群体的不断增大,再加上不少大厂的扩... 查看详情

android大牛近期大厂面试详解(附解答)(代码片段)

...些总结,Android面试中常见的面试题。1,Android的Handler运行机制要解释Handler的运行机制就要讲几个对象:Message、Handler、MessageQueue、Looper。Handler获取当前线程中的looper对象,looper用来 查看详情

android全网最详细的android面试题合集,23求职速领

求职热门季到来,最近看到很多对于Android面试相关内容的分享,但基本上都比较片面,面试题的整理不够经典和完整。因此,我这边整理了一套非常系统的Android技术面试题,所有类目条理清晰,知识点层... 查看详情

腾讯面试官精选的27道handler面试题(内含参考答案)(代码片段)

前言对于handler,你会想到什么呢?面试必问?项目常用?体系庞大?既然它如此重要,不知对面的你了解它多深呢?今天就和大家一起打破砂锅问到底,看看Handler这口砂锅的底到底在哪里。二十七... 查看详情

分析了上百份大厂面经,我总结了这些最最最常问的面试题,刷完直接进大厂!

...我非常推荐的一种学习方式:带着问题学习或者准备面试。另外,准备面试的小伙伴,一定要根据自身情况制定好复习计划!并且,你最好还要时不时自测一下,对着一些面试常见的问题进行自问。为了方... 查看详情