必看!2025 年 Android 面试基础高频题全解析,助你轻松拿 offer

引言:部分开发者认为面试题难度过高,面试问题总是答不出来或者答的没条例,其实根源在于基础薄弱或缺乏深度研究。本文专门收集了网络上大部分面试题并进行整理,分基础篇与进阶篇,具备以下特点:

  • 覆盖面广:涵盖Android核心技术及扩展知识点。

  • 答案可靠:每道题目均经作者亲自验证并附解答。

  • 互动性强:支持通过评论区或私信交流疑问,倡导共同探讨进步。

1、ActivityFragment生命周期有哪些?

2、LaunchMode应用场景


standard,创建一个新的Activity。

singleTop,栈顶不是该类型的Activity,创建一个新的Activity。否则,onNewIntent。 singleTask,回退栈中没有该类型的Activity,创建Activity,否则,onNewIntent+ClearTop。注意:
设置了"singleTask"启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity等于它的属
性值taskAffinity的Task存在;如果存在这样的Task,它就会在这个Task中启动,否则就会在新的任务栈
中启动。因此, 如果我们想要设置了"singleTask"启动模式的Activity在新的任务中启动,就要为它设置
一个独立的taskAffinity属性值。
如果设置了"singleTask"启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已
经存在相应的Activity实例, 如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终
这个Activity 实例会位于任务的Stack顶端中。
在一个任务栈中只有一个”singleTask”启动模式的Activity存在。他的上面可以有其他的Activity。这点与singleInstance是有区别的。
singleInstance,回退栈中,只有这一个Activity,没有其他Activity。
singleTop适合接收通知启动的内容显示页面。
例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。
singleTask适合作为程序入口点。
例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走
onNewIntent,并且会清空主界面上面的其他页面。
singleInstance应用场景:
闹铃的响铃界面。 你以前设置了一个闹铃:上午6点。在上午5点58分,你启动了闹铃设置界面,并按Home 键回桌面;在上午5点59分时,你在微信和朋友聊天;在6点时,闹铃响了,并且弹出了一个对话框形式的 Activity(名为 AlarmAlertActivity) 提示你到6点了(这个 Activity 就是以SingleInstance 加载模式打开的),你按返回键,回到的是微信的聊天界面,这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了。如果是以 SingleTask 打开 AlarmAlertActivity,那么当闹铃响了的时候,按返回键应该进入闹铃设置界面

3、onSaveInstanceState()  onRestoreIntanceState()

Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。

在activity被杀掉之前调用保存每个实例的状态,以保证该状态可以在onCreate(Bundle)或者 onRestoreInstanceState(Bundle) (传入的Bundle参数是由onSaveInstanceState封装好的)中恢复。这个方法在一个activity被杀死前调用,当该activity在将来某个时刻回来时可以恢复其先前状态。

例如,如果activity B启用后位于activity A的前端,在某个时刻activity A因为系统回收资源的问题要被杀掉,A通过onSaveInstanceState将有机会保存其用户界面状态,使得将来用户返回到activity A时能通过onCreate(Bundle)或者onRestoreInstanceState(Bundle)恢复界面的状态

4android中进程的优先级?

前台进程:即与用户正在交互的Activity或者Activity用到的Service等,如果系统内存不足时前台进程是最晚被杀死的

可见进程:可以是处于暂停状态(onPause)的Activity或者绑定在其上的Service,即被用户可见,但由于失了焦点而不能与用户交互

服务进程:其中运行着使用startService方法启动的Service,虽然不被用户可见,但是却是用户关心的,例如用户正在非音乐界面听的音乐或者正在非下载页面下载的文件等;当系统要空间运行,前两者进程才会被终止

后台进程:其中运行着执行onStop方法而停止的程序,但是却不是用户当前关心的,例如后台挂着的QQ,这时的进程系统一旦没了有内存就首先被杀死

空进程:不包含任何应用程序的进程,这样的进程系统是一般不会让他存在的

5Bunder传递对象为什么需要序列化?SerialzableParcelable的区别?

因为bundle传递数据时只支持基本数据类型,所以在传递对象时需要序列化转换成可存储或可传输的本质状态(字节流)。序列化后的对象可以在网络、IPC(比如启动另一个进程的Activity、Service和 Reciver)之间进行传输,也可以存储到本地。

  SerializableJava自带):

Serializable 是序列化的意思,表示将一个对象转换成存储或可传输的状态。序列化后的对象可以在网络上进传输,也可以存储到本地。

  Parcelableandroid专用):

除了Serializable之外,使用Parcelable也可以实现相同的效果,不过不同于将对象进行序列化, Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这也就实现传递对象的功能了。

区别总结如下图所示:

 

6、动画

补间动画。通过指定View的初末状态和变化方式,对View的内容完成一系列的图形变换来实现动画效果。 Alpha, Scale ,Translate, Rotate。

帧动画。AnimationDrawable控制animation-list.xml布局

属性动画3.0引入,属性动画核心思想是对值的变化。

Property Animation 动画有两个步聚:

  1. 计算属性值
  2. 为目标对象的属性设置属性值,即应用和刷新动画

计算属性分为3个过程:过程一:

计算已完成动画分数 elapsed fraction。为了执行一个动画,你需要创建一个ValueAnimator,并且指定目标对象属性的开始、结束和持续时间。在调用 start 后的整个动画过程中,ValueAnimator 会根据已经完成的动画时间计算得到一个0 到 1 之间的分数,代表该动画的已完成动画百分比。0表示 0%,1表示 100%。

过程二:

计算插值(动画变化率)interpolated fraction 。当 ValueAnimator计算完已完成的动画分数后,它会调用当前设置的TimeInterpolator,去计算得到一个interpolated(插值)分数,在计算过程中,已完成动画百分比会被加入到新的插值计算中。

过程三:

计算属性值当插值分数计算完成后,ValueAnimator会根据插值分数调用合适的 TypeEvaluator去计算运动中的属性值。

以上分析引入了两个概念:已完成动画分数(elapsed fraction)、插值分数( interpolated fraction )。

原理及特点:

属性动画:

插值器:作用是根据时间流逝的百分比来计算属性变化的百分比

估值器:在1的基础上由这个东西来计算出属性到底变化了多少数值的类

其实就是利用插值器和估值器,来计出各个时刻View的属性,然后通过改变View的属性来实现View的动画效果。

View动画:只是影像变化,view的实际位置还在原来地方。

帧动画:是在xml中定义好一系列图片之后,使用AnimatonDrawable来播放的动画。

它们的区别:

属性动画才是真正的实现了 view 的移动,补间动画对view 的移动更像是在不同地方绘制了一个影子,实际对象还是处于原来的地方。

当动画的 repeatCount 设置为无限循环时,如果在Activity退出时没有及时将动画停止,属性动画会导致Activity无法释放而导致内存泄漏,而补间动画却没问题。

xml 文件实现的补间动画,复用率极高。在 Activity切换,窗口弹出时等情景中有着很好的效果。使用帧动画时需要注意,不要使用过多特别大的图,容导致内存不足。

为什么属性动画移动后仍可点击?

播放补间动画的时候,我们所看到的变化,都只是临时的。而属性动画呢,它所改变的东西,却会更新到这个View所对应的矩阵中,所以当ViewGroup分派事件的时候,会正确的将当前触摸坐标,转换成矩阵变化后的坐标,这就是为什么播放补间动画不会改变触摸区域的原因了。

7、Context相关

  • Activity和Service以及Application的Context是不一样的,Activity继承自 ContextThemeWraper.其他的继承自ContextWrapper。
  • 每一个Activity和Service以及Application的Context是一个新的ContextImpl对象。
  • getApplication()用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。那也许在绝大多数情况下我们都是在Activity或者Servic中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助 getApplicationContext()方法,getApplicationContext()比getApplication()方法的作用域会更广一些,任何一个Context的实例,只要调用getApplicationContext()方法都可以拿到我们的 Application对象。
  • 创建对话框时不可以用Application的context,只能用Activity的context。
  • Context的数量等于Activity的个数 + Service的个数 +1,这个1为Application。

8ThreadAsyncTaskIntentService的使用场景与特点。

  • Thread线程,独立运行与于 Activity 的,当Activity 被 finish 后,如果没有主动停止 Thread或者 run 方法没有执行完,其会一直执行下去。
  • AsyncTask 封装了两个线程池和一个Handler(SerialExecutor用于排队, THREAD_POOL_EXECUTOR为真正的执行任务,Handler将工作线程切换到主线程),其必须在 UI线程中创建,execute 方法必须在 UI线程中执行,一个任务实例只允许执行一次,执行多次抛出异常,用于网络请求或者简单数据处理。
  • IntentService:处理异步请求,实现多线程,在onHandleIntent中处理耗时操作,多个耗时任务会依次执行,执行完毕自动结束。

9、什么是ANR 如何避免它?

在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应

用程序无响应(ANR:Application NotResponding)对话框。用户可以选择让程序继续运行,但是,他们在使用你的

应用程序时,并不希望每次都要处理这个对话框。因此,在程序里对响应性能的设计很重要这样,这样系统就不会显示ANR给用户。

不同的组件发生ANR的时间不一样,Activity是5秒,BroadCastReceiver是10秒,Service是20秒(均为前台)。

如果开发机器上出现问题,我们可以通过查看/data/anr/traces.txt即可,最新的ANR信息在最开始部分。

  • IO操作(从4.0之后网络IO不允许在主线程中)阻塞。
  • 主线程存在耗时操作。
  • 主线程中错误操作,比如Thread.wait或者Thread.sleep等 Android系统会监控程序的响应状况,一旦出现下面两种情况,则弹出ANR对话框
  • 应用在5秒内未响应用户的输入事件(如按键或者触摸)
  • BroadcastReceiver未在10秒内完成相关的处理
  • Service在特定的时间内无法处理完成 20秒

修正:

1、使用AsyncTask处理耗时IO操作。

2、使用Thread或者HandlerThread时,调用 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同。

3、使用Handler处理工作线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程。   

4、Activity的onCreate和onResume回调中尽量避免耗时的代码,BroadcastReceiver中onReceive代码也要尽量减少耗时,建议使用IntentService处理。

解决方案:

将所有耗时操作,比如访问网络,Socket通信,查询大量SQL 语句,复杂逻辑计算等都放在子线程中去,然

后通过handler.sendMessage、runonUIThread、AsyncTask、RxJava等方式更新UI。无论如何都要确保用户界面的流畅

度。如果耗时操作需要让用户等待,那么可以在界面上显示度条。

10activitystartActivitycontextstartActivity区别?

(1)、从Activity中启动新的Activity时可以直接mContext.startActivity(intent)就好

(2)、如果从其他Context中启动Activity则必须给intent设置Flag:

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ; 
mContext.startActivity(intent);

11、怎么在Service中创建Dialog对话框?

在我们取得Dialog对象后,需给它设置类型,即:

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)

在Manifest中加上权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINOW" />

12Asset目录与res目录的区别?

assets:不会在 R 文件中生成相应标记,存放到这里的资源在打包时会打包到程序安装包中。(通过AssetManager 类访问这些文件)

res:会在 R 文件中生成 id 标记,资源在打包时如果使用到则打包到安装包中,未用到不会打入安装包中。

res/anim:存放动画资源。

res/raw:和 asset 下文件一样,打包时直接打入程序安装包中(会映射到 R 文件中)。

13、程序A能否接收到程序B的广播?

能,使用全局的BroadCastRecevier能进行跨进程通信,但是注意它只能被动接收广播。此外,

LocalBroadCastRecevier只限于本进程的广播间通信。

14Android为什么引入Parcelable

可以肯定的是,两者都是支持序列化和反序列化的操作。

两者最大的区别在于 存储媒介的不同,Serializable 使用 I/O 读写存储在硬盘上,而 Parcelable 是直接在内存中读写。很明显,内存的读写速度通常大于 IO 读写,所以在 Android 中传递数据优先选择 Parcelable。

Serializable 会使用反射,序列化和反序列化过程需要大量 I/O 操作, Parcelable 自已实现封送和解封(marshalled  &unmarshalled)操作不需要用反射,数据也存放在  Native  内存中,效率要快很多。

15Bitmap 使用时候注意什么?

1、要选择合适的图片规格(bitmap类型):

2、降低采样率。BitmapFactory.Options 参数inSampleSize的使用,先把 options.inJustDecodeBounds设为true,只是去读取图片的大小,在拿到图片的大小之后和要显示的大小做比较通过calculateInSampleSize()函数计算inSampleSize的具体值,得到值之后。 options.inJustDecodeBounds设为false读图片资源。

3、复用内存。即,通过软引用(内存不够的时候才会回收掉),复用内存块,不需要再重新给这个 bitmap申请一块新的内存,避免了一次内存的分配和回收,从而改善了运行效率。

4、使用recycle()方法及时回收内存。 5、压缩图片。

16Oom 是否可以try catch 

只有在一种情况下,这样做是可行的:

在try语句中声明了很大的对象,导致OOM,并且可以确认OOM是由try语句中的对象声明导致的,那么在catch语句中,可以释放掉这些对象,解决OOM的问题,继续执行剩余语句。

但是这通常不是合适的做法。

Java中管理内存除了显式地catch OOM之外还有更多有效的方法:比如SoftReference,

WeakReference, 硬盘缓存等。

在JVM用光内存之前,会多次触发GC,这些GC会降低程序运行的效率。

如果OOM的原因不是try语句中的对象(比如内存泄漏),那么在catch语句中会继续抛出OOM。

17、强引用置为null,会不会被回收?

不会立即释放对象占用的内存。 如果对象的引用被置为null,只是断开了当前线程栈帧中对该对象的引用关系,而 垃圾收集器是运行在后台的线程,只有当用户线程运行到安全点(safe point)或者安全区域才会扫描对象引用关系,扫描到对象没有被引用则会标记对象,这时候仍然不会立即释放该对象内存,因为有些对象是可恢复的(在 finalize方法中恢复引用 )。只有确定了对象无法恢复引用的时候才会清除对象内存。

18Bundle传递数据为什么需要序列化?

序列化,表示将一个对象转换成可存储或可传输的状态。序列化的原因基本三种情况:

  • 永久性保存对象,保存对象的字节序列到本地文件中;
  • 对象在网络中传递;
  • 对象在IPC间传递。

19、Handler机制

Android消息循环流程图如下所示:

主要涉及的角色如下所示:

  1. Message:消息。
  2. MessageQueue:消息队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message。读取会自动删除消息,单链表维护,插入和删除上有优势。在其next()方法中会无限循环,不断判断是否有消息,有就返回这条消息并移除。
  3. Looper:消息循环器,负责关联线程以及消息的分发,在该线程下从 MessageQueue获取 Message,分发给Handler,Looper创建的时候会创建一个MessageQueue,调用loop()方法的时候消息循环开始,其中会不断调用messageQueue的next()方法,当有消息就处理,否则阻塞在messageQueue的next()方法中。当Looper的quit()被调用的时候会调用messageQueue的quit(),此时next()会返回null,然后loop()方法也就跟着退出。
  4. Handler:消息处理器,负责发送并处理消息,面向开发者,提供  API,并隐藏背后实现的细节。

整个消息的循环流程还是比较清晰的,具体说来:

  1. Handler通过sendMessage()发送消息Message到消息队列MessageQueue。
  2. Looper通过loop()不断提取触发条件的Message,并将Message交给对应的target handler来处理。
  3. target  handler调用自身的handleMessage()方法来处理Message。

事实上,在整个消息循环的流程中,并不只有Java层参与,很多重要的工作都是在C++层来完成的。我们来看下这些类的调用关系。

注:虚线表示关联关系,实线表示调用关系。

在这些类中MessageQueue是Java层与C++层维系的桥梁,MessageQueue与Looper相关功能都通过 MessageQueue的Native方法来完成,而其他虚线连接的类只有关联关系,并没有直接调用的关系,它们发生关联的桥梁是MessageQueue。

总结

  •  handlere发送的消息由 MessageQueue 存储管理,并由 Looper 负责回调消息到handleMessage()。
  • 线程转换由Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。

Handler 引起的内存泄露原因以及最佳解决方案

Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。

解决:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并在Acitivity的onDestroy()中调用handler.removeCallbacksAndMessages(null)及时移除所有消息。

为什么我们能在主线程直接使用 Handler,而不需要创建 Looper 

通常我们认为 ActivityThread 就是主线程。事实上它并不是一个线程,而是主线程操作的管理者。在 ActivityThread.main() 方法中调用了 Looper.prepareMainLooper() 方法创建了 主线程的 Looper ,并且调用了 loop() 方法,所以我们就可以直接使用 Handler 了。

因此我们可以利用 Callback 这个拦截机制来拦截 Handler 的消息。如大部分插件化框架中Hook ActivityThread.mH 的处理。

主线程的 Looper 不允许退出

主线程不允许退出,退出就意味 APP 要挂。

Handler 里藏着的 Callback 能干什么?

Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截,那么就意味着一个消息可以同时被 Callback 以及 Handler 处理。

创建 Message 实例的最佳方式

为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候尽量复用 Message ,减少内存消耗:

 
  • Message 的静态方法 Message.obtain();
  • Handler 的公有方法 handler.obtainMessage()。

子线程里弹 Toast 的正确姿势

本质上是因为 Toast 的实现依赖于 Handler,按子线程使用 Handler 的要求修改即可,同理的还有

Dialog。

妙用 Looper 机制

  • Runnable post 到主线程执行;
  • Looper 判断当前线程是否是主线程。

主线程的死循环一直运行是不是特别消耗CPU资源呢?

并不是,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的 epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质是同步I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

handler postDelay这个延迟是怎么实现的?

handler.postDelay并不是先等待一定的时间再放入到MessageQueue中,而是直接进入  MessageQueue,以MessageQueue的时间顺序排列和唤醒的方式结合实现的。

结束语:

在竞争白热化的就业浪潮中,每一次跳槽机会都如同千军万马过独木桥!如果你正怀揣着职业转型的梦想,想要在激烈的求职战场脱颖而出,那精心筹备面试,就是你打开理想 offer 大门的金钥匙!我熬夜数月,精心打磨了这份超全面试资料库,不仅有 Android 从入门到大神的全阶段学习秘籍,还有无数资深面试官私藏的通关技巧视频,助你逆风翻盘,拿下心动岗位!

面试资料:

Android教学资料  (600G+)资料

【!!以上全部资料扫描下方二维码领取!!】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值