吐血总结 2020 Android 实习面经

知识点总结:
  1. onSaveInstanceState方法只会在Activity被异常终止的情况下调用。销毁时把Activity在onSaveInstanceState方法中保存的Bundle对象作为参数传递给onRestoreInstanceState和onCreate方法。对于时间的调用:onSaveInstanceState在onStop之前,onRestoreInstanceState在onStart之后。在这两个方法中,系统默认为我们做了一定的恢复操作,比如保护和恢复View的层次结构,用户输入的数据,LisView的滚动位置等。每一个View中都有这俩个方法。

  2. 通过Application去启动Standard模式的Activity的时候会报错,因为Application没有任务栈,解决的方法是设置一个标志位,这样启动的时候会为新的Activity以singleTask的模式启动。singleTask模式下如果被启动的Activity没有找到活动栈,就会新创建一个。

  3. TaskAffinity 标识了一个Activity所需的任务栈的名字。任务栈也分为前台任务栈和后台任务栈

  4. 隐式调用的匹配规则:Action:必须有一个完全一致。 Category:Intent一旦设置Category,那么必须与规则中的任何一个category相同,当然,如果不设置category也可以通过。前提是过滤规则中必须加上DEFAULT这个规则

  5. Binder通信中由于反序列化之后不是同一个对象,解注册会失败,这时候就需要用到RemoteCallbackList来存储客户端传来的注册的接口

  6. 如果明确知道某一个远程方法是耗时的,那么就在子线程执行,而如果是在UI线程去发起的远程请求的话,会导致ANR。所以最好避免客户端通过UI线程去访问远程方法。

  7. Binder权限验证的方法 通过自定义permission,在onBind方法中去检查权限,或者是在binder类的onTransact方法中做验证,通过得到uid和pid。可以做一些验证,比如验证包名

  8. View的绘制流程与Activty的生命周期不同步,所以不能简单的在生命周期中去获取View的宽高,可以使用View.post,或者onWindowFocusChanged回调接口。

  9. 记忆集的设计是为了解决跨代引用的问题。卡表是记忆集的一种实现。JVM通过写屏障,在引用赋值操作生成更新卡表的操作。

  10. 解决并发标记所产生的问题:增量更新和原始快照,也是应用写屏障,分别记录下删除引用和添加引用的操作,在GC Roots 树遍历结束后,以刚才改变地方为GC Roots,重新开始遍历。

  11. 垃圾收集器:

    • Serial/Serial old 适用于客户端,虚拟机管理的内存小的场景,这两种收集器通过单线程进行垃圾回收
    • ParNew 新生代收集器,通过多线程垃圾回收,一般配合CMS老年代收集器使用,JDK9之火就不推荐了
    • Parallel Scavenge/Parallel Old(吞吐量优先垃圾收集器) 新生代/老年代收集器,通过多线程垃圾回收,专注于吞吐量的提高,这个收集器的目标是达到一个可控制的吞吐量。它有提供两个参数来分别控制最大垃圾收集停顿时间和直接设置吞吐量大小。
    • CMS 以最短回收停顿时间为目标的老年代收集器 基于标记清除算法(缺点)。会有大量空间碎片差生、同时无法处理浮动垃圾(在垃圾清除时产生的垃圾)、容易占用处理器资源,使用户线程执行变慢。垃圾回收分为四个阶段:初始标记,并发标记,重新标记,并发清除。其中并发标志所导致的问题通过增量更新来解决。
    • G1 开创了面向局部收集的设计思路和基于Region的内存布局形式。JDK8的时候被官方成为“全能的垃圾收集器”。是CMS的代替者和继承人。它将堆内存分配成多个大小相等的独立Region,
  12. 类加载机制:Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。在类加载过程中,会经历:加载,验证,准备,解析,初始化,使用,卸载这几个生命周期

  13. 加载过程需要完成三件事情:1、通过类的权限定名获取类的二进制字节码字节流。2、将字节流代表的静态存储结构转化成方法区的运行时数据结构 3、生成类的class对象。 其中获取二进制流字节码文件是我们开发人员可控性最强的阶段

  14. 验证阶段的四个过程:1.文件格式验证:保证字节流能正确解析并存储于方法区内 2、 元数据验证:使其符合Java语法,比如不能继承fianl类。 3、对类的方法体(也就是Code属性)进行校验分析。 4、 符号引用验证,确保自己访问的资源可以访问到,不是private的。

  15. 准备阶段:为类中的变量分配内存并设置类变量初始值(如果是final static修饰的常量则直接赋值)。本应该在方法区中分配内存,但JDK8之后类变量随着Class对象一起存放在堆内存中(元空间),元空间使用的是本地内存,原则上和堆区共享内存区域。

  16. 解析阶段:将常量池内的符号引用替换为直接引用的过程,直接引用可以是一个指针、相对偏移量或者是能定位到目标的句柄。分为类解析、字段解析、方法解析、接口方法解析。本质都是对继承树自下而上进行搜索,找到具体的目标后返回对这个目标的引用。

  17. 初始化阶段,就是执行类构造器方法的过程,类构造器是javac编译器自动生成的产物。由静态代码块和类变量的赋值语句组成,顺序按照源代码申明的顺序开始。静态语句块只能访问到定义在静态语句块之前的变量。定义在他之后的变量,只能赋值,不能访问。最先执行方法的类一定是Object。clinit必须同步处理。

  18. 类加载器:通过一个类的权限定名来获取描述该类的二进制字节流,实现这个动作的代码被称为“类加载器”。

  19. 双亲委派模式:从JVM的角度看只有两种类加载器,启动类加载器(C++实现)和其他继承自类ClassLoader的java实现的类加载器。从开发人员的角度看Java类加载机制的话保持着三层类加载器、双亲委派的类加载架构:1.顶层:启动类加载器,负责加载lib包下的类库,用户不能直接引用。如果想把加载请求给引导类加载器去处理,那直接使用null代替即可。2. 拓展类加载器:负责加载lib\ext目录的类库,开发者可以直接使用这个类加载器。 3. 应用程序类加载器,是ClassLoader.getSystemClassLoader的返回值,负责加载用户类库上的所有java文件。

  20. 双亲委派模式的工作过程:当一个类加载器收到请求时,自己本身先不去处理,而是将请求委托给上一级,每一级都是如此。直到顶层类加载器,只有当父加载器反馈自己无法加载这个类,再讲请求向下传递。 作用:为了保证不同的类加载器去执行加载同一个class文件的请求时,能将请求传递给同一个类加载器,这样加载的类才是同一个类。保证了Java中的秩序不混乱,越基础的类交给越上层的类加载器

  21. 通过线程类加载器可以破坏双亲委派模式,当上层的基础类想要去加载底层类时,上层的类加载器是无法加载下层的类,这是只能破坏双亲委派模式,由上层类加载器去调用下层的类加载器。

  22. JDK9模块化增加了一个平台化接口,替代了拓展类加载器。继承结构也发生了改变,三个类加载器都继承自BuiltinClassLoader。双亲委派模式还在维持,但是有一点发生变化,当平台类或者应用程序类加载器收到请求时,会在向上委托请求前,先判断一下该类是否能够归属到一个系统模块中。

  23. Java 内存模型:所有的变量都存储在主内存,每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用的变量的主内存副本,线程对变量的所有操作必须在工作内存进行,不能直接读写主内存中的数据。不同线程也不能直接访问对方工作内存中的变量。线程间变量的传递均需要通过主内存来完成。

  24. volatile Java虚拟机提供的最轻量级的同步机制。它具有两个特性1:、保证了修饰的变量具有可见性 2、禁止指令重排序优化。Java 内存模型针对volatile的特殊规则:load、read动作相关联,必须连续且一块出现,store和write动作相关联,必须连续且一起出现。

  25. Java内存模型定义了一些很常见的先行发生原则,比如start操作早于线程的每一个动作等等,这个先行发生原则和时间的先后没有关系。

  26. 主流的虚拟机每一个线程都是直接映射到一个操作系统原生线程来实现的,而且中间没有额外的间接结构。虚拟机自己是不会去干涉线程调度。


1. 主线程里的looper的无限循环为什么不会堵塞主线程,是不是非常会消耗系统资源?

  1. 不会堵塞主线程,因为一个Android的App就是基于这个无限循环来维持运行的,每一个动作,包括Activity的生命周期的回调,都是通过包装成一个消息,发送到MessageQueue中,然后Loop中收到消息去执行响应的逻辑。真正可能卡死的是在回调方法中去执行长时间的操作,导致掉帧甚至ANR。
  2. 不会非常消耗系统资源,当loop循环中去接受消息时,如果当前没有消息,主线程会在messageQueue的next方法处堵塞,休眠,具体是在一个native方法:nativePollOnce方法中。这里就涉及到linux内核的东西。在这个方法里,主线程会释放cpu资源进入休眠状态,直到下一个消息到达或者有事务发生,通过pipe管道写端写入数据来唤醒主线程工作,所以说主线程大部分时候都是出于休眠状态,并不会消耗大量CPU资源。

2. 系统为什么不允许子线程中访问UI?

因为Android的UI控件是线程不安全的,如果多线程并发访问可能会导致UI控件出于不可预期的状态。


3. Handler是如何能够线程切换的

本质是因为线程间可以对内存共享,这样主线程和子线程和与对同一个主线程创建的Handler访问,子线程通过在主线程创建的mHandler,发送一个消息到MessageQueue中,然后再由loop循环取出消息,再由handler去处理这条消息,这个过程其实就已经是运行在主线程了。


4.说一下你对Android的事件分发机制的理解

常说的事件分发机制,一般指的是


5. 说一下关于屏幕适配的理
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值