被字节跳动、小米、美团面试官问的AndroidFramework难倒了? 这里有23道面试真题,助力成为offer收割机!

本文列举了字节跳动、小米、美团等公司面试中常问的Android Framework相关问题,涵盖了Binder机制、Handler通信、线程间通信等多个方面。通过这些问题,帮助读者深入理解Android系统内部机制,提升面试技能。
摘要由CSDN通过智能技术生成

目录

1.Android中多进程通信的方式有哪些?
a.进程通信你用过哪些?原理是什么?(字节跳动、小米)
2.描述下Binder机制原理?(东方头条)
3.Binder线程池的工作过程是什么样?(东方头条)
4.Handler怎么进行线程通信,原理是什么?(东方头条)
5.Handler如果没有消息处理是阻塞的还是非阻塞的?(字节跳动、小米)
6.handler.post(Runnable) runnable是如何执行的?(字节跳动、小米)
7.handler的Callback和handlemessage都存在,但callback返回true handleMessage还会执行么?(字节跳动、小米)
8.Handler的sendMessage和postDelay的区别?(字节跳动)
9.IdleHandler是什么?怎么使用,能解决什么问题?
10.为什么Looper.loop不阻塞主线程?
a.Looper无限循环为啥没有ANR(B站)
11.Looper如何在子线程中创建?(字节跳动、小米)
12.Looper、handler、线程间的关系。例如一个线程可以有几个Looper可以对应几个Handler?(字节跳动、小米)
13.如何更新UI,为什么子线程不能更新UI?(美团)
14.ThreadLocal的原理,以及在Looper是如何应用的?(字节跳动、小米)
15.Android 有哪些存储数据的方式?
16.SharedPreference原理,commit与apply的区别是什么?使用时需要有哪些注意?
17.如何判断一个 APP 在前台还是后台?
18.如何做应用保活?
19.一张图片100x100在内存中的大小?(字节跳动)
20.Intent的原理,作用,可以传递哪些类型的参数?
21.如果需要在Activity间传递大量的数据怎么办?
22.打开多个页面,如何实现一键退出?
23.LiveData的生命周期如何监听的?(B站)

参考解析

1.Android 线程间通信有哪几种方式

跨进程通信要求把方法调用及其数据分解至操作系统可以识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。

然后,返回值将沿相反方向传输回来。

Android 为我们提供了以下几种进程通信机制(供开发者使用的进程通信 API)

  • 文件
  • AIDL (基于 Binder)
  • Messenger (基于 Binder)
  • ContentProvider (基于 Binder)
  • Socket

在上述通信机制的基础上,我们只需集中精力定义和实现 RPC 编程接口即可。

2.描述下Binder机制原理?(东方头条)

Linux系统将一个进程分为用户空间和内核空间。对于进程之间来说,用户空间的数据不可共享,内核空间的数据可共享,为了保证安全性和独立性,一个进程不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的,这就需要跨进程之间的数据通信方式。普通的跨进程通信方式一般需要2次内存拷贝,如下图所示:

一次完整的 Binder IPC 通信过程通常是这样:

  • 首先 Binder 驱动在内核空间创建一个数据接收缓存区。
  • 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系。
  • 发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

3.Binder线程池的工作过程是什么样?(东方头条)

Binder设计架构中,只有第一个Binder主线程(也就是Binder_1线程)是由应用程序主动创建,Binder线程池的普通线程都是由Binder驱动根据IPC通信需求创建,Binder线程的创建流程图:

每次由Zygote fork出新进程的过程中,伴随着创建binder线程池,调用spawnPooledThread来创建binder主线程。当线程执行binder_thread_read的过程中,发现当前没有空闲线程,没有请求创建线程,且没有达到上限,则创建新的binder线程。

Binder的transaction有3种类型:

call: 发起进程的线程不一定是在Binder线程, 大多数情況下,接收者只指向进程,并不确定会有哪个线程来处理,所以不指定线程;

reply: 发起者一定是binder线程,并且接收者线程便是上次call时的发起线程(该线程不一定是binder线程,可以是任意线程)。

async: 与call类型差不多,唯一不同的是async是oneway方式不需要回复,发起进程的线程不一定是在Binder线程, 接收者只指向进程,并不确定会有哪个线程来处理,所以不指定线程。

Binder系统中可分为3类binder线程:

Binder主线程:进程创建过程会调用startThreadPool()过程中再进入spawnPooledThread(true),来创建Binder主线程。编号从1开始,也就是意味着binder主线程名为binder_1,并且主线程是不会退出的。
Binder普通线程:是由Binder Driver来根据是否有空闲的binder线程来决定是否创建binder线程,回调spawnPooledThread(false) ,isMain=false,该线程名格式为binder_x。
Binder其他线程:其他线程是指并没有调用spawnPooledThread方法,而是直接调用IPC.joinThreadPool(),将当前线程直接加入binder线程队列。例如: mediaserver和servicemanager的主线程都是binder线程,但system_server的主线程并非binder线程。

4.Handler怎么进行线程通信,原理是什么?(东方头条)

Handler的消息传递机制涉及到四个部分:

  1. Message:线程间传递的对象。
  2. MessageQueue: 消息队列,用来存放Handler发布的Message.
  3. Handler:负责将Message插入到MessageQueue中以及对MessageQueue中的Message进行处理。
  4. Looper:负责从MessageQueue中取出Message,并交给Handler.

其中:

  • Looper存储在ThreadLocal中,Looper在创建时会同时创建MessageQueue,作为其成员对象.因此Looper和MessageQueue是属于创建者线程的,各线程之间的Looper和MessageQueue相互独立。
  • Handler在创建时会从当前线程的ThreadLocal中取得Looper.
  • 发送消息时,在发送线程中调用接收线程中的Handler的sendMessage方法,过程中,Handler会将自身赋予到Message的target中,并将Message插入到Handler对应的MessageQueue中。
  • 而接收线程中的Looper在循环过程中会取出这个Message,通过Message.target取出接收线程中的Handler,并将消息交Handler对象处理。由此实现了跨线程通信。
  • 要注意的是:线程与Looper和MessageQueue是一对一的关系,即一个线程只维护一个Looper和一个MessageQueue;而线程与Handler的关系是一对多,即一个线程可以有很多Handler,一个Handler只对应一个线程,这也是为什么Handler在发送消息时,为什么要将自身赋给Message.target的原因。

5.Handler如果没有消息处理是阻塞的还是非阻塞的?(字节跳动、小米)

// 下面这个方法有可能会产生阻塞,主要是通过native层的epoll机制,监听文件描述符的写入事件实现的。
// -1:一直阻塞;0:不阻塞;n>0:最多阻塞n秒
nativePollOnce(ptr, nextPollTimeoutMillis);

6.handler.post(Runnable) runnable是如何执行的?(字节跳动、小米)

关于这个handler.post(Runnable r)这个方法,用过很多次,

看下源码,它到底是怎样处理的。

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

看下getPostMessage®这个方法的源码,

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

给message设置了回调,
然后,looper进行消息循环,进行消息的分发,

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

如果回调不为空,handleCallback(msg) 找个方法就会执行,

private static void handleCallback(Message message) {
        message.callback.run();
    }<span style="font-family: Arial, Helvetica, sans-serif;">调用了run方法。</span>

7.handler的Callback和handlemessage都存在,但callback返回true handleMessage还会执行么?(字节跳动、小米)

sendMessageAtTime 使用的uptimeMillis依赖的是系统以开机时间的绝对时间;

而sendMessageDelayed使用的delayMillis依赖的是系统以开机时间的相对时间。

啥意思呢:就是说delayMillis使用的时候要加一个SystemClock.uptimeMillis(),

也就是sendMessageAtTime等于snedMessageDelayed的情况为uptimeMillis == delayMillis - SystemClock.uptimeMillis()[这是一个相对时间].

SystemClock.uptimeMillis()是获取系统从开机启动到现在的时间,期间不包括休眠的时间,这里获得到的时间是一个相对的时间,而不是通过获取当前的时间(绝对时间)。

而之所以使用这种方式来计算时间,而不是获得当前currenttime来计算,在于handler会受到阻塞,挂起状态,睡眠等,这些时候是不应该执行的;如果使用绝对时间的话,就会抢占资源来执行当前handler的内容,显然这是不应该出现的情况,所以要避免。

查看源码如下&#x

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值