2026-10-21 19:24:05
影石一面秋招Android面试主要围绕Android基础、Java知识、网络协议、多线程编程等展开,面试官会深入追问直至候选人不会为止,整体难度较高。 以下是具体面试问题及解析:
Android基础Handler是Android中用于在不同线程间传递消息和处理消息的机制。它通常与Looper和MessageQueue配合使用,主线程默认有一个Looper和MessageQueue,通过Handler可以将消息发送到MessageQueue中,Looper会不断从MessageQueue中取出消息并分发给对应的Handler进行处理,从而实现线程间的通信。
代码为loga handler(logb) logc且都在主线程。正常情况下,若不涉及耗时操作,代码按书写顺序执行,即先执行loga,然后通过Handler发送消息处理logb(由于在主线程,消息会被放入主线程消息队列,在当前代码执行完后Looper才会处理),最后执行logc。
若handler(logb)耗时,那么loga和logc会先执行完,logb会在Handler对应的消息被Looper处理时执行。
若不耗时,执行顺序基本还是按代码顺序,不过logb的处理是在当前代码执行完后,由Looper从消息队列取出消息并调用Handler处理。
事件分发机制主要涉及Activity、ViewGroup和View对触摸事件的传递过程。当一个触摸事件发生时,首先由Activity的dispatchTouchEvent方法进行分发,然后传递给根ViewGroup,ViewGroup会按照一定的规则(如是否拦截事件)将事件传递给子View。
消费机制是指当某个View想要处理这个事件时,会调用onTouchEvent方法并返回true表示消费了该事件,事件将不再继续传递;若返回false,则事件会继续向上传递给父View。
如果事件没有被消费,会按照与分发相反的方向向上传递,即从当前View传递给父ViewGroup,再依次向上,直到Activity。如果最终Activity也没有消费该事件,则这个事件会被丢弃。
Service基础能力:Service可以在后台长时间运行,执行一些不需要用户交互的任务,如播放音乐、下载文件等。它可以通过启动服务(startService)和绑定服务(bindService)两种方式启动。
自定义Service问题:自己构造的Service可以根据具体需求进行设计,但与系统Service相比,劣势在于可能缺乏系统级的优化和权限管理。系统的Service通常经过严格测试和优化,具有更高的稳定性和安全性。
下载需求Service设计:对于下载需求的Service,可以采用前台服务的方式,以保证在后台运行时不会被系统轻易回收。启动方式可以通过startForegroundService启动,并在Service中调用startForeground方法显示一个通知。
让别人调用Service的设计:可以通过AIDL(Android Interface Definition Language)或者Messenger的方式实现跨进程通信,让别人能够调用自己的Service。AIDL适合复杂的跨进程通信,而Messenger相对简单,适合简单的消息传递。
官方Service和自定义Service区别:官方Service通常是系统提供的一些基础服务,如媒体服务、位置服务等,具有系统级的权限和功能;自定义Service则是开发者根据自己的需求创建的,功能相对单一,权限也受到限制。
强引用:最常见的引用类型,只要强引用存在,垃圾回收器就不会回收被引用的对象。
软引用:用于描述一些还有用但并非必需的对象,在内存不足时,垃圾回收器会回收这些对象。
弱引用:比软引用更弱一些,无论内存是否充足,只要发生垃圾回收,被弱引用关联的对象就会被回收。
虚引用:也称为幽灵引用,是最弱的一种引用,无法通过虚引用来获取对象实例,主要用于跟踪对象被垃圾回收的活动。
GC回收条件:弱引用对象在垃圾回收时,只要发现只有弱引用指向该对象,就会被回收。即使内存充足,也会进行回收。
弱引用本身类型:弱引用本身是一个独立的引用类型,通过WeakReference类来实现。
Activity创建时持有对象未置空:如果一个Activity在创建时new了一个对象a,并且m持有a,在Activity销毁时没有将m置空,可能会导致内存泄漏。因为a仍然被m引用,而m可能又被其他长期存活的对象引用,使得a无法被垃圾回收。
相互持有引用未置空:如果两个对象相互持有引用,并且在不再需要时没有置空,会导致这两个对象都无法被垃圾回收,从而产生内存泄漏。
泛型是否产生相应类型:Java的泛型在编译时会进行类型擦除,不会真正产生相应的类型。在编译后的字节码中,泛型信息会被擦除,只保留原始类型。
其他语言情况:像C++等语言,其模板是真正在编译时生成相应的类型代码,与Java的泛型实现方式不同。
原因:如果只重写hash方法而不重写equals方法,可能会导致在使用哈希表(如HashMap)时出现不一致的情况。因为哈希表的查找和插入操作依赖于hash方法和equals方法,如果两个对象equals返回true,但hash值不同,哈希表可能无法正确处理这两个对象。
异常:可能会导致哈希表无法正常工作,出现找不到已插入的对象或者插入重复对象等问题。
理解:HashMap是线程不安全的,因为在多线程环境下,当多个线程同时对HashMap进行插入、删除等操作时,可能会导致数据不一致、死循环等问题。
并发解决方案:如果不加锁,可以考虑使用ConcurrentHashMap,它是线程安全的哈希表实现,通过分段锁等技术来保证并发安全。
模型描述:消费者生产者模型是一种经典的多线程同步问题,生产者负责生产数据并放入共享缓冲区,消费者负责从共享缓冲区取出数据进行消费。
伪代码口述:可以使用wait和notify方法来实现消费者生产者模型的同步。生产者在缓冲区满时调用wait方法等待,消费者在缓冲区空时调用wait方法等待;当生产者生产了数据或者消费者消费了数据后,调用notify方法唤醒等待的线程。
多生产多消费情况:多生产多消费时,需要考虑锁的粒度和同步的准确性。可以在生产者和消费者操作共享缓冲区的关键代码处加锁,确保同一时间只有一个线程能够访问缓冲区。
提高效率方法:可以通过引入循环缓冲区、使用更高效的同步机制(如ReentrantLock和Condition)等方式来提高效率。
生产加锁位置:生产时应该在将数据放入缓冲区之前加锁,确保在放入数据的过程中不会被其他线程干扰。
唤醒消费者时的锁状态:唤醒消费者时,锁在生产者手上,唤醒后锁仍然在生产者手上,消费者需要等待生产者释放锁后才能拿到锁进行消费。
第一次握手:客户端发送一个带有SYN标志的数据包给服务器,表示客户端请求建立连接。
第二次握手:服务器收到客户端的SYN包后,发送一个带有SYN和ACK标志的数据包给客户端,表示服务器同意建立连接。
第三次握手:客户端收到服务器的SYN+ACK包后,发送一个带有ACK标志的数据包给服务器,表示连接建立成功。
确认和重传机制:发送方每发送一个数据包,都会等待接收方的确认信息。如果在一定时间内没有收到确认信息,发送方会重新发送该数据包。
排序和重组:TCP会对接收到的数据包进行排序和重组,确保数据的正确性和完整性。
流量控制和拥塞控制:通过滑动窗口机制进行流量控制,避免发送方发送数据过快导致接收方无法处理;通过拥塞控制算法(如慢开始、拥塞避免、快重传、快恢复等)来避免网络拥塞。
第一次握手:你说一个“喂”,相当于客户端发送SYN包,表示请求建立连接。
第二次握手:我说一个“喂”,相当于服务器发送SYN+ACK包,表示同意建立连接。
第三次握手:你说一个“听到了”,相当于客户端发送ACK包,表示连接建立成功。
如果中间少了一次握手,比如服务器没有收到客户端的SYN包,服务器就不会发送SYN+ACK包,客户端也无法收到确认信息,连接就无法建立成功。
加密通信:HTTPS通过SSL/TLS协议对通信内容进行加密,使得中间人无法窃取和篡改通信内容。
证书验证:服务器需要提供有效的证书,客户端会验证证书的真实性和合法性,确保与正确的服务器进行通信。
通过加密技术,将通信内容进行加密处理,使得中间人即使截获了数据,也无法解密获取其中的信息。
CA证书:证书由权威的证书颁发机构(CA)颁发,CA会对服务器的身份进行验证,确保证书的真实性和合法性。
公钥解密校验过程:服务器会将自己的公钥包含在证书中发送给客户端,客户端使用CA的公钥对证书进行解密和校验,验证证书的签名和有效期等信息。
客户端会预先内置一些权威CA的根证书,通过验证证书的签名链,确保证书是由可信的CA颁发的,从而确认证书的真实性。