本文乃找工作、招人必备之良品。后期不断完善中……
对于面试仍然充满畏惧?多次面试后信心被搓,被恶心的面试官打击?这里有一份宝典合集:包括有《1599页Android面试宝典》+《Android Framework 开发揭秘》+《Android项目合集》等等,一切你需要的面试资料,我们都有合集大礼包等着你,需要的朋友可以添加下面VX回复JJ即可领取,100%免费!!!
这是我既前两次做Android面试题合集之后重新整理和完善的一份新的面试题。
写在前面,之前面试题带入的都是咱们求职者的视角,这次我联络的是部分招聘人员,当然具体名字和公司不方便透露,总之,该部分面试是从招聘角度出发,当然作为求职者我觉得只会收获更加丰富。
如何招聘人,搜集了一些知识点。如何做好应聘准备,也收集了一些主要知识点,供你参考。
死锁是一种情形,多个线程被阻塞,他们中的一个或者全部都在等待某个资源被释放,由于线程被无限期的阻塞,因此程序不能正常运行,简单的说就是线程死锁时,第一个线程等待第二个线程释放资源,而同时第二个线程又在等待第一个线程释放资源;
死锁产生的四个必要条件:
互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某资源仅为一个进程所占有,此时若有其他请求该资源,则请求进程只能等待;
请求与保持条件:进程已经持有了至少一个资源,但又提出了新的资源请求,而该资源已经被其他进程所占有,此时请求会被阻塞,但对自己获得的资源又保持不放;
不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,只能由获得该资源的进程自己来释放(只能时主动释放);
循环等待条件:即若干进程形成首尾相接的循环等待资源的关系,即形成了一个进程等待环路,环路中每一个进程所占有的资源同时被另一个进程所申请,也就是前一个进程占有后一个进程所申请的资源
这四个条件是死锁产生必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁;
死锁的避免:避免死锁的基本思想是系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配资源后系统可能发生死锁,则不分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。
在Java中内存分为两种,一种是栈内存,另一种是堆内存
堆内存:用于存储Java中的对象和数组,当我们new一个对象或创建一个数组的时候,就会在堆内存中开辟一段空间给它,用于存放,堆内存的特点:先进先出,后今后出,②可以动态的分配内存的大小,生存期不必告诉编译器,但存取速度较慢;
栈内存:主要用来执行程序用,比如基本类型的变量和对象的引用变量,其特点:①先进后出,后进后出,②存取速度比堆快,仅次于寄存器,栈数据可以共享,但其在栈中的数据大小和生存期必须是确定的;
栈内存和堆内存都属于Java内存的一种,系统会自动去回收它,但对于堆内存开发人员一般会自动回收。
栈是一块和线程紧密相关的内存区域,每个线程都有自己的栈内存,用于存储本地变量、方法参数和栈调用一个线程中存储的变量,对于其他线程是不可见的,而堆是所有线程共享的一个公用内存区域,对象都在堆里创建,但为了提升效率,线程会从堆中拷贝一个缓存到自己的栈中,如果多个线程使用该变量,就可能引发问题,这是volatile修饰变量就可以发挥作用,他要求线程从主存中读取变量的值。
线程是CPU调度的最小单位(进程是CPU分配资源的最小单位),在Android中主线程是不能够做耗时的操作的,子线程是不能更新UI的,而线程间的通信方式有很多,比如广播、接口回掉等,在Android中主要使用handler,handler通过调用sendMessage方法,将保存好的消息发送到MessageQueue中,而Looper对象不断地调用loop方法,从MessageQueue中取出message,交给handler处理,从而完成线程间通信。
Thread类的join()方法主要作用是同步,它可以使线程之间的并行执行变为串行执行,join()方法把指定的线程加入到当前线程中,可以将两个交替执行的线程合并为顺序执行的线程,比如在线程B中调用了线程A的join()方法,则会等到A线程执行完,才会继续执行线程B,join方法还可以指定时长,表示等待该线程执行指定时长后再执行其他线程,如A.join(1000),表示等待A线程执行1000毫秒后,再执行其他线程,当有多个线程、要使他们案顺序执行时,可以使用join()方法在一个线程中启动另一个线程,另外一个线程在该线程执行完之后再继续执行。
AsyncTask是对Handler和线程池的封装,使用它可以更新用户界面,当然,这里的更新操作还是在主线程中完成的,但由于AsyncTask内部包含了一个Handler,所以可以发送消息给主线程,让他更新UI,另外AsyncTask内还包含了一个线程池,避免了不必要的创建和销毁线程的开销。
在单核的机器上,“多进程”并不是真正多个进程同时执行,而是通过CPU时间分片,操作系统快速在进程间切换而模拟出来的多进程,我们通常将这种情况称为并发,也就是多个进程的运行行为是“一并发生”的,但不是同时执行的,因为CPU核数的限制(CPU和通用寄存器只有有一套),严格来说,同一时刻只能存在一个进程的上下文。
但在多核CPU上,能真正实现多个进程并行执行,这种情况叫做并行,因为多个进程是真正“一并执行的”(具体多少个进程可以并行执行取决于CPU的核数),所以可知,并发是一个比并行更加宽泛的概念,在单核情况下,并发只是并发,而在多核情况下,并发就变为并行了。
同步:所谓同步是一个服务的完成需要依赖其他服务,只有等待被依赖的服务完成后,依赖的服务才能算完成,这是一种可靠的服务序列,要么都成功,要么都失败服务的状态可以保持一致;
异步:所谓异步是一个服务的完成需要依赖其他的服务时,只要通知其依赖的服务开始执行,而不需要等待被依赖的服务完成,此时该服务就算完成了,至于被依赖的服务是否真正完成并不关心,所以这是不可靠的服务序列;
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务,函数只有在得到结果之后才会返回;
非阻塞:和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立即返回;
阻塞调用和同步调用是不同的,对于同步调用来说,很多时候当前线程可能还是激活的,只是从逻辑上当前函数没有返回值而已,此时这个线程可能也会处理其他消息,所以如下总结:
如果这个线程在等待当前函数返回时,仍在执行其他消息处理,这种情况叫做同步非阻塞;
如果这个线程在等待当前函数返回时,没有执行其他的消息处理,而是处于挂起等待的状态,这种情况叫做同步阻塞;
如果这个线程当前的函数已经返回,并且仍在执行其他的消息处理,这种情况叫做异步非阻塞;
如果这个线程当前的函数已经返回,但没有执行其他的消息处理,而是处于被挂起的等待状态,这种情况叫做异步阻塞;
同步与异步的重点在于的等待依赖的服务是否返回结果(即使没有执行完),也就是结果通知的方式,而不管其依赖的服务是否完成,阻塞与非阻塞的重点在于当前线程等待消息返回时的行为,是否执行其他的消息处理,当前线程是否被挂起;
大部分操作系统(如windows、Linux)的任务调度采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行,任务执行的一段时间叫做时间片,任务正在执行的状态叫做运行状态,任务执行一段时间后强制暂停去执行下一个任务,被暂停的任务就处于就绪状态,等待下一个属于他的时间片的到来,这样每个任务都可以得到执行,由于CPU的执行效率非常高,时间片非常短,在各个任务之间快速的切换,给人的感觉是多个任务在“同时执行”,这也即我们所说的并发;
计算机的额核心是CPU,它承担了所有的计算任务,操作系统是计算机的管理者,它负责任务的调度、资源的分配和管理,统领整个计算机的硬件,应用程序是具有某种功能的程序,其运行在操作系统上,而进程则是一个具有一定独立功能的程序在一个数据集上一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体,进程是CPU资源分配的最小单位(线程是CPU执行的最小的单元),各个进程之间内存地址相互隔离。
线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位,一个进程可以有多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间),线程是一个进程中代码的不同执行路线,线程上下文切换比进程上下文切换要快得多。
由于手机硬件的限制,在Android手机中过多的使用内存,会容易导致oom(out of memory 内存溢出),过多的使用CPU资源,会导致手机卡顿,甚至导致ANR(Application Not Responding 应用程序无响应),Android主要从以下几部分对此进行优化:
布局优化,使用herarchyviewer(视图层次窗口)工具删除无用的空间和层级;
选择性能较低的viewgroup,比如在可以选择RelativeLayout也可以使用LinearLayout的情况下,优先使用LinearLayout,因为相对来说RelativeLayout功能较为复杂,会占用更多的CPU资源;
使用标签重用布局、减少层级、进行预加载(用的时候才加载);
绘制优化:指view在ondraw方法中避免大量的耗时的操作,由于onDraw方法可能会被频繁的调用;
ondraw方法中不要创建新的局部变量,ondraw方法被频繁的调用,很容易引起GC;
ondraw方法不要做耗时的操作;
线程优化:使用线程池来管理和复用线程,避免程序中出现大量的Thread,同时可以控制线 的并发数,避免相互抢占资源,而导致线程阻塞;
如果一个无用的对象(不需要再使用的对象)仍然被其他对象持有引用,造成该对象无法被系统回收,以致该对象在堆中所占有的内存单元无法被释放而造成内存空间浪费,这种情况就是内存泄漏,常见的内存泄露的场景有:
单例模式:因为单例的静态特性使得他的生命周期同应用的生命周期一样长,如果一个对象已经没有用处了,但是单例还持有他的引用,那么在某个应用程序的生命周期他都不能正常回收,从而导致内存泄漏;
静态变量导致内存泄漏:静态变量存储在方法区,他的生命周期从类加载开始,到整个进程结束,一旦静态变量初始化,他所持有的引用只有等到进程结束才会释放;
非静态内部类导致内存泄漏:非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部对象的生命周期长时,就会导致内存泄漏,通常在Android开发中,如果要使用内部类,但又要规避内存泄漏,一般会采用静态内部类+弱引用的方式;
未取消注册或回掉导致内存泄漏:比如在Activity中注册广播,如果在Activity销毁后不取消注册,那么这个广播会一直存在于系统中,同非静态内部类一样持有Activity引用,导致内存泄漏,因此在注册广播后一定要取消注册;
Android中主线程是不能进行耗时操作的,子线程不能进行更新UI,所以就有了Handler,其作用就是实现线程之间的通信。
Handler在整个线程通信的过程中,主要有四个对象,Handler、Message、MessageQueue、Looper等,当应用创建时,就会在主线程中创建Handler对象,将要传递的信息保存到Message中,Handler通过调用sendMessage()方法将Message发送到MessageQueue中,Looper对象不断调用Loop( )方法从MessageQueue中取出Message交给handler进行处理,从而实现线程之间的通信。
帧动画:通过指定每一帧图片和播放的时间,有序地进行播放而形成动画效果;
补间动画:通过指定的view的初始状态、变化时间、方式等,通过一系列的算法去进行图形变换从而形成动画效果,主要有Alpha、Scale、Translate、Rotate四种效果;
属性动画:在Android3.0开始支持,通过不断改变view的属性,不断的重绘而形成动画效果;
使用一个比喻形容他们的关系,Activity像一个工匠(控制单元),Window像窗户(承载模型)、View像窗花(显示视图)、LayoutInflater像剪刀、XML配置像窗花图纸:
Activity构造的时候会初始化一个Window,准确的说应该是PhoneWindow;
这个PhoneWindow有一个”ViewRoot“,这个”ViewRoot”是一个View或者说是ViewGroup,是最初的根视图;
“ViewRoot”是通过addView方法来添加一个个View的,比如TextView、Button等;
这些view的事件的监听,是由WindowManagerService来接收消息,并且回调Activity函数,比如onClickListener、onKeyDown等;
Context是一个抽象基类,译为上下文,也可理解为环境,是用户操作操作系统的一个过程,这个对象描述的是一个应用程序环境的全局信息,通过它可以访问应用程序的资源和相关的权限,简单的说Context负责Activity、Service、Intent、资源、Package和权限等操作。Context层次如下图:
第一层:Context抽象类;
第二层:一个ContextImpl的实现类,里面拥有一个PackageInfo类,PackageInfo类是关于整个包信息的类,一个ContextWraper是一个Context的包装类,里面有一个ContextImpl类的实例,通过整个实例去调用ContextImpl里面的方法;
第三层:Service和Application直接继承ContextWrapper,但是Activity需要引入主题,所以有了ContextThemeImpl类;
总结:在使用Context对象获取静态资源,创建单例对象或者静态方法时,多考虑Context的生命周期,不要使用Activity的Context,要使用生命周期较长的Application的Context对象,但并不是所有的情况都使用Application的Context,在创建Dialog、view等控件的时候,必须使用Activity的Context对象。
float是单精度类型,精度是8位有效数字,取值范围是10的-38次方到10的38次方,float占用4个字节的存储空间;
double是双精度类型,精度是10的-308次方到10的308次方,double类型占用8个字节的存储空间;
默认情况下都用double来表示,所以如果要用float,则应在数字对象后加字符f。
使用SharedPreference存储:保存基于xml文件存储的key-value键值对数据,通常用来存储一些简单的配置信息;
文件存储方式:Context提供了两个方法来打开数据文件的文件IO流;
SQLite存储数据:SQLite是轻量级的嵌入式数据库引擎,支持SQL语言;
网络存储数据:通过网络存储数据;
ANR全名Appication NotResponding ,也就是“应用程序无反应”,当操作在一段时间内无法得到系统回应时就会出现ANR错误,造成ANR的主要原因是由于线程的任务在规定的时间内没有完成造成的;
垃圾收集算法的核心思想:对虚拟机的可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,则称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。
JVM进行次GC的频率很高,但因为这种GC占用的时间极短,所以对系统产生的影响不大,但主GC对系统的影响很明显,触发主GC的条件主要有下:
当应用程序空闲时,即没有应用线程在运行时,GC会被调用,因为GC在优先级别最低的线程中进行,所以当应用繁忙时,GC就不会被调用;
Java堆内存不足时,主GC会被调用,当应用线程正在运行,并在运行过程中创建新对象时,若这时内存空间不足,JVM会强制调用主GC,以便回收内存用于新的分配,若GC一次之后仍无法满足,则会继续进行两次,若仍无法满足,则会报OOM错误;
System.gc( )函数的作用只是提醒虚拟机希望进行一次垃圾回收,但并不一定保证会进行,具体什么时候进行取决于虚拟机;
按照国际惯例,给大家分享一套十分好用的Android进阶资料:《全网最全Android开发笔记》。
整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。
因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。
如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
1、热修复设计
2、插件化框架设计
3、组件化框架设计
4、图片加载框架
5、网络访问框架设计
6、RXJava响应式编程框架设计
……
1、设计思想与代码质量优化
2、程序性能优化
3、开发效率优化
……
1、高级UI晋升
2、Android内核组件
3、大型项目必备IPC
4、数据持久与序列化
5、Framework内核解析
……
1、NDK开发之C/C++入门
2、JNI模块开发
3、Linux编程
4、底层图片处理
5、音视频开发
6、机器学习
……
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter Dart语言系统入门
……
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战
……
1、准备开始
2、基础
3、类和对象
4、函数和lambda表达式
5、其他
……