牛马命,八股情,再练十题小八股,争做合格好牛马

3,531 阅读11分钟

最近面了几家公司,感觉虽然说八股文不怎么被提倡了,面试不怎么考八股文了,但是我怎么感觉这种说法在我身上不适用呢,基本面的每家公司都有八股文的内容,有一家还跟我聊了半小时八股文,差点嘴里要冒火星子了,所以这种东西还是要多准备准备,但是说起来容易,做起来就很难了,光Android应用层能考的八股文就有一大堆,没有一定的时间是没法都理清的,所以只能是慢慢积累,多看多写,能记住一些是一些,这篇文章又整理了几个八股文,有兴趣的可以看下

1.App的启动流程

  1. 当点击了app图标后,由Launcher进程通过Binder IPC向system_server发出startActivity的请求
  2. system_server在收到请求后,向zygnote进程发出创建进程的请求
  3. zygnote在收到请求后,fock出了一个子进程也就是App进程,App进程通过Binder IPC向system_server进程发出attachApplication的请求
  4. system_server进程收到请求后,经过一系列操作,通过Binder IPC向App进程发出scheduleLaunchActivity的请求
  5. App进程的binder线程,也就是ApplicationThread,收到请求后,通过handler向主线程发出LAUNCH_ACTIVITY的消息
  6. 主线程收到消息后,通过发射机制创建目标Activity,并且调用目标Activity的OnCreate方法
  7. 然后进入Activity生命周期渲染页面,结束后页面就被渲染到屏幕上了

2.线程有哪些状态

  1. 新建状态(New):新创建了一个线程对象,仅在堆中分配内存
  2. 就绪状态(Runnable):线程被创建后,其他线程调用了该线程的start方法
  3. 运行状态(Running):就绪状态下的线程获得了cpu时间片
  4. 阻塞状态(Blocked):运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态,阻塞状态是正在运行的线程遇到某个特殊情况。例如,延迟、挂起、等待I/O操作完成等。 进入阻塞状态的线程让出CPU,并暂时停止自己的执行。线程进入阻塞状态后,就一直等待,直到引起阻塞的原因被消除,线程又转入就绪状态,重新进入就绪队列排队
  5. 死亡状态(Dead):线程执行完了或者因异常退出run()方法,该线程结束生命周期。死亡的线程不可再次复生。

画了一个草图来描述这五种状态的关系

image.png

3.java中的四大引用

  • 强引用:当对象是强引用的时候,就不会被垃圾回收器回收,当内存不足的时候,会抛出oom异常,如果想让垃圾回收器回收该对象,就要显示的将对象置为null,那么垃圾回收器就会在合适的时间回收它
  • 软引用:不会很快去回收具有软引用的对象,垃圾回收器会根据具体堆的使用情况去判断是否去回收,即超过一定阀值时才会去回收
  • 弱引用:如果一个对象是弱引用的,那么无论什么时候,只要被垃圾回收器检测到,那么都会被回收
  • 虚引用:它不会决定对象的生命周期,任何时候都会被垃圾回收器回收,它的作用是跟踪对象被垃圾回收器回收的活动,经常与ReferenceQueue一起使用

4.Fragment的生命周期

image.png image.png

5.简述双亲委托机制

双亲委托机制的本质就是,当一个类加载器收到类加载请求时候,它首先会一直向上找它的父加载器,直到上面没有父加载器了,再去加载类,只有父加载器在自己的搜索范围内找不到指定类,才会交给子加载器去加载,大致一个流程图如下

image.png

6.协程launch有哪些参数

我们通常在使用launch创建一个协程的时候,没啥意外都会这么写

image.png

很简洁,看起来很舒服,久而久之我们就会忽略了一点,其实launch这个函数也是有其他参数的,当我们点到launch函数里面后,会看到,launch函数里面有三个参数

image.png

其中第三个参数就是我们的协程作用域,前两个是什么呢?先来看context,它是一个协程的上下文环境,默认是没有上下文环境的,作用是什么呢?来看下面这段代码

image.png

这里有三个协程,分别都是打印一段文字,其中第一个协程在启动后里面将它cancel掉,第二个协程的上下文环境设置的是第一个协程,那么这段代码的执行结果会怎么样呢?

image.png

仅仅只是执行了第三个协程,第一个没执行很好理解因为cancel掉了,而第二个协程由于它的上下文环境设置的是第一个协程,也就是依赖于第一个协程的,所以第二个协程也不会执行,第一个参数的作用大概理解了,来看下第二个参数start,它是一个CoroutineStart的枚举类型,个人把它理解为协程的启动模式,点到里面可以看到有这么些枚举值

  • DEFAULT:默认值,立即根据它的上下文环境执行协程
  • LAZY:不会立即执行,只有在被调用的时候才会执行
  • ATOMIC:当调用cancel函数的时候,会将挂起点之后的代码取消
  • UNDISPATCHED:不调度,线程与父协程所在的线程是一致的

7.如果子协程发生异常,会不会影响它的兄弟协程

这种问题知道的肯定直接脱口而出,不知道的咱就自己写段代码试试看

image.png

现在有这么一段代码,它的执行结果如下所示

image.png

如果其中一个协程里面出现异常了,那么其他协程还会不会执行呢,我们改下代码

image.png

现在的执行结果如下所示

image.png

可以看到只执行了第一个协程,出现异常的协程的后面的协程都没有执行

8.如何让协程的异常不影响它的其他兄弟协程

上面我们讲到了当协程出现异常后,它的后续要执行的兄弟协程也不会执行了,那么有没有办法让它的兄弟协程别受影响呢,这里介绍俩种方式

SupervisorJob

之前在介绍launch函数中的参数时候,我们知道context参数是协程运行的上下文环境,那么这里我们可以给出现异常的协程的上下文环境设置一个SupervisorJob,这样就能防止协程的异常影响它的兄弟协程

image.png

这个时候再看下执行结果有什么不一样

image.png

可以看到这里第三个协程也执行了,除此之外,还有另一个办法

supervisorScope

除了给协程设置上下文环境,还可以把所有兄弟协程都包在一个supervisorScope作用域里面,这样也可以达到某个协程出现异常后,不影响其他协程

image.png

这段代码的执行结果如下

image.png

同样除了出现异常的协程外,其他协程都执行了

9.协程之间是如何通信的

这题要听清楚了,很容易听成线程之间如何通信,毕竟这个也是个常见题,但是协程之间的通信就不一样了,可能你会脱口而出Flow,当然这个是对的,但有可能面试官还会继续问你还有吗,那么这个时候,你就要知道协程里面还有一个Channel也可以实现协程间的通信,来看下面这个例子,假如这里有两个协程

image.png

一个协程里面有个字符串,现在我想要让另一个协程拿到这个字符串,就要用到Channel了,并且使用send以及receive函数来发送接收数据,代码如下

image.png

这个时候第二个协程就拿到了传送过来的数据

image.png

那么如果第一个协程里面是一个网络请求,数据需要等一段时间才能拿到,那么第二个协程还会接收到数据吗,答案是会的,因为receive函数它是一个挂起函数,它会等到有数据来了以后再获取数据,我们来试一下

image.png

我们给第一个协程加了个延迟三秒,并且在操作执行前以及协程二获取到数据的地方打印了时间,看下具体执行结果

image.png

和预期的一样,第二个协程在没有接收到数据时候一直是挂起状态,直到延迟时间结束了,数据发送过来了,第二个协程才执行

10.协程的Channel里面的三个参数都是做什么用的

当点到Channel的构造函数里面的时候,会看到它有三个参数

image.png

咱先来看第一个参数,capacity是容量的意思,那么什么时候应该设置这个值呢,来看下这个例子

image.png

这里在第一个协程中连续发送了五条数据,在第二个协程里面调用receive函数来接收数据,我们可能猜到了,它接收的数据应该是第一条数据

image.png

那么我们不是还发送了另外四条数据吗,都到哪去了呢?其实我们可以从截图左上角发现,这个程序其实还没有结束,没有结束的原因是程序仍旧在等待去接收另外四条数据,因为我们只调用了一次receive函数,也就是我们再调用四次receive函数就可以让程序完成执行了,我们试一下

image.png image.png

诶!果然在多调用四次receive函数后,程序就完成执行了,但是这个做法太傻了,如果我们发送了100条数据呢,也在下面调用100次receive吗?所以这里就要用上我们第一个参数capacity了,这个参数就表示一个阻塞队列的大小,默认是0,所以我们只需要扩大这个队列就好了,让其他数据在队列里面等待,代码如下

image.png

这里将大小增加到5了,再来运行下程序看看

image.png

可以发现虽然只调用了一次receive函数,但是程序已经执行完成了,因为我们已经扩大阻塞队列的大小了,其余数据都被塞入队列里面去了,当然除了扩大阻塞队列大小,我们还可以使用第二个参数来做同样的事情,第二个参数叫onBufferOverflow,它是一个枚举类型,有以下三个枚举值

image.png

我们默认参数用的就是第一个SUSPEND,意思就是让多出来的数据处于挂起状态,而另外两个枚举值,字面意思就可以知道,是丢弃最老以及丢弃最新的意思,我们分别使用这俩值对比下执行结果

image.png image.png

同样可以让程序完成执行,并且使用DROP_OLDEST的时候,是将老数据丢弃,留下最新数据也就是message5,而使用DROP_LATEST的时候,留下的就是最老数据,也就是message1,这个时候可能有人要问了,别丢啊,这些数据我还有用呢,那么这里就要用到Channel第三个参数onUndeliveredElement,这是一个函数类型的参数,有个回调参数E,这个就是被丢弃的那些数据,我们以DROP_OLDEST为例看下

image.png

这里要注意的是,想要获取被丢弃的数据,我们的capacity的值必须是大于0,不然将会拿不到被丢弃的数据,这段代码运行后得到的结果如下

image.png

可以看到除了最新数据被接收到之外,其余数据都在onUndeliveredElement函数内被获取到了

总结

八股文这种东西,看起来貌似都没啥用,有一些在我们开发当中甚至都碰不到,但是这种东西,多准备一些,多掌握一些,可能就可以帮助你在面试当中脱颖而出,给面试官留下更深的印象,我就遇到过一次,上午看了一题,然后下午的面试就问到了,这个题目我要是不看,那么面试中就回答不出来,面试通过的概率小了,所以还是静下心来,多看点多背点多写点吧