5年Android 开发者的社招面经总结(值得你记录收藏)

结论:直接提供实用信息。
Java:
创建线程:线程继承、可执行文件、可调用文件(FutureTask)和线程池。

Runnable/Callable:解决单继承,Callable有返回值和异常。

线程池:核心数、最大数、生存时间、队列。
执行:提交→主执行→队列→新线程→拒绝策略。

引用类型:强引用(普通)、软引用(缓存)、弱引用(WeakHashMap key)、虚拟引用(堆外内存)。

HashMap:1 .8 之前是数组+链表,链表>=8 为红黑树。
扩展:初始1 6 ,负载系数0.7 5
ArrayList 与 LinkedList:
ArrayList:数组,随机访问快,添加删除慢。

LinkedList:链表,添加和删除快,随机访问慢。

JVM内存:线程私有(PC、栈、本地栈)、共享(堆、方法区)、直接内存(NIO)。
String 与 StringBuilder 与 StringBuffer:
字符串:不可变、线程安全、速度慢。

StringBuilder:可变、非线程安全、快速。

StringBuffer:可变、同步、慢。
HTTP 与 HTTPS:
HTTP:纯文本,端口 8 0。

HTTPS:加密,端口 4 4 3 ,需要证书。

反射:动态加载、访问私有函数、框架。
缺点:性能、包装、安全。

深拷贝与浅拷贝:
浅复制:复制引用。

深拷贝:递归拷贝。

安卓:
数据存储:
SharedPreferences:键值对。

文件:内部/外部存储。

SQLite:数据库。

ContentProvider:多应用程序。

网络存储:API请求。

动画:
显示动画:显示动画。

属性动画:ValueAnimator/ObjectAnimator。

Drawable动画:图像动画。
Activity 生命周期:onCreate→onStart→onResume→onPause→onStop→onDestroy。

异常:屏幕旋转触发onSaveInstanceState→onRestoreInstanceState。

经理原则:
Looper:消息循环。

MessageQueue:消息队列。
处理程序管理器:发送/处理消息。

AsyncTask缺陷:生命周期绑定、串行执行。

改进:RxJava/协程、WeakReference。

缓存: 内存:LruCache。
磁盘:DiskLruCache。

第三方:Glide/Picasso。

MOO解决方案:
原因:图片太大,缓存和静态采集。

方法:压缩图像、WeakReference、免费资源。
工具:LeakCanary/AndroidProfiler。

Android与JS交互:
WebView:@JavascriptInterface。

JS调用Android:addJavascriptInterface/shouldOverrideUrlLoading。

Android调用JS:loadUrl/evaluateJavascript。

MVP模型:
模型:数据逻辑。

视图:用户界面。

主持人:协调。

优点:解耦、测试。

多渠道包装:
方法:产品风味。
实现:ManifestPlaceholder/Walle。

说实话:面试的时候背下来就可以了,别出错了。

线程池的四种创建方式及区别

上周我看了一下线程池源代码。

newFixedThreadPool:

作为参数传递的核心线程数。

非核心线程的数量也作为参数传递,与核心线程的数量相同。

队列没有限制。

达到核心线程数后,新任务将排队。

无核线程在不使用时不会被回收。

newSingleThreadPool:

只有一个核心线程。

非核心线程数也是1
队列没有限制。

保证任务的单线程顺序执行。

如果核心线程异常,则创建新的替换线程。

newScheduledThreadPool:

支持计划任务和周期性任务。

核心线程数作为参数传递。

非核心线程数为 MAX_VALUE。

队列是DelayedWorkQueue,是有序的。

执行逻辑是先核心线程,再队列,最后非核心线程。

默认的拒绝策略是AbortPolicy,这会抛出异常。

您可以使用CallerRunsPolicy在主线程上运行。

您还可以自定义拒绝策略。

我不确定这部分。

算了。

线程池创建的四种方法是什么

哈,你的总结很完整了。
它实际上解释了Java中的所有四个线程组。
不过,上次在项目中使用线程池的时候,遇到了一些坑,想跟大家分享一下我的经验。

例如:newFixedThreadPool,你是对的,它使用无限的队列。
如果你不小心提交了太多的任务,队列就会爆炸,然后新的任务就会卡住,系统资源就会耗尽。
我记得2 02 2 年底我在上海的一个金融项目中就遇到了这个问题,当时大量订单突然淹没系统,后台任务无法处理,整个服务卡住了。
然后我们转而手动创建ThreadPoolExecutor,使用ArrayBlockingQueue绑定队列,设置核心线程数和最大线程数,问题就好多了。

newCachedThreadPool 需要更多关注。
空闲6 0秒后回收线程听起来不错,但在实际使用中,如果任务提交太快,线程创建速度可能跟不上,直接占用系统资源。
我在杭州的另一个项目中尝试过。
在处理短连接请求时,我注意到 CPU 和内存出现峰值。
然后改成直接使用ThreadPoolExecutor,核心线程数设置为CPU核数,最大线程数设置为CPU核数2 ,任务队列使用LinkedBlockingQueue,然后就稳定了。

我很习惯使用 newSingleThreadExecutor。
确保顺序实施尤为重要。
例如,当写入日志文件时,您当然不希望它被中断。
在深圳开发一个物流系统时,我用它来处理订单状态的顺序更新,以确保数据的一致性。
但请注意,如果这个单个线程挂起并且没有自动重试机制,则可能需要额外的逻辑来重试。

我也经常使用newScheduledThreadPool,特别是对于计划任务。
例如,如果我们的系统每天早上都会同步数据,那么就应该使用calendarAtFixedRate。
但要注意,如果任务执行时间太长,超过循环时间,就会并行执行下一个任务,这可能会出现问题。
我在北京的一个电商项目中就遇到了这种情况。
最好改用scheduleWithFixedDelay。

说实话,目前我们团队强烈建议您直接使用ThreadPoolExecutor.new()并手动设置参数。
Executors的工厂方法虽然方便,但是默认配置过于简单,很容易出现问题。
尤其是newFixedThreadPool的无限队列和newCachedThreadPool的默认参数,被很多新开发者坑过。
现在,当我们培训新人时,第一件事就是教他们线程池创建、核心线程数、最大线程数、队列类型和拒绝策略。
这些事情他们必须自己计算。

无论如何,这取决于你,但最好提前了解这些陷阱。