Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)

三月,想必各位程序员GG 此刻想要的,莫过于一篇面试宝典。鄙人不才,也从未系统的刷过面试题,以鸿洋大哥一篇面试题为引,略加整理,希望能帮到各位不知道看什么小伙伴。(大部分知识都是有答案的).

原文链接: [https://mp.weixin.qq.com/s/p3l9wr4DX976Lr62-dYe8w(只有题)]

PS:其实在推送2天后就已经全部 总结出来了,只是新的CSDN 编辑器排版不会用,也没时间来整理,终于有时间了,把格式排版下。(另外有些题我没去搜索,是因为我觉得那些问的比较少,而且我对那些问题从未听过,之后再进阶的时候在回来看看,并在更新,现在的这些我觉得足够复习了。)


主要分为以下几部分:

(1)java面试题
(2)Android面试题
(3)高端技术面试题
(4)非技术性问题&HR问题汇总

1.java面试题

熟练掌握java是很关键的,大公司不仅仅要求你会使用几个api,更多的是要你熟悉源码实现原理,甚至要你知道有哪些不足,怎么改进,还有一些java有关的一些算法,设计模式等等。

(一) java基础面试知识点

  1. java中==和equals和hashCode的区别
    基本数据类型(int,char,long,boolean等)使用 = =,引用字符类型使用 equals,hashmap中,key.hashCode 出值先进行值对比,在进行equals 对比,然后在确定是否加入HashMap
  2. int、char、long各占多少字节数
    Int -4,char -2,float: 4个字节 double: 8个字节 long: 8个字节,short-2
  3. int与integer的区别
    1、Integer是int的包装类,int则是java的一种基本数据类型
    2、Integer变量必须实例化后才能使用,而int变量不需要
    3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
    4、Integer的默认值是null,int的默认值是0
  4. 探探对java多态的理解
    面向对象的三大基本特征:封装、继承、多态
    1.(封装)这个封装其实就是面向对象语言的精髓,在这里一些都是对象,我们通过封装,只为用户提供接口,而隐藏了内部的具体实现,比如插班。不用考虑内部,我们只需要插统一的一个插口,就是调用这个接口。
    2.(继承)
    ①提高了代码的复用性。
    ②类与类之间产生了关系,关系的出现
    ③对父类功能进行重定义.
    3.(多态)同一消息可以根据发送对象的不同而采用多种不同的行为方式,父类引用指向子类对象,在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。用我的话就是智能判断你需要调用的方法!
  5. String、StringBuffer、StringBuilder区别
    String 字符串常量
    StringBuffer 字符串变量(线程安全)
    StringBuilder 字符串变量(非线程安全)
    1.String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String,因为每次生成对象都会对系统性能产生影响.而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用
    2.有时候你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
    String S1 = “This is only a” + “ simple” + “test”; 其实就是:
    String S1 = “This is only a simple test”; 所以当然不需要太多的时间了
    3.在大部分情况下 StringBuilder > StringBuffer ,不保证同步,
  6. 什么是内部类?内部类的作用

  7. 1.内部类可以直接访问外部类中的成员(包括私有成员),
    而外部类要访问内部类中的成员必须要建立内部类的对象
    2.内部类可分为静态内部类,动态内部类,匿名类
    静态内部类:性质上和外部类没什么区别,只是形式上是写在另外一个类里面,通过另外一个类名访问。所以一个类应不应该被写为静态内部类纯看个人喜好。对我来说,一个类满足以下条件时我就把它写为静态内部类:
    ① 类的代码量不多,可能只是定义一个结构体,一个接口,或者一丁点逻辑代码
    ② 类只起到辅助作用,一般不单独拿出来用
    ③. 和它所在的类关系非常紧密,以至于它的修改一般都是由它所在类引起动态内部类:
    动态内部类实例化之后会保留它所在类的实例,所以内部类可以访问它所在类的动态属性。动态内部类可以模拟多继承。
    匿名内部类其实也是一种动态内部类,只是没有类名。
  8. 抽象类和接口区别
    Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第1张图片
    抽象类的意义
    利于代码的维护和重用,我目前使用最频繁的就是BaseActivity 基类。(初始化调用状态栏的方法)以及网络请求中的错误类型判断。
  9. 抽象类与接口的应用场景
    ①interface的应用场合
    A. 类与类之前需要特定的接口进行协调,而不在乎其如何实现。(比如jni中的调用)
    B. 作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。
    C. 需要将一组类视为单一的类,而调用者只通过接口来与这组类发生联系。
    D. 需要实现特定的多项功能,而这些功能之间可能完全没有任何联系。
    ②abstract class的应用场合
    一句话,在既需要统一的接口,又需要实例变量或缺省的方法的情况下,就可以使用它。最常见的有:
    A. 定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口。可以用abstract class定义一组方法体,甚至可以是空方法体,然后由子类选择自己所感兴趣的方法来覆盖。
    B. 某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还必需类中表示状态的变量来区别不同的关系。abstract的中介作用可以很好地满足这一点。
    C. 规范了一组相互协调的方法,其中一些方法是共同的,与状态无关的,可以共享的,无需子类分别实现;而另一些方法却需要各个子类根据自己特定的状态来实现特定的功能
  10. 抽象类是否可以没有方法和属性?
    ?????? (作者偷懒没去总结)
  11. 接口的意义
    功能的抽象,通过接口可以实现不相关类的相同行为,而不需要了解对象所对应的类。
    通过接口可以指明多个类需要实现的方法。
  12. 父类的静态方法能否被子类重写
    来说一下我的观点,父类的静态方法不能被子类继承,更谈不上重写,就算是子类中有一个和父类一模一样的静态方法,那也是子类本身的,和父类的那个静态方法不是一回事。方法加静态后就属于类不属于对象了。
  13. 进程和线程的区别
    1)进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
    2)同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。
    3)线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的
    5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
    6)线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志
  14. final,finally,finalize的区别
    1)Final用于修饰类、成员变量和成员方法。final修饰的类,不能被继承(String、StringBuilder、StringBuffer、Math,不可变类),其中所有的方法都不能被重写,所以不能同时用abstract和final修饰类
    2)Finally通常和try catch搭配使用,保证不管有没有发生异常,资源都能够被释放(释放连接、关闭IO流)。
    3)Finalize是object类中的一个方法,子类可以重写finalize()方法实现对资源的回收。垃圾*回收只负责回收内存,并不负责资源的回收,资源回收要由程序员完成,Java虚拟机在垃圾回收之前会先调用垃圾对象的finalize方法用于使对象释放资源(如关闭连接、关闭文件),之后才进行垃圾回收。
  15. 序列化的方式
    1)有的时候我们想要把一个Java对象变成字节流的形式传出去,有的时候我们想要从一个字节流中恢复一个Java对象。
    2)我常用Serializable接口 实现序列化,在传递Bean 数据包时候
  16. Serializable 和Parcelable 的区别
    1)Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、rmi以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是因为Serializable效率过慢
    2)Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据,而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable
  17. 静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
    一旦静态,就不属于对象了,因此不存在重新和继承
  18. 静态内部类的设计意图
    ?????? (作者偷懒没去总结)
  19. 成员内部类
    成员内部类也是最普通的内部类,它是外围类的一个成员,所以它可以无限制的访问外围类的所有成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
    在成员内部类中要注意两点:
    1)成员内部类中不能存在任何static的变量和方法;
    2)成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
  20. 静态内部类
    **静态内部类与非静态内部类之间存在一个最大的区别:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。
    没有这个引用就意味着:
    它的创建是不需要依赖于外围类的。
    它不能使用任何外围类的非static成员变量和方法。**
  21. 成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用
    ?????? (作者偷懒没去总结)
  22. 谈谈对kotlin的理解
    2017年谷歌I/O大会的最后,谷歌宣布将Kotlin语言作为安卓开发的一级编程语言
    Kotlin程序可以使用所有现有的Java框架和库,也就是说所有的现有程序不需要更改就可以直接被调用。
    Kotlin可以轻松学习,平易近人。它的规则及其简单,语法规则少,易于学习。
    Kotlin是开放源码,没有收费。虽然java也是开源语言,但是相比于其他的非开源的还是有一定优势的。
    Kotlin的空安全性很好,使用Kotlin,你可以用更少的代码获得更多的功能。 而你写的代码越少,你犯的错误就越少。
  23. 闭包和局部内部类的区别
    1)因为Java不支持多继承,支持实现多个接口。但有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
    2)局部内部类是嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。
  24. string 转换成 integer的方式及原理
    1)静态方法Integer.toString(intvalues),
    2)成员方法 a.toString();
    3.)String.valueof(a)
    4)String转integer 需要先进行非空判断,Integer.valueOf(str);

(二) java深入源码级的面试题(有难度)

  1. 哪些情况下的对象会被垃圾回收机制处理掉?
    1) 没有被引用的
    2)当内存占用过多,弱引用
    3)垃圾回收他是在虚拟机空闲的时候或者内存紧张的时候执行的,什么时候回收不是由程序员来控制的,需要被回收的时候并不会马上被回收,而是将其放入到一个准备回收的队列,去执行finalize方法。这也就是java比较耗内存的原因之一。
  2. 讲一下常见编码方式?
    在studio的右下角 有一个编码选择,通常有GBK,utf-8,utf-16,ASCII码
    utf-8编码中的中文占几个字节;int型几个字节?
    UTF-8不是固定字长编码的,而是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
    我在Notepad++ 测试 长度 为 3字节,
  3. 静态代理和动态代理的区别,什么场景使用
    1)为某个对象提供一个代理,以控制对这个对象的访问。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
    2)所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
  4. Java的异常体系
    1) Thorwable类所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常。
    其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常,
    2) Error是程序无法处理的错误,比如OutOfMemoryError、 Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。
    3) 运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等, 这些异常是不检查异常,非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。IOException,JSONException 从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。
  5. 谈谈你对解析与分派的认识。
    ?????? (作者偷懒没去总结)
  6. 修改对象A的equals方法的签名,那么使用HashMap存放这个对象实例的时候,会调用哪个equals方法
    ??????? (作者偷懒没去总结)
  7. Java中实现多态的机制是什么?
    多态自我理解就是多种状态!
    多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
  8. 如何将一个Java对象序列化到文件里?
    ?????? (作者偷懒没去总结)
  9. 说说你对Java反射的理解
    1)最简单的一句话来说就是:反射就是把Java类中的各种成分映射成相应的Java类。类中有什么信息,它就可以获得什么信息,不过前提是得知道类的名字,要不就没有后文了
    2)JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
  10. 说说你对Java注解的理解
    首先先说下我接触过的2种注解,一种是xUtils框架另外一种是ButterKnife(黄油刀),黄油刀注解真的很方便,对于需要一次性初始化大量的view 尤为方便。另外也可以对监听方法进行注解,xUtils只是了解,ButterKnife也只是使用了一段时间,后来在工作中,老大告诉我但凡是注解都是基于反射机制,影响性能,还是最原始的方法最好。现在常用的注解就是 @Deprecated 用来处理废弃代码,其他没深入了解.
  11. 说说你对依赖注入的理解
    本文从开头到现在提到的一系列依赖,只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI) 。
  12. 说一下泛型原理,并举例说明
    泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。提高代码的复用
  13. Java中String的了解
    1)String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。
    2)从源码中可以看出String类其实是通过char数组来保存字符串的。
    3)“String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。
  14. String为什么要设计成不可变的?
    1) 假若字符串对象允许改变,那么将会导致各种逻辑错误,比如改变一个对象会影响到另一个独立对象.
    2) String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。
  15. Object类的equal和hashCode方法重写,为什么?
    1)hashCode的意思就是散列码,也就是哈希码,散列码是没有规律的,如果x与y是两个不同的对象,那么x.hashCode()与y.hashCode()基本是不会相同的
    2)因为Object对象只与自身相等,所以同一个对象的地址总是相等的,计算取得的哈希码也必然相等,对于不同的对象,由于地址不同,所获取的哈希码自然也不会相等。因此到这里我们就明白了,两个对象通过调用equals方法是相等的,那么这两个对象调用hashCode方法必须返回相同的整数。

(三) 数据结构

  1. 常用数据结构简介
    1)数据的逻辑结构
    集合,线性结构,树形结构,图形结构
    2)数据的物理结构 :指数据的逻辑结构在计算机存储空间的存放形式。
    有顺序、链接、索引、散列等多种,常用的有顺序存储结构和链式存储结构。顺序映像借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。非顺序映像借助指示元素存储位置的指针(pointer)来表示数据元素之间的逻辑关系
  2. 并发集合了解哪些?
    在新增的Concurrent包中,BlockingQueue(阻塞队列)很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。
    1) 阻塞列表,使用LinkedBlockingDeque类。
    2) 用在生产者与消费者数据的阻塞列表,使用LinkedTransferQueue类。
    3) 使用优先级排序元素的阻塞列表,使用PriorityBlockingQueue类。
    4) 存储延迟元素的阻塞列表,使用DelayQueue类
  3. 列举java的集合以及集合之间的继承关系
    ?????? (作者偷懒没去总结)
  4. 集合类以及集合框架
    学习Java集合框架下大致可以分为如下五个部分:List列表、Set集合、Map映射、迭代器(Iterator、Enumeration)、工具类(Arrays、Collections)。
    容器类介绍以及之间的区别
    (容器类估计很多人没听这个词,Java容器主要可以划分为4个部分:List列表、Set集合、Map映射、工具类(Iterator迭代器、Enumeration枚举类、Arrays和Collections),具体的可以看看这篇博文 Java容器类 http://alexyyek.github.io/2015/04/06/Collection/)
    List,Set,Map的区别
    List
    1.可以允许重复的对象。
    2.可以插入多个null元素。
    3.是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
    4.常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
    Set:
    1.不允许重复对象
    2.无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
    3.只允许一个 null 元素
    4.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;
    Map
    1.Map不是collection的子接口或者实现类。Map是一个接口。
    2.Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。
    3.TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。
    4.Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
    5.Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、 TreeMap。
    (HashMap、TreeMap最常用)
  5. List和Map的实现方式以及存储方式
    1)List(有序,可重复)
    A:Map集合的数据结构仅仅针对键有效,与值无关。
    B:存储的是键值对形式的元素,键唯一,值可重复。
    2)HashMap也用到了哈希码的算法,以便快速查找一个键,
  6. HashMap的实现原理
    HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
    存储:当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标), 如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
    读取:从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。
  7. HashMap数据结构?
    ?????? (作者偷懒没去总结)
  8. HashMap源码理解
    ?????? (作者偷懒没去总结)
  9. HashMap如何put数据(从HashMap源码角度讲解)?
    存储:当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标), 如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
  10. ConcurrentHashMap的实现原理
    ?????? (作者偷懒没去总结)
  11. ArrayMap和HashMap的对比
    HashMap内部是使用一个默认容量为16的数组来存储数据的,而数组中每一个元素却又是一个链表的头结点,所以,更准确的来说,HashMap内部存储结构是使用哈希表的拉链结构(数组+链表),这种存储数据的方法叫做拉链法
    假设数据量都在千级以内的情况下:
    1、数据量不大,最好在千级以内,数据结构类型为Map类型
    2、如果key类型为其它的类型,则使用ArrayMap
  12. HashTable实现原理
    以淘汰
  13. TreeMap具体实现
    TreeMap是Java内部实现比较复杂的集合类之一。与HashMap不一样,TreeMap的底层不是用哈希表实现的,而是用红黑树实现的。当需要查找的元素是排好序的,TreeMap的优势就体现出来了。<1>每个节点的颜色是红色或黑色。<2>根节点必须是黑色的。<3>每个叶节点是黑色的(叶节点是指树尾端的NULL节点)。<4>如果一个节点是红色的,那么它的子节点必须是黑色。即,不能有连续的红色节点。<5>对于任意一个节点,从它到叶节点的每条路径包含相同数量的黑色节点。
  14. HashMap和HashTable的区别
    以淘汰
  15. HashMap与HashSet的区别
    1)HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。
    2)HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。HashMap是非synchronized的,但collection框架提供方法能保证HashMap synchronized,这样多个线程同时访问HashMap时,能保证只有一个线程更改Map。
  16. HashSet与HashMap怎么判断集合元素重复?
    其实HashSet 只是实现了HashMap 的 key部分,都是无序的存储形势
    1)HashSet不能添加重复的元素,当调用add(Object)方法时候,
    首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;
    如果已存在则调用Object对象的equals方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。
    2)HashMap中判断元素是否相同主要有两个方法,一个是判断key是否相同,一个是判断value是否相同。那就是首先得hashCode相同,然后根据key得到的 value 进行 equals 如果相同,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上
  17. 集合Set实现Hash怎么防止碰撞
    ?????? (作者偷懒没去总结)
  18. ArrayList和LinkedList的区别,以及应用场景
    ArrayList是基于数组实现的,LinkedList是基于双链表实现的:(双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。)
    (1)如果应用程序对各个索引位置的元素进行大量的存取或删除操作,ArrayList对象要远优于LinkedList对象;
    ( 2 ) 如果应用程序主要是对列表进行循环,并且循环时候进行插入或者删除操作,LinkedList对象要远优于ArrayList对象;
  19. 数组和链表的区别
  20. 二叉树的深度优先遍历和广度优先遍历的具体实现
  21. 堆的结构
  22. 堆和树的区别
  23. 堆和栈在内存中的区别是什么(解答提示:可以从数据结构方面以及实际实现方面两个方面去回答)?
  24. 什么是深拷贝和浅拷贝
  25. 手写链表逆序代码
  26. 讲一下对树,B+树的理解
  27. 讲一下对图的理解
  28. 判断单链表成环与否?
  29. 链表翻转(即:翻转一个单项链表)
  30. 合并多个单有序链表(假设都是递增的)
    ?????? (尼玛,这些都太深奥了,查了也不懂。。GG)

(四) 线程、多线程和线程池

  1. 开启线程的三种方式?
    1)首先第一种启用方法是通过继承Thread类,并改写run方法来实现一个线程
    2)第二种启用方式实现Runnable对象,并new Thread,常用第一种
    3)第三种启用方式通过Handler启动线程,handler.post(run)
    4)Thread和Runnable是实现java多线程的2种方式,runable是接口,thread是类,最终都需要通过thread.start()来使线程处于可运行状态。
  2. 线程和进程的区别?
    1)进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
    用我的话说就类似人类,每个人都是一个进程,负责调度,比如我现在开个线程去吃零食,在开个线程去看视频。进程用来分配资源,共享一个大脑和心脏.
    2)同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程(你活着不就是一直开着呼吸这个线程么)。
    3)进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束.
    4)线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的
    5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
    6)线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志
  3. 为什么要有线程,而不是仅仅用进程?
    1)进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
    进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
    2)如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,
    这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二
  4. 线程优点
    除了提高进程的并发度,线程还有个好处,就是可以有效地利用多处理器和多核计算机。现在的处理器有个趋势就是朝着多核方向发展,在没有线程之前,多核并不能让一个进程的执行速度提高,原因还是上面所有的两点限制。但如果讲一个进程分解为若干个线程,则可以让不同的线程运行在不同的核上,从而提高了进程的执行速度
  5. run()和start()方法区别
    1)用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体(跳过)代码执行完毕而直接继续执行下面的代码。通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
    2)run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
  6. 如何控制某个方法允许并发访问线程的个数?
    synchronized关键字主要解决多线程共享数据同步问题。
    synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。
    用于在多个线程间通信 时能够获得数据共享。Synchronized用于实现同步机制,
    synchronized取得的锁都是对象;每个对象只有一个锁(lock)与之相关联;实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制,缩小同步块内容。
  7. 在Java中wait和seelp方法的不同;
    1)这两个方法来自不同的类分别是Object ,Thread
    2)最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法(锁代码块和方法锁)。
    3)wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)
    4)sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
    5)注意sleep()方法是一个静态方法,也就是说他只对当前对象有效,通过t.sleep()让t对象进入sleep,这样的做法是错误的,它只会是使当前线程被sleep 而不是t线程
    6)wait属于Object的成员方法,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程;如果线程拥有某个或某些对象的同步锁,那么在调用了wait()后,这个线程就会释放它持有的所有同步资源。
  8. 谈谈wait/notify关键字的理解
    wait( ),notify( ),notifyAll( )都不属于Thread类,而是属于Object基础类,也就是每个对象都有wait( ),notify( ),notifyAll( ) 的功能,因为每个对象都有锁,锁是每个对象的基础,当然操作锁的方法也是最基础了。
    当需要调用以上的方法的时候,一定要对竞争资源进行加锁,如果不加锁的话,则会报 IllegalMonitorStateException 异常
    当想要调用wait( )进行线程等待时,必须要取得这个锁对象的控制权(对象监视器),一般是放到synchronized(obj)代码中。
    notify( )方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程)
    notifyAll( )通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行)
    假设有三个线程执行了obj.wait( ),那么obj.notifyAll( )则能全部唤醒tread1,thread2,thread3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,tread1,thread2,thread3只有一个有机会获得锁继续执行,例如tread1,其余的需要等待thread1释放obj锁之后才能继续执行。
    当调用obj.notify/notifyAll后,调用线程依旧持有obj锁,因此,thread1,thread2,thread3虽被唤醒,但是仍无法获得obj锁。直到调用线程退出synchronized块,释放obj锁后,thread1,thread2,thread3中的一个才有机会获得锁继续执行。
  9. 什么导致线程阻塞?
    1)线程执行了Thread.sleep(intmillsecond);方法,当前线程放弃CPU,睡眠一段时间,然后再恢复执行
    2)线程执行一段同步代码,但是尚且无法获得相关的同步锁,只能进入阻塞状态,等到获取了同步锁,才能回复执行。
    3)线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()或者notifyAll()方法。
    4)线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。
  10. 线程如何关闭?
    1)使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
    2)使用stop方法强行终止线程(这个方法不推荐使用)。
    3)使用interrupt(因特R 普特)方法中断线程。
  11. 讲一下java中的同步的方法
    1)同步方法.synchronized.
    2)同步代码块.synchronized.
    3)特殊域变量,volatile,注意不能修饰final的变量.
    a.volatile关键字为域变量的访问提供了一种免锁机制
    b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
    c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
    d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
  12. 数据一致性如何保证?
  13. 如何保证线程安全?
  14. 如何实现线程同步?
    synchronized关键字和锁
    a)Lock使用起来比较灵活,但需要手动释放和开启;采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;
    b)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;
    c)在并发量比较小的情况下,使用synchronized是个不错的选择,但是在并发量比较高的情况下,其性能下降很严重,此时Lock是个不错的方案。
    d)使用Lock的时候,等待/通知 是使用的Condition对象的await()/signal()/signalAll() ,而使用synchronized的时候,则是对象的wait()/notify()/notifyAll();由此可以看出,使用Lock的时候,粒度更细了,一个Lock可以对应多个Condition。
    e)虽然Lock缺少了synchronized隐式获取释放锁的便捷性,但是却拥有了锁获取与是释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized所不具备的同步特性;
  15. 两个进程同时要求写或者读,能不能实现?如何防止进程的同步?
  16. 线程间操作List
  17. Java中对象的生命周期
    1)创建阶段(Created)
    2)应用阶段(In Use)
    3)不可见阶段(Invisible)
    4)不可达阶段(Unreachable)
    5)收集阶段(Collected)
    6)终结阶段(Finalized)
    7)对象空间重分配阶段(De-allocated)
  18. Synchronized用法
    1)修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
    2)修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  19. synchronize的原理
    synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性
    谈谈对Synchronized关键字,类锁,方法锁,重入锁的理解
    static synchronized 方法的多线程访问和作用
    我们知道,当synchronized修饰一个static方法时,多线程下,获取的是类锁(即Class本身,注意:不是实例);
    当synchronized修饰一个非static方法时,多线程下,获取的是对象锁(即类的实例对象)
    同一个类里面两个synchronized方法,两个线程同时访问的问题
  20. volatile的原理
    将当前处理器缓存行的数据会写回到系统内存。
    这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效
  21. 谈谈volatile关键字的用法
    一般用于变量的修饰
  22. 谈谈volatile关键字的作用
    特殊域变量,volatile,注意不能修饰final的变量.
    a)volatile关键字为域变量的访问提供了一种免锁机制
    b)使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
    c)因此每次使用该域就要重新计算,而不是使用寄存器中的值
    d)volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
  23. 谈谈NIO的理解
    最近大概看了ZooKeeper和Mina的源码发现都是用Java NIO实现的,所以有必要搞清楚什么是NIO。下面是我结合网络资料自己总结的,为了节约时间图示随便画的,能达意就行。
    简介:
    BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
    NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
    AIO(NIO.2):异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
    BIO
    同步阻塞式IO,相信每一个学习过操作系统网络编程或者任何语言的网络编程的人都很熟悉,在while循环中服务端会调用accept方法等待接收客户端的连接请求,一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成。
    如果BIO要能够同时处理多个客户端请求,就必须使用多线程,即每次accept阻塞等待来自客户端请求,一旦受到连接请求就建立通信套接字同时开启一个新的线程来处理这个套接字的数据读写请求,然后立刻又继续accept等待其他客户端连接请求,即为每一个客户端连接请求都创建一个线程来单独处理,大概原理图就像这样:
    Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第2张图片
    NIO
    同步非阻塞式IO,关键是采用了事件驱动的思想来实现了一个多路转换器。
    NIO与BIO最大的区别就是只需要开启一个线程就可以处理来自多个客户端的IO事件,这是怎么做到的呢?
    就是多路复用器,可以监听来自多个客户端的IO事件:
    A. 若服务端监听到客户端连接请求,便为其建立通信套接字(java中就是通道),然后返回继续监听,若同时有多个客户端连接请求到来也可以全部收到,依次为它们都建立通信套接字。
    B. 若服务端监听到来自已经创建了通信套接字的客户端发送来的数据,就会调用对应接口处理接收到的数据,若同时有多个客户端发来数据也可以依次进行处理。
    C. 监听多个客户端的连接请求和接收数据请求同时还能监听自己时候有数据要发送。
    Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第3张图片
    总之就是在一个线程中就可以调用多路复用接口(java中是select)阻塞同时监听来自多个客户端的IO请求,一旦有收到IO请求就调用对应函数处理。
    各自应用场景
    到这里你也许已经发现,一旦有请求到来(不管是几个同时到还是只有一个到),都会调用对应IO处理函数处理,所以:
    (1)NIO适合处理连接数目特别多,但是连接比较短(轻操作)的场景,Jetty,Mina,ZooKeeper等都是基于java nio实现。
    (2)BIO方式适用于连接数目比较小且固定的场景,这种方式对服务器资源要求比较高,并发局限于应用中。
  24. synchronized 和volatile 关键字的区别
  25. synchronized与Lock的区别
  26. ReentrantLock 、synchronized和volatile比较
  27. ReentrantLock的内部实现
  28. lock原理
    需要实现锁的功能,两个必备元素,一个是表示(锁)状态的变量(我们假设0表示没有线程获取锁,1表示已有线程占有锁),另一个是队列,队列中的节点表示因未能获取锁而阻塞的线程。为了解决多核处理器下多线程缓存不一致的问题,表示状态的变量必须声明为voaltile类型,并且对表示状态的变量和队列的某些操作要保证原子性和可见性
  29. Lock与synchronized的区别
    1)Lock的加锁和解锁都是由java代码配合native方法(调用操作系统的相关方法)实现的,而synchronize的加锁和解锁的过程是由JVM管理的
    2) 当一个线程使用synchronize获取锁时,若锁被其他线程占用着,那么当前只能被阻塞,直到成功获取锁。而Lock则提供超时锁和可中断等更加灵活的方式,在未能获取锁的 条件下提供一种退出的机制。
    3) 一个锁内部可以有多个Condition实例,即有多路条件队列,而synchronize只有一路条件队列;同样Condition也提供灵活的阻塞方式,在未获得通知之前可以通过中断线程以 及设置等待时限等方式退出条件队列。
    4) synchronize对线程的同步仅提供独占模式,而Lock即可以提供独占模式,也可以提供共享模式
  30. 死锁的四个必要条件?
    1) 互斥条件:一个资源每次只能被一个进程使用。
    2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
    4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之
    一不满足,就不会发生死锁。
  31. 怎么避免死锁?
    打破上述3种情况同时出现的:避免同一时刻持有两把锁,在可能的情况下改为先持有A,释放后再申请B。如果上述情况无法避免,确保会被同时持有的锁的申请顺序和释放顺序在任何地方都一致。允许出现死锁,当检测到死锁后触发解锁机制:例如申请到A后,再申请B,500ms后发现申请失败,回头去释放A,解除死锁。避免使用锁,
  32. 对象锁和类锁是否会互相影响?
    1.类锁和对象锁不是同1个东西,一个是类的Class对象的锁,一个是类的实例的锁。也就是说:1个线程访问静态synchronized的时候,允许另一个线程访问对象的实例synchronized方法。反过来也是成立的,因为他们需要的锁是不同的。
    对象锁:Java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。
    类锁:对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。我们都知道,java类可能会有很多个对象,但是只有1个Class对象,也就是说类的不同实例之间共享该类的Class对象。Class对象其实也仅仅是1个java对象,只不过有点特殊而已。由于每个java对象都有1个互斥锁,而类的静态方法是需要Class对象。所以所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是MyClass.class的方式。
  33. 什么是线程池,如何使用?
    多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
    如果:创建线程时间+ 销毁线程时间 >远大在线程中执行任务的时间,则可以采用线程池,以提高服务器性能。
    一个线程池包括以下四个基本组成部分:
    1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
    2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
    3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
    4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
  34. Java的并发、多线程、线程模型
    1)之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程。虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一定的时间片运行。
    2)多线程比多任务更加有挑战。多线程是在同一个程序内部并行执行,因此会对相同的内存空间进行并发读写操作。这可能是在单线程程序中从来不会遇到的问题。然而,更现代的计算机伴随着多核CPU的出现,也就意味着不同的线程能被不同的CPU核得到真正意义的并行执行。
  35. 谈谈对多线程的理解
    这里面至少存在两条程序,一条是主线程main,另外一条就是垃圾回收线程,即JVM一遍”执行”main,”一边”也在执行垃圾回收机制,所以说java支持多线程,而多线程的意义它可以让程序在一个时间段执行多个事情,提高了应用程序的使用率;
    比如上面那个例子,如果是单线程,那么JVM不得不执行一会main线程然后停下来去执行垃圾回收机制
  36. 多线程有什么要注意的问题?
    1.线程使用中要注意,如何控制线程的调度和阻塞,例如利用事件的触发来控制线程的调度,也有用消息来控制的。
    2.线程中如果用到公共资源,一定要考虑公共资源的线程安全性。一般用LOCK锁机制来控制线程安全性。一定要保证不要有死锁机制。
  37. 谈谈你对并发编程的理解并举例说明
    可以说多线程编程带来的影响有利有弊,好处自然是提高处理器的利用率,加快任务执行速度,弊端是线程安全问题。
    1)如果存在可变的成员变量,不论是基础数据类型还是引用类型,那么都是需要关注它的线程安全问题
    2)如果存在并发写的情况,那么修改它的时候需要加锁。局部变量一般情况下访问是不需要加锁的,因为它是栈封闭的,多线程不会并发访问到。
    局部变量如果是基础数据类型,而非引用类型,那么一定线程安全的,因为传参是使用拷贝的方式,修改它不会影响成员变量。
  38. 谈谈你对多线程同步机制的理解?
    多线程技术被设计出来是为了充分利用多核cpu的优势,让cpu得到充分利用。
    但多线程有一个问题:线程之间是可以共享资源的,如果多个进程都在读写同一个资源,就会出现问题,最简单的就是加锁!保证线程的安全性,保持数据的同步。
  39. 如何保证多线程读写文件的安全?
    同步锁,保证线程的安全性和数据同步!基本上多线程的原理就是这样,其实也很简单
  40. 多线程断点续传原理断点续传的实现
    那么我们接着说断点续传,断点续传其实也很简单,原理就是使用数据库保存上次每个线程下载的位置和长度
    例如我开了两个线程T1,T2来下载一个文件,设文件总大小为1024M,那么就是每个线程下载512M
    可是我的下载中断了,那么我下次启动线程的时候(继续下载),是不是应该要知道,我原来下载了多少呢
    所以是这样的,我每下载一点,就更新数据库的数据,
    例如T1,下载了100M,就要实时更新数据库,记录下100M,并且记录下这个线程开始下载位置(startPos),还有线程负责的长度(512M)
    那么我继续下载的时候,就可以像服务器请求startPos+1000M开始的数据了,然后在文件里面也是seek(startPos+1000M)的位置继续下载,就可以实现断点续传了
    继续读出,把文件返回给客户端。 当然为了下载的更快一下,也可以多线程下载,那么基本实现就是给每个线程分配固定的字节的文件,分别去读

(五)并发编程有关知识点

(这个是一般Android开发用的少的,所以建议多去看看):
平时Android开发中对并发编程可以做得比较少,Thread这个类经常会用到,但是我们想提升自己的话,一定不能停留在表面,,我们也应该去了解一下java的关于线程相关的源码级别的东西。
学习的参考资料如下:
Java 内存模型
java线程安全总结
http://www.iteye.com/topic/806990
深入理解java内存模型系列文章
http://ifeve.com/java-memory-model-0/
线程状态:
一张图让你看懂JAVA线程间的状态转换
https://my.oschina.net/mingdongcheng/blog/139263
锁:
锁机制:synchronized、Lock、Condition
http://blog.csdn.net/vking_wang/article/details/9952063
Java 中的锁
http://wiki.jikexueyuan.com/project/java-concurrent/locks-in-java.html
并发编程:
Java并发编程:Thread类的使用
http://www.cnblogs.com/dolphin0520/p/3920357.html
Java多线程编程总结
http://blog.51cto.com/lavasoft/27069
Java并发编程的总结与思考
https://www.jianshu.com/p/053943a425c3#
Java并发编程实战—–synchronized
http://www.cnblogs.com/chenssy/p/4701027.html
深入分析ConcurrentHashMap
http://www.infoq.com/cn/articles/ConcurrentHashMap#


Android面试题
Android面试题包括Android基础,还有一些源码级别的、原理这些等。所以想去大公司面试,一定要多看看源码和实现方式,常用框架可以试试自己能不能手写实现一下,锻炼一下自己。
(一)Android基础知识点

  1. 四大组件是什么
    Activity、Service、Broadcast Receiver、Content Provider
  2. 四大组件的生命周期和简单用法
    1)Activity
    Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第4张图片
    2)Service
    Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第5张图片
    3)Broadcast Receiver
    广播分为动态注册静态注册,主要是负责时间消息的通知,举个简单的例子,当手机wifi打开的时候,会有广播通知,这个时候我们可以做权限的申请。
    (1)动态注册 使用IntentFilter
    MyReceiver myReceiver = new MyReceiver();
    IntentFilter intentFilter =new IntentFilter();
    intentFilter.addAction(“com.test”);
    registerReceiver(myReceiver,intentFilter);
    注意:动态注册方式的BroadcastReceiver,生命周期仅限于当前注册的activity,离开activity一定要解除注册,否则就会抛出非常熟悉的错误,但是这个错误不会导致app崩溃。
    (2)静态注册在AndroidManifest.xml文件中进行配置:
    静态注册的BroadcastReceiver,生命周期不仅局限于activity,对比动态注册,进行了测试,发现activity关闭与否,不受影响,即使app退出了还是会收到广播,意思就是说如何使通过标签进行静态注册receiver,会在执行完onReceive方法后任意时间段内进行销毁,所以我们不用手动进行取消注册操作。
    4)Content Provider
    ContentProvider作为Android四大组件之一,并没有Activity那样复杂的生命周期,只有简单地onCreate过程。ContentProvider是一个抽象类,当实现自己的ContentProvider类,只需继承于ContentProvider,
    。对于数据的使用者来说,无需知晓数据的来源是数据库、文件,或者网络,只需简单地使用ContentProvider提供的数据操作接口,也就是增(insert)、删(delete)、改(update)、查(query)四个过程。
  3. Activity之间的通信方式
    1)Intent
    2)借助类的静态变量
    3)借助全局变量/Application
    3)借助外部工具
    – 借助SharedPreference
    – 使用Android数据库SQLite
    – 赤裸裸的使用File
    4)借助Service
  4. Activity各种情况下的生命周期
    横竖屏切换的时候,Activity 各种情况下的生命周期
    刚刚启动Activity的时候:
    Activity1—–>onCreate
    Activity1—–>onStart
    Activity1—–>onResume
    由竖屏切换到横屏(由横屏切换到竖屏):
    Activity1—–>onPause
    Activity1—–>onSaveInstanceState
    Activity1—–>onStop
    Activity1—–>onDestroy
    Activity1—–>onCreate
    Activity1—–>onStart
    Activity1—–>onRestoreInstanceState
    Activity1—–>onResume
  5. Activity与Fragment之间生命周期比较
    Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第6张图片
  6. 两个Activity 之间跳转时必然会执行的是哪几个方法?
    一般情况下比如说有两个activity,分别叫A,B。
    当在A 里面激活B 组件的时候, A会调用onPause()方法,然后B调用onCreate() ,onStart(), onResume()。
    这个时候B覆盖了A的窗体, A会调用onStop()方法。
    如果B是个透明的窗口,或者是对话框的样式, 就不会调用A的onStop()方法。
    如果B已经存在于Activity栈中,B就不会调用onCreate()方法。
    前台切换到后台,然后再回到前台,Activity生命周期回调方法。弹出Dialog,生命值周期回调方法。
    前后台切换(onPause—>onStop ===> onStart–>onResume)
    弹出Dialog(onPause ===> onResume)
  7. Activity的四种启动模式对比
    Standard、singleTop、singleTask、singleInstance
    1)Standard—标准
    这个模式是默认的启动模式,即标准模式,在不指定启动模式的前提下,系统默认使用该模式启动Activity,每次启动一个Activity都会重写创建一个新的实例,不管这个实例存不存在,
    2)singleTop–栈顶复用模式
    这个模式下,如果新的activity已经位于栈顶,那么这个Activity不会被重写创建,同时它的onNewIntent方法会被调用,通过此方法的参数我们可以去除当前请求的信息。如果栈顶不存在该Activity的实例,则情况与standard模式相同。需要注意的是这个Activity它的onCreate(),onStart()方法不会被调用,因为它并没有发生改变。
    1.当前栈中已有该Activity的实例并且该实例位于栈顶时,不会新建实例,而是复用栈顶的实例,并且会将Intent对象传入,回调onNewIntent方法
    2.当前栈中已有该Activity的实例但是该实例不在栈顶时,其行为和standard启动模式一样,依然会创建一个新的实例
    3.当前栈中不存在该Activity的实例时,其行为同standard启动模式
    3)singleTask
    这个模式十分复杂,有各式各样的组合。在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,如果这个任务栈不存在,则会创建这个任务栈。
    4)singleInstance
    该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
    我们看到,第一个应用启动SingleInstanceActivity时,由于系统中不存在该实例,所以新建了一个Task,按home键后,使用另一个App进入该Activity,由于系统中已经存在了一个实例,不会再创建新的Task,直接复用该实例,并且回调onNewIntent方法。
  8. Activity状态保存于恢复
    什么时候调用Activity的onSaveInstanceState()
    屏幕旋转重建会调用onSaveInstanceState()
    启动另一个activity: 当前activity在离开前会调用onSaveInstanceState()
    当前activity在离开前会onSaveInstanceState()
    用户主动finish()掉的activity不会调用onSaveInstanceState(), 包括主动按back退出的情况.
    新建的activity, 从onCreate()开始, 不会调用onRestoreInstanceState().
  9. fragment各种情况下的生命周期
  10. Fragment状态保存startActivityForResult是哪个类的方法,在什么情况下使用?
    Activity类中,如果想在Activity中得到新打开Activity 关闭后返回的数据,需要使用系统提供的startActivityForResult
  11. 如何实现Fragment的滑动?
    一个是ViewPager 另一个是onFling手势滑动 GestureDetector(杰斯特带可特)手势滑动识别
  12. fragment之间传递数据的方式?
    1)最简单的就是全局参数,在一个Activity下,多个fragment的add 和hide ,使用Activity 的成员变量。
    2)可以使用bundle进行参数传递,这样在两个Fragment跳转的时候就可以带上参数了
    3)在A Fragment 中设置一个 setData,在B Fragmen中,获取A fragment getActivity()
    4)采取接口回调的方式进行数据传递。
    5)使用EventBus 三方开源框架.
  13. Activity 怎么和Service 绑定?
    通过Intent(”david.bindService”),并通过BingService 传入 Intent ,ServiceConnection,和BIND_AUTO_CREATE
    MyBinder binder = (MyBinder)service; //通过IBinder获取Service
    musicService = binder.getService();
    if(musicService != null){ musicService.play(); }
    怎么在Activity 中启动自己对应的Service?
    绑定成功以后,Service会将代理对象通过回调的形式传递给MyServiceConnection,这样我们就获取Service提供的代理对象
    service和activity怎么进行数据交互?
    startService()、bindService()。第一种方法我们直接开启Service进行使用,没有与它进行交互处理。所以我们只有通过bindService()方法然后借助Binder进行数据交互。
    在onServiceConnected 中获取 Service的实例,我们就可以直接访问 Service的的内部方法了。
  14. Service的开启方式
    第一种方式:通过StartService启动Service
    通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。
    要创建一个这样的Service,你需要让该类继承Service类,然后重写以下方法:
    onCreate()
    1.如果service没被创建过,调用startService()后会执行onCreate()回调;
    2.如果service已处于运行中,调用startService()不会执行onCreate()方法。
    也就是说,onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate(),此方法适合完成一些初始化工作。
    第二种方式:通过bindService启动Service
  15. bindService启动服务特点?
    1)bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
    2)client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
    3)bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁
  16. 请描述一下Service 的生命周期
    Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第7张图片
    Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第8张图片
  17. 谈谈你对ContentProvider的理解
    ContentProvider为存储和获取数据提供统一的接口,可以在不同应用程序之间共享数据。
    ContentProvider主要有以下优点:
    1)ContentProvider提供了对底层数据存储方式的抽象。如底层可以采用SQLite方式存储数据,使用ContentProvider封装之后,即便底层换成XML存储也不会对上层应用代码产生影响。
    2)ContentProvider为应用间的数据交互提供了一个安全的环境。它准许你把自己的应用数据根据需求开放给其它应用进行增、删、改、查,而不用担心直接开放数据库权限而带来的安全问题。
  18. 说说ContentProvider、ContentResolver、ContentObserver 之间的关系
  19. 请描述一下广播BroadcastReceiver的理解
    广播(Broadcast)是用来在进程的不同组件之间通信的,起到通知和传递数据的作用。当然也可以使用广播在不同的进程之间进行通信,广播接受者(BroadcastReceiver)是android中的四大组件之一(Activity、Service、Broadcast、Contentprovider),一旦被启动之后就可以在后台监听广播事件,当然对于已经用不到的广播事件我们也可以调用unregisterReceiver(receiver)方法来注销广播,被注销的广播就不能在重新注册了,因为注销之后所占用的内存就会被回收了。
  20. 广播的分类
    动态注册 || 静态注册
    按是否有序:
    1.有序广播,如短信,电话。 android:priority = “1000” (-1000 ~ 1000) 由高到低 可以拦截广播。2.无序广播,无优先级。
  21. 广播使用的方式和场景
    1)同一app内部的同一组件内的消息通信(单个或多个线程之间);
    2)同一app内部的不同组件之间的消息通信(单个进程);
    3)同一app具有多个进程的不同组件之间的消息通信;
    4)不同app之间的组件之间消息通信;
    5)Android系统在特定情况下与App之间的消息通信。
  22. 在manifest 和代码中如何注册和使用BroadcastReceiver?
    1)在代码中注册: context.registerReceiver(receriver,filter);
    2)在AndroidManifest中注册,使用这种方式注册时,即使程序已经退出,广播依然会响应(因为会被操作系统调用)。
    在AndroidManifest中注册时,可以通过指定exported属性,来控制该广播接收器是否可以接受来自其它应用程序的广播,
  23. 本地广播和全局广播有什么差别?
    1)发送的广播只会在自己App内传播,不会泄露给其他App,确保隐私数据不会泄露
    2)其他App也无法向你的App发送该广播,不用担心其他App会来搞破坏
    3)比系统全局广播更加高效
  24. BroadcastReceiver,LocalBroadcastReceiver 区别
  25. AlertDialog,popupWindow,Activity区别
    AlertDialog是非阻塞式对话框:AlertDialog弹出时,后台还可以做事情;
    而PopupWindow是阻塞式对话框:PopupWindow弹出时,程序会等待,在PopupWindow退出前,程序一直等待,只有当我们调用了dismiss方法的后,PopupWindow退出,程序才会向下执行。这两种区别的表现是:AlertDialog弹出时,背景是黑色的,但是当我们点击背景,AlertDialog会消失,证明程序不仅响应AlertDialog的操作,还响应其他操作,其他程序没有被阻塞,这说明了AlertDialog是非阻塞式对话框;PopupWindow弹出时,背景没有什么变化,但是当我们点击背景的时候,程序没有响应,只允许我们操作PopupWindow,其他操作被阻塞。
  26. Application 和 Activity 的 Context 对象的区别
    这是两种不同的context,也是最常见的两种.第一种中context的生命周期与Application的生命周期相关的,context随着Application的销毁而销毁,伴随application的一生,与activity的生命周期无关.第二种中的context跟Activity的生命周期是相关的,但是对一个Application来说,Activity可以销毁几次,那么属于Activity的context就会销毁多次.至于用哪种context,得看应用场景,
  27. Android属性动画特性
    1)这里我举一个简单的例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了
    2)就是它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?
    3)最后,补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。
  28. 如何导入外部数据库?
    通过assets,获取到相应的资源文件,并通过流读出数据
  29. LinearLayout、RelativeLayout、FrameLayout的特性及对比,并介绍使用场景。
    1)LinearLayout又称作线性布局,是一种非常常用的布局。
    正如名字所描述的一样,这个布局会将它所包含的控件在线性方向上依次排列。
    2)FrameLayout帧布局是最简单的布局之一,采用帧布局的容器中无论放入多少个控件,控件默认情况下左上角都对齐到容器的左上角,如果控件一样大,同一时间只能见到最上面的。
  30. 谈谈对接口与回调的理解
    回调函数的个人理解:你使用别人的类的方法,但是别人的函数却要调用你的代码。
    其实回调函数并没有想象中的那么神秘。举个例子:别人已经写好的类的方法需要调用你实现的代码,该怎么调?
    答:别人在设计的时候就只需要给你一个接口,你在使用别人的代码的时候,只需要将固定接口的子类对象 传给那份代码,就可以完成功能。
  31. 介绍下SurfView
    SurfaceView与普通的View的区别就是View视图必须在当前UI线程中进行,这也是在更新View组件时总要采用Handler处理的原因;但SurfeceView不会存在这个问题,它的绘图是由SurfaceHolder来完成的,SurfaceHolder会启动新的线程去更新SurfaceView的绘制,不会阻塞主UI线程。一般而言,如果程序中或游戏界面中的动画元素较多,而且很多动画元素都需要通过定时器来控制,就可以考虑使用SurfaceView而不是View。
    glSurfaceView.getHolder().addCallback(this);
  32. RecycleView的使用
    RecyclerView 是一个增强版的ListView,不仅可以实现和ListView同样的效果,还优化了ListView中存在的各种不足之处,还可以GridView
    ResyslerView 能够实现横向滚动,这是ListView所不能实现的
  33. 序列化的作用,以及Android两种序列化的区别
  34. 差值器
  35. 估值器
  36. Android中数据存储方式
      Android提供了5种方式来让用户保存持久化应用程序数据。根据自己的需求来做选择,比如数据是否是应用程序私有的,是否能被其他程序访问,需要多少数据存储空间等,分别是:   
    ① 使用SharedPreferences存储数据 
    ② 文件存储数据
    ③  SQLite数据库存储数据
    ④ 使用ContentProvider存储数据
    ⑤ 网络存储数据 

(二)Android源码相关分析

Android动画框架实现原理
Android各个版本API的区别
Requestlayout,onlayout,onDraw,DrawChild区别与联系
invalidate和postInvalidate的区别及使用
Activity-Window-View三者的差别
谈谈对Volley的理解

  1. 如何优化自定义View
    1)降低刷新频率
    2)使用硬件加速
    3)初始化时创建对象;不要在onDraw方法内创建绘制对象,一般都在构造函数里面初始化对象;
    4)状态的存储与恢复
  2. 低版本SDK如何实现高版本api?
  3. 描述一次网络请求的流程
    通过url获取连接,设置请求方式,设置连接超时,获取响应码,针对不同的响应码,做不同的操作,成功根据流获取字符串,并做响应的处理。
  4. HttpUrlConnection 和 okhttp关系以及Volley
    虽然这三个都是可以用来访问网络的,但是还是不同的。我们最熟悉的肯定是HttpUrlConnection,这是google官方提供的用来访问网络,但是HttpUrlConnection实现的比较简单,只支持1.0/1.1,并没有上面讲的多路复用,如果碰到app大量网络请求的时候,性能比较差,而且HttpUrlConnection底层也是用Socket来实现的。而Volley是一个开源库,它只是封装了访问网络的一些操作,但是底层还是使用HttpUrlConnection。但是OkHttp不同,在看源码之前,一直以为OkHttp和Volley一样,是用HttpUrlConnection,但是找了半天代码,都没看到HttpUrlConnection和InputStream(OutputStream),倒是反而看到socket和sink(source)这些,后来才搞明白,原来OkHttp像HttpUrlConnection一样,实现了一个网络连接的过程。所以按照层级来说,OkHttp和HttpUrlConnection是一级的,用socket实现了网络连接,只是OkHttp更强大,而Volley只是一个引用了HttpUrlConnection,它并不关心网络连接过程,只是封装了请求的过程而已。刚刚提到HttpUrlConnection在IO方面用到的是InputStream和OutputStream,但是OkHttp用的是sink和source,这两个是在Okio这个开源库里的,sink相当于outputStream,source相当于是inputStream。sink和source比InputStream和OutputStream更加强大,单拿sink举例,他的子类有BufferedSink(支持缓冲)、GzipSink(支持Gzip压缩)、ForwardingSink和InflaterSink(后面这两者服务于GzipSink),source对应的也有,具体的可以自行上网查找。
  5. Bitmap对象的理解
  6. looper架构
  7. ActivityThread,AMS,WMS的工作原理
  8. View的绘制流程。
    View的绘制是从ViewRoot的performTraversals()方法开始的,经过measure,layout,draw这个3大步骤。
    1)measure的过程:
    measure过程是对整个view树的所有控件计算宽高
    measure是冲ViewRoot类中的host.measure开始的,内部调用的是View的measure(int widthMeasureSpec,int heightMeasureSpec)方法,measure方法里面调用了onMeasure(int widthMeasureSpec,int heightMeasureSpec)方法,方法中的两个参数都是是MeasureSpec类型(指父控件对子控件宽高的期望值,它是一个32位的int类型数,前两位表示测量模式,后30位表示测量大小)
    测量模式一共有3种:
    1)EXACTLY 精确测量模式,xml文件中写200dp,march_parent等代表使用该模式,
    2)AT_MOST 最大模式,xml文件中写wrap_content表示使用该模式。
    3)UNSPECIFIED 无限大测量模式,只有在绘制特定自定义View时才用的到这个模式。
    真正代表测量结束的方法是setMeasuredDimension方法。
    自定义View如果在xml中使用了wrap_content属性,就需要重写onMeasure方法来设置wrap_content的默认大小,不然会显示出match_parent的效果。
    2)layout的过程:
    ViewGroup用来将子View放在合适的位置上。
    layout是从ViewRoot类中的host.layout开始的,内部调用的是ViewGroup的layout方法。在ViewGroup的layout方法中,先调用setFrame来确定自己的左上右下的位置,再调用onLayout来确定子View的位置。
    自定义ViewGroup一定要重写layout方法来确定子View的位置,自定义View一般不需要重写该方法,它的位置是右父控件确定的。
    3)draw过程:
    此过程是真正将内容展示在屏幕上让我们能够看到的过程。
    draw是从ViewRoot类中的host.draw开始的,内部调用的是View的draw方法。
    draw的步骤:
    1)绘制背景。
    2)绘制内容,也就是调用onDraw方法。
    3)绘制子View,调用的是dispatchDraw方法。
    4)绘制装饰,如listview的滚动条等。
    对于View的绘制过程,既可以说是简单的,也可以说是复杂的,简单的在于Google已经帮我们将draw框架写好了,我们在自定义ViewGroup时不用管draw过程,只需要实现measure和layout过程。复杂在于,我们写继承View的自定义控件的时候需要重写onDraw方法,这样才能绘制出你自定义的View的内容,onDraw(Canvas canvas)方法中最重要的两个东西是Paint和Canvas,这个使用起来算是比较复杂的。
  9. 自定义View如何考虑机型适配
    自定义View的事件
    这里要考虑的是屏幕的问题:
    合理使用warp_content,match_parent.
    尽可能的是使用RelativeLayout
    针对不同的机型,使用不同的布局文件放在对应的目录下,android会自动匹配。
    尽量使用点9图片。
    使用与密度无关的像素单位dp,sp
    引入android的百分比布局。
    切图的时候切大分辨率的图,应用到布局当中。在小分辨率的手机上也会有很好的显示效果。
  10. Touch事件分发机制
    一个完整的touch事件,由一个down事件,多个move事件,一个up事件组成。
    Touch事件的一般传递流程Activity–>window(唯一实现类PhoneWindow)–>顶级View(DecorView)–>ViewGroup–>View
    监听Touch事件有两种方式,setOnTouchListener和直接重写三个方法:dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent。
    使用setOnTouchListenre方式设置监听事件,该方式的优先级较高,如果在onTouchListener的onTouch方法中return true的话,那么onTouchEvent方法是接收不到该Touch事件的。而且因为onClickListener中的onClick方法实际上是在onTouchEvent中被调用的,所以Touch事件走不到onTouchEvent,Touch事件就不会生效。
    (我的理解)直接重写三个方法:
    dispatchTouchEvent:该方法是对点击事件的分发,在这个方法中我们一般return super.dispatchTouchEvent,讲该事件分发下去。
    onInterceptTouchEvent:该方法表示对Touch事件进行拦截,这是ViewGroup特有的方法,View没有。在ViewGroup中如果onInterceptTouchEvent返回true,表示将该事件拦截,那么事件将传递给ViewGroup的onTouchEvent方法处理。如果onInterceptTouchEvent方法返回true,事件将传递个子View的dispatchTouchEvent进行分发。
    onTouchEvent:该方法表示对Touch事件进行消费,返回true表现消费,返回false表示不消费,那么该事件该事件将传递给父控件的onTouchEvent处理。
  11. AstncTask+HttpClient 与 AsyncHttpClient有什么区别?
  12. LaunchMode应用场景
  13. AsyncTask 出现的原因?
    当一个Android应用初次启动时,Android会自动创建一个主线程,这个主线程就是传说中的非常重要的UI线程,之所以说它重要,是因为它负责把事件分派给相应的控件(其中就包括屏幕绘图事件),同样,用户和控件的交互也是由它完成的。
    但是因为所有的任务都是在同一线程中完成的,如果执行一些耗时的操作,如访问网络或查询数据库,不能及时的分发事件,导致阻塞用户界面的绘制。从用户的角度看,应用好像挂掉了,更糟的是,如果阻塞应用程序的时间过长(现在大概是5秒钟)Android会向用户提示一些信息,即 “应用程序没有响应”的对话框。
    如何解决这个问题?
    我们用的最多的办法就是Handler + Thread 的方式,即:
    1) 新开一个线程,在该线程中执行比较耗时的操作。
    2) 耗时操作完成后,用Handler向UI线程发送消息,通知主线程更新UI。
    这种方式虽然简单,但是缺点也是显而易见的,比如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制;比如耗时的操作太多,那么我们需要开启太多的子线程,这就会给系统带来巨大的负担,随之也会带来性能方面的问题。
  14. AsyncTask 如何使用?
    1) AsyncTask对线程间的通讯做了包装,使后台线程和UI线程可以简易通讯:后台线程执行异步任务,将result告知UI线程。
    2)使用方法:定义一个继承自AsyncTask的方法,把比较耗时的操作放在doInBackground中完成,onPostExecute方法中可以完成对UI的更新。
    3)onPreExecute(), doInBackground(Params… params),onProgressUpdate(Params… values),
    onPostExecute(Result result) ,execute(Params… params)—-执行一个异步任务。
  15. 请介绍下ContentProvider 是如何实现数据共享的?
    Android提供了ContentProvider,一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProviders是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。外部访问通过ContentResolver去访问并操作这些被暴露的数据
  16. AndroidService与Activity之间通信的几种方式
    1)方式一:通过Binder,客户端Activity必须创建一个长连接ServiceConnection的实例并传给bindService(),ServiceConnection包含一个回调方法,系统调用这个方法来传递要返回的IBinder。
    2)通过Broadcast广播
    3)自定义接口回调
  17. IntentService原理及作用是什么?
    在Android开发中,我们或许会碰到这么一种业务需求,一项任务分成几个子任务,子任务按顺序先后执行,子任务全部执行完后,这项任务才算成功。那么,利用几个子线程顺序执行是可以达到这个目的的,但是每个线程必须去手动控制,而且得在一个子线程执行完后,再开启另一个子线程。或者,全部放到一个线程中让其顺序执行。这样都可以做到,但是,如果这是一个后台任务,就得放到Service里面,由于Service和Activity是同级的,所以,要执行耗时任务,就得在Service里面开子线程来执行,说白了,service和Activity 差不多,操作方法,只是一个是可以负责前台的显示,一个后台进行工作(都不能执行耗时操作,他们的区别大部分仅限于可见和不可见)。那么,有没有一种简单的方法来处理这个过程呢,答案就是IntentService。
    IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
    所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。那么,用IntentService有什么好处呢?首先,我们省去了在Service中手动开线程的麻烦,第二,当操作完成时,我们不用手动停止Service,第三,it’s so easy to use!
  18. 说说Activity、Intent、Service 是什么关系
    1)一个 Activity 通常是一个单独的屏幕,
    每一个 Activity 都被实现为一个单独的类,
    这些类都 是从 Activity 基类中继承来的,
    Activity 类会显示由视图控件组成的用户接口,
    并对视图控 件的事件做出响应。
    2)Intent 的调用是用来进行架构屏幕之间的切换的。
    Intent 是描述应用想要做什么。
    Intent 数 据结构中两个最重要的部分是动作和动作 对应的数据,
    一个动作对应一个动作数据。
    3)Service 是运行在后台的代码,
    不能与用户交互,可以运行在自己的进程,也可以 运行在其他应用程序进程的上下文里。
    需要通过某一个 Activity 或者其他 Context 对象来调 用。
    Activity 跳转到 Activity,Activity 启动 Service,Service 打开 Activity
    都需要 Intent 表明跳转 的意图,以及传递参数,Intent 是这些组件间信号传递的承载者。
    ApplicationContext和ActivityContext的区别
    SP是进程同步的吗?有什么方法做到同步?
  19. 谈谈多线程在Android中的使用
    一)handler的理解
    优点:对于对后台任务时,简单清晰
    缺点:对于操作单个后台任务,代码过于繁琐
    具体操作:
    在主线程中创建Handler对象并实现handlmessage()方法,
    创建runnable线程,先在线程中执行耗时操作,
    开启一个线程会相应的产生一个looper,在初始化looper的时候会创建一个消息队列MessageQueue();
    执行完耗时操作,通过handler将消息发送到消息队列中、、looper轮询消息队列将消息取出来交给Handler,
    Handler接收到取出来的消息,并根据消息类型做出相应的处理
    二)AsyncTask的理解
    优点:操作简单方便,过程可控
    缺点:对于多异步操作更新UI会变得很繁琐
    具体操作:
    onPreExecute()运行在主线程中,开启线程前的准备操作,
    doInBackground()运行在子线程中,
    onPreExecute()之后的操作,用于处理耗时操作,通过调用publishProcess()向 onProcessUpdata()推送消息
    onProcessUpdata()运行在主线程中,当调用 publishProcess()方法时就会开启此方法,接收到推送过来的数据,更新UI进度页面
    onPostExecute()运行在主线程中,当子线程耗时操作执行完毕后会调用此方法, doInBackground()返回的参数传递到这里来用于更新UI
    调用execute()方法开启AsyncTask,类似runnable的start()方法
    三)IntentService的理解
    IntentService和普通的Service区别在于,IntentService在oncreate()方法中单独开启一个线程用于耗时操作
    通过onHandleIntent(Intent intent)方法来处理耗时操作
    在耗时操作执行完毕之后,会自动关闭service不用手动关闭
    如果同时new出多个IntentService对象进行耗时操作,oncreate()和ondestory()方法会执行一次,onstart()、onstartcommand()、onHandleIntent()会执行多次。
    执行完毕自动关闭service
  20. IntentService和Service的区别?
    一:简单描述一下Service
    Service是用于后台服务的,当应用程序被挂到后台的时候,为了保证应用某些组件仍然可以工作而引入了Service这个概念。
    那么这里面要强调的是Service不是独立的进程,也不是独立的线程,它是依赖于应用程序的主线程的,也就是说,在更多时候不建议在Service中编写耗时的逻辑和操作,否则会引起ANR,为了解决这样的问题,引入了IntentService
    二:简单描述一下IntentService
    IntentService是继承Service的,那么它包含了Service的全部特性,当然也包含service的生命周期。
    那么与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去执行耗时操作
  21. 进程和 Application 的生命周期
  22. 封装View的时候怎么知道view的大小
  23. RecycleView原理
  24. AndroidManifest的作用与理解(类似一本书的目录)
    它指定了该应用程序的Java包:该包名作为应用程序的一个独特标识。(书名)
    它描述了应用程序组件:该应用程序由哪些activity,service,broadcast receiver和content provider组成。它指定了实现每个组件的类以及公开发布它们的能力(例如,它们能持有哪个Intent信息)。这些声明使Android系统知道这 儿有什么组件以及在什么条件下它们可以被载入。
    它决定那些进程将容纳应用程序组件。
    它声明了本应用程序必须拥有哪些许可,以便访问API的被保护部分,以及与其他应用程序交互。
    它也声明了其他应用程序在和该应用程序交互时需要持有的许可。
    它声明了该应用程序所需的Android API的最小化水平。

(三)常见的一些原理性问题

  1. Handler机制和底层实现
    Handler、Thread和HandlerThread的差别
    但是这个HandlerThread是拿来做什么的呢?它是Handler还是Thread?我们知道Handler是用来异步更新UI的,更详细的说是用来做线程间的通信的,更新UI时是子线程与UI主线程之间的通信。那么现在我们要是想子线程与子线程之间的通信要怎么做呢?当然说到底也是用Handler+Thread来完成(不推荐,需要自己操作Looper),Google官方很贴心的帮我们封装好了一个类,那就是刚才说到的:HandlerThread。(类似的封装对于多线程的场景还有AsyncTask)
  2. handler发消息给子线程,looper怎么启动?
  3. 关于Handler,在任何地方new Handler 都是什么线程下?
  4. ThreadLocal原理,实现及如何保证Local属性?
  5. 请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系
    这个我在面试中,被问道的蛮多的!今天是任务是排版,暂时不写。
  6. 请描述一下View事件传递分发机制
    Touch事件传递流程
    事件分发中的onTouch 和onTouchEvent 有什么区别,又该如何使用?
    先说事件分发是有三种,分别是Activity,View,ViewGroup。
    Activity –dispath –superdispath-true(自己消费)–false(返回)–onTouchEvent
    View -diapath-onTouch-true(自己消费)–false(返回)–dispath(onTouch同样的值)–onTouchEvent
    Viewgroup-dispath-oninterceptTouchevent (拦截)-true(自己消费)-返回给自己的diapath-并onTouch消费 –false -传递给View-然后View 在根据自己的事件分发处理。
    1、onTouch()方法:
      onTouch方式是View的OnTouchListener接口中定义的方法。
      当一个View绑定了OnTouchListener后,当有Touch事件触发时,就会调用onTouch方法。
    2、onTouchEvent()方法:
      onTouchEvent方法时重载的Activity的方法
      重写了Acitivity的onTouchEvent方法后,当屏幕有Touch事件时,此方法就会被调用。
    3、Touch事件的传递:
      在一个Activity里面放一个TextView的实例tv,并且这个tv的属性设定为march_parent
      在这种情况下,当手放到屏幕上的时候,首先会是tv响应Touch事件,执行onTouch方法。
      如果onTouch返回值为true,表示这个Touch事件被onTouch方法处理完毕,不会把Touch事件再传递给Activity
      也就是说onTouchEvent方法不会被调用
      如果onTouch返回值为false,表示这个Touch事件没有被tv完全处理,onTouch返回以后,Touch事件被传递给Activity,onTouchEvent方法调用。
  7. View和ViewGroup分别有哪些事件分发相关的回调方法
    dispatchTouchEvent()
  8. View刷新机制
    在Android的布局体系中,父View负责刷新、布局显示子View;而当子View需要刷新时,则是通知父View来完成。这种处理逻辑在View的代码中明确的表现出来:
    1)调用invalidate()
    View绘制流程
    基本分为measure、layout、draw 过程
  9. 自定义控件原理
  10. 自定义View如何提供获取View属性的接口?
  11. Android代码中实现WAP方式联网
  12. AsyncTask机制
    当使用线程和Handler组合实现异步处理时,当每次执行耗时操作都创建一条新线程进行处理,性能开销会比较大。为了提高性能我们使用AsyncTask实现异步处理(其实也是线程和handler组合实现),因为其内部使用了java提供的线程池技术,有效的降低了线程创建数量及限定了同时运行的线程数,还有一些针对性的对池的优化操作。所以说AsyncTask是Android为我们提供的方便编写异步任务的工具类。
  13. AsyncTask原理及不足
    AsyncTask的优点在于执行完后台任务后可以很方便的更新UI,然而使用它存在着诸多的限制。先抛开内存泄漏问题,使用AsyncTask主要存在以下局限性:
    AsyncTask对象必须在主线程中创建
    AsyncTask对象的execute方法必须在主线程中调用
    一个AsyncTask对象只能调用一次execute方法
  14. 如何取消AsyncTask?
    可以在doInBackground 中增加标志符,可以在onPostExecute 增加标识符,还可以直接取消asyncTask.cancel();
  15. 为什么不能在子线程更新UI?
    Android UI操作并不是线程安全的,并且这些操作必须在UI线程执行
    当一个程序第一次启动的时候,Android会同时启动一个对应的主线程,这个主线程就是UI线程,也就是ActivityThread。UI线程主要负责处理与UI相关的事件,如用户的按键点击、用户触摸屏幕以及屏幕绘图等。系统不会为每个组件单独创建一个线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去。所以,响应系统回调的方法永远都是在UI线程里运行,如响应用户动作的onKeyDown()的回调。
  16. ANR产生的原因是什么?(应用程序无响应)
    引起ANR的根本原因,总的来说可以归纳为两类:
    1)应用进程自身引起的,比如:主线程阻塞、挂起、死循环,执行耗时操作等;
    2)其他进程引起的,比如:其他进程CPU占用率过高,导致当前应用进程无法抢占到CPU时间片。常见的问题如文件读写频繁,io进程CPU占用率过高,导致当前应用出现ANR;
    主线程:只有应用程序进程的主线程响应超时才会产生ANR;
    超时时间:产生ANR的上下文不同,超时时间也不同,但只要超过这个时间上限没有响应就会产生ANR;
  17. 如何访问UI 线程
    Activity.runOnUiThread(Runnable)
    View.post(Runnable)
    View.postDelayed(Runnable, long)
    Handler
  18. ANR定位和修正
    因为ANR主要是因为主线程由于耗时操作被阻塞而产生的,所以常见的解决方法是不在主线程做耗时操作,具体实现时需要注意以下几点:
    主线程需要做耗时操作时,比如网络访问、数据库操作及位图变换等,必须启动一子线程处理,并利用handler来更新UI;
    子线程尽量使用Android提供的API,比如HandlerThread,AsyncTask,等,这些API都提供了对于线程的系统级管理。如果应用直接使用Thread实现的话,则需要对这些子线程进行显式管理,比如线程池及线程周期的控制,以防止系统资源和内存泄漏;
    Broadcast Receiver中如果有耗时操作,可以放到service中来处理;
    在后台子线程处理耗时操作时,为了提高用户体验,可以在前台界面显示某些动画或者progress bar;
  19. oom是什么?
    内存溢出!
  20. 什么情况导致oom?
    程序申请内存过大,虚拟机无法满足我们,然后自杀了。这个现象通常出现在大图片的APP开发,或者需要用到很多图片的时候。程序没有捕捉异常,故而弹窗崩溃了
    因为Android系统的APP每个进程或者虚拟机有最大内存限制,一旦超过这个限制系统就会抛出OOM错误。跟手机剩余内存是否充足没有多少关系。
  21. 有什么解决方法可以避免OOM?
    1)使用缓存技术,比如LruCache、DiskLruCache、对象重复并且频繁调用可以考虑对象池
    2)对于引用生命周期不一样的对象,可以用软引用或弱引用
    3)对于资源对象 使用finally 强制关闭
    4)内存压力过大就要统一的管理内存
    5)对于java中不再使用的资源需要尽快的释放,即设置成null,不要老是指望垃圾回收器为你工作。如果不设置成null,那么资源回收会受到一定的影响。
    6)尽量少用static方法和static成员。因为static的方法或成员被外部使用的话,而外部的牵引对象没有对其进行释放的话那么整个static的类都不会被释放,也就造成内存泄漏。
    7)对于不再使用的bitmap应该设置成null。图片还要尽量使用软引用方式,这样可以加快垃圾回收。
  22. Oom 是否可以try catch?为什么?
    Oom理论上可以被try catch 住,但是往往oom不是简单因为这里申请了内存导致oom,,你在这里catch,它又会在下一个地方崩溃。解决办法就是 需要清除内存,找出出内存异常的真正原因!但是一般不要做,找出oom真正的原因并解决,这样才不会在用户面前出现问题。
  23. 内存泄漏是什么?
    内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。
  24. 什么情况导致内存泄漏?
    1)资源释放
    程序代码的问题,长期保持某些资源,如Context,Cursor,IO流的引用,资源得不到释放造成内存泄露。
    2).static关键字的使用
    static 是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例,就可能会造成内存的泄露。
    针对static的解决方案:
    应该尽量避免static成员变量引用资源耗费过多的实例,比如Context.
    Context尽量使用ApplicationContext的生命周期比较长,引用它不会出现内存泄露。
    3)线程导致内存溢出
    线程产生内存泄露的主要原因在于线程生命周期的不可控。如当我们切换横竖屏的时候,一般会重新创建Activity,老的Activity应该被销毁。但是此时我们在子线程中正在进行耗时的操作,老的Activity不会被销毁,这个时候就会出现内存泄露。
    解决方案
    将线程的内部类,改为静态内部类。
    在线程内部采用弱引用保存Context引用。
  25. Handler中的内存泄漏!
    在该 SampleActivity 中声明了一个延迟10分钟执行的消息 Message,mLeakyHandler 将其 push 进了消息队列 MessageQueue 里。当该 Activity 被 finish() 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,所以此时 finish() 掉的 Activity 就不会被回收了从而造成内存泄漏(因 Handler 为非静态内部类,它会持有外部类的引用,在这里就是指 SampleActivity)。
    如何防止线程的内存泄漏?
  26. 内存泄露的解决方法
    即推荐使用静态内部类 + WeakReference 这种方式。每次使用前注意判空。
    前面提到了 WeakReference,所以这里就简单的说一下 Java 对象的几种引用类型。
    Java对引用的分类有 Strong reference, SoftReference, WeakReference, PhatomReference 四种。
  27. 内存泄漏和内存溢出区别?
    内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
    LruCache默认缓存大小
    ContentProvider的权限管理(解答:读写分离,权限控制-精确到表级,URL控制)
  28. 如何通过广播拦截和abort一条短信?
  29. 广播是否可以请求网络?
  30. 广播引起anr的时间限制是多少?
  31. 计算一个view的嵌套层级
  32. Activity栈
  33. Android线程有没有上限?
  34. 线程池有没有上限?
  35. ListView重用的是什么?
  36. Android为什么引入Parcelable?
  37. 有没有尝试简化Parcelable的使用?

(四)开发中常见的一些问题

  1. ListView 中图片错位的问题是如何产生的?
    根据Listview 的原理,每当他的一个item从屏幕上消失的时候,RecycleBin当中使用mScrapViews和mCurrentScrap这两个List来存储废弃View。
    他将移出的view存入setScrapView()中,当下方item滚动出来后会在mCurrentScrap,取出item使用,但是异步加载,中,当前item 绑定了view 还没来得及加载,就已经消失,随之imageview又被拿出来显示在界面上,又会附上一个新的URL,所以滑动的时候我们就看到错乱,先显示最初的那个加载图片,在显示当前的加载图片。
    http://blog.csdn.net/guolin_blog/article/details/44996879
  2. 混合开发有了解吗?
  3. 知道哪些混合开发的方式?说出它们的优缺点和各自使用场景?(解答:比如:RN,weex,H5,小程序,WPA等。做Android的了解一些前端js等还是很有好处的);
  4. 屏幕适配的处理技巧都有哪些?
  5. 服务器只提供数据接收接口,在多线程或多进程条件下,如何保证数据的有序到达?
  6. 动态布局的理解
  7. 怎么去除重复代码?
  8. 画出 Android 的大体架构图
  9. Recycleview和ListView的区别
  10. ListView图片加载错乱的原理和解决方案
    最简单的就是增加标记,如果我们此刻进行标记,将url和imageview进行标记绑定,如果下一次imageviw 被复用的时候,我们进行判断,每个ImageView中都会对应一个ImageContainer。这里从ImageContainer对象中获取封装的图片请求地址,并拿来和当前的请求地址做对比,如果相同的话说明这是一条重复的请求,就直接return掉,如果不同的话就调用cancelRequest()方法将请求取消掉,然后将图片设置为默认图片并重新发起请求。
  11. 动态权限适配方案,权限组的概念
  12. Android系统为什么会设计ContentProvider?
  13. 下拉状态栏是不是影响activity的生命周期
  14. 如果在onStop的时候做了网络请求,onResume的时候怎么恢复?
  15. Bitmap 使用时候注意什么?
    1)缓存中使用内存缓存,文件缓存双缓存机制。
    2)及时释放Bitmap的内存(针对Android 3.0之前的设备)
    3)复用内存
    4)降低采样率
  16. Bitmap的recycler()
  17. Android中开启摄像头的主要步骤
  18. ViewPager使用细节,如何设置成每次只初始化当前的Fragment,其他的不初始化?
  19. 点击事件被拦截,但是想传到下面的View,如何操作?
  20. 微信主页面的实现方式
  21. 微信上消息小红点的原理
    CAS介绍(这是阿里巴巴的面试题,我不是很了解,可以参考博客: CAS简介http://blog.csdn.net/jly4758/article/details/46673835)
  22. &和| (位运算符)
    运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;
    即:两位同时为“1”,结果才为“1”,否则为0
    即:两位同时为“0”,结果才为“0”,否则为0

高端技术面试题

这里讲的是大公司需要用到的一些高端Android技术,这里专门整理了一个文档,希望大家都可以看看。这些题目有点技术含量,需要好点时间去研究一下的。

(一)图片

图片库对比
图片库的源码分析
图片框架缓存实现
LRUCache原理
图片加载原理
自己去实现图片库,怎么做?
Glide源码解析
Glide使用什么缓存?
Glide内存缓存如何控制大小?


(二)网络和安全机制

  1. 网络框架对比和源码分析
  2. 自己去设计网络请求框架,怎么做?
    1)首先新建一个HttpUrlConnectionUtil类,先分别封装实现最常用的GET、POST方法,
    2)方法中需要输入许多参数,包括请求行(请求方法、URL)、头(Header)、体(请求参数)。GET与POST的区别就是,GET方法的请求参数是写在URL里的,POST的参数是写在请求体中的,(全部封装)
    3)所以我们在HttpUrlConnectionUtil类中新建一个execute(),异步方法,来统一处理请求,通过一个分支判断来实现,
  3. okhttp源码
  4. 网络请求缓存处理,okhttp如何处理网络缓存的
  5. 从网络加载一个10M的图片,说下注意事项
  6. TCP的3次握手和四次挥手
    1)握手
    第一次握手:客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
    握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
    2)挥手
    第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可以接受数据。
    第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
    第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
    第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
    TCP的四次挥手过程(简言之):主动关闭方向被动关闭方发送不会再给你发数据了的信息;被动关闭方对收到的主动关闭方的报文段进行确认;被动关闭方向主动关闭方发送我也不会再给你发数据了的信息;主动关闭方再次对被动关闭方的确认进行确认。
  7. TCP与UDP的区别
    1)TCP是一种面向连接的、可靠的传输层协议;
    TCP协议建立在不可靠的网络层 IP 协议之上,IP协议并不能提供任何可靠性机制,TCP的可靠性完全由自己实现;
    TCP采用的最基本的可靠性技术是:确认与超时重传机制、流量控制机制;
    超时重传是TCP协议保证数据可靠性的一个重要机制,其原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。
    2)UDP是一种无连接的、不可靠的传输层协议;
    提供了有限的差错检验功能;
    目的是希望以最小的开销来达到网络环境中的进程通信目的;
    随着网络技术飞速发展,网速已不再是传输的瓶颈,UDP协议以其简单、传输快的优势,在越来越多场景下取代了TCP,如网页浏览、流媒体、实时游戏、物联网。
    1.网速的提升给UDP稳定性提供可靠网络保障
    2.对比测试结果UDP性能优于TCP
  8. HTTP协议
    1)TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。
    2)把IP想像成一种高速公路,它允许其它协议在上面行驶并找到到其它电脑的出口。TCP和UDP是高速公路上的“卡车”,它们携带的货物就是像HTTP,文件传输协议FTP这样的协议等,将数据进行包装和打包。
    3)HTTP(超文本传输协议)是利用TCP在两台电脑(通常是Web服务器和客户端)之间传输信息的协议
    HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。
    Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第9张图片
  9. HTTP1.0与2.0的区别
    1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。
    2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。
  10. HTTP报文结构
  11. HTTP与HTTPS的区别以及如何实现安全性
  12. 如何验证证书的合法性?
  13. https中哪里用了对称加密,哪里用了非对称加密,对加密算法(如RSA)等是否有了解?
  14. client如何确定自己发送的消息被server收到?
  15. 谈谈你对WebSocket的理解
  16. WebSocket与socket的区别
  17. 谈谈你对安卓签名的理解。
  18. 请解释安卓为啥要加签名机制?
  19. 视频加密传输
  20. App 是如何沙箱化,为什么要这么做?
  21. 权限管理系统(底层的权限是如何进行 grant 的)?
  22. Socket的使用
    DatagramSocket 是用来接受和发送数据,DatagramPacket 用来处理数据, DatagramPacket.getData();获取数据。
  23. Socket 的原理!
    http://blog.csdn.net/hudashi/article/details/50790002
    套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
    应用 层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应 用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。
    套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。络连接的通信,实现数据传输的并发服务。
    服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
    客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
    连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发 给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
    现在我们知道,socket跟TCP/IP并没有必然的联系。Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以,socket的出现只是可以更方便的使用TCP/IP协议栈而已,其对TCP/IP进行了抽象,形成了几个最基本的函数接口。

(三)数据库
SQLiteOpenHelper使用
onCreate 中创建表
“CREATE TABLE “+ <表名> + “(_id Integer primary key autoincrement,”
插入
“INSERT INTO “+<表名>
更新
update <表名> set <列名=更新值> [where <更新条件>]
例:update tongxunlu set 年龄=18 where 姓名=’蓝色小名’
SQLiteDatabase SQLiteDatabase本身是一个数据库的操作类,但是如果想进行数据库的操作,还需要android.database.sqlite.SQLiteOpenHelper类的帮助,在执行SQL语句时execSQL()

sqlite升级,增加字段的语句
1.构造方法:
public ClassName(Context context, String name, CursorFactory factory, int version)

参数1:上下文对象(MainActivity.this)、
参数2:数据库的名称、
参数3:创建Cursor的工厂类,参数为了可以自定义Cursor创建(ps:一般为null)、
参数4:数据库的版本
2.两个回调函数:
onCreate(SQLiteDatabase db)该方法是当没有数据库存在才会执行
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)该方法是数据库存更新才会执行

数据库框架对比和源码分析
数据库的优化
数据库数据迁移问题


(四)算法

排序算法有哪些?
最快的排序算法是哪个?
手写一个冒泡排序
手写快速排序代码
快速排序的过程、时间复杂度、空间复杂度
手写堆排序
堆排序过程、时间复杂度及空间复杂度
写出你所知道的排序算法及时空复杂度,稳定性
二叉树给出根节点和目标节点,找出从根节点到目标节点的路径
给阿里2万多名员工按年龄排序应该选择哪个算法?
GC算法(各种算法的优缺点以及应用场景)
蚁群算法与蒙特卡洛算法
子串包含问题(KMP 算法)写代码实现
一个无序,不重复数组,输出N个元素,使得N个元素的和相加为M,给出时间复杂度、空间复杂度。手写算法
万亿级别的两个URL文件A和B,如何求出A和B的差集C(提示:Bit映射->hash分组->多文件读写效率->磁盘寻址以及应用层面对寻址的优化)
百度POI中如何试下查找最近的商家功能(提示:坐标镜像+R树)。
两个不重复的数组集合中,求共同的元素。
两个不重复的数组集合中,这两个集合都是海量数据,内存中放不下,怎么求共同的元素?
一个文件中有100万个整数,由空格分开,在程序中判断用户输入的整数是否在此文件中。说出最优的方法
一张Bitmap所占内存以及内存占用的计算
2000万个整数,找出第五十大的数字?
烧一根不均匀的绳,从头烧到尾总共需要1个小时。现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢?
求1000以内的水仙花数以及40亿以内的水仙花数
5枚硬币,2正3反如何划分为两堆然后通过翻转让两堆中正面向上的硬8币和反面向上的硬币个数相同
时针走一圈,时针分针重合几次
N*N的方格纸,里面有多少个正方形
x个苹果,一天只能吃一个、两个、或者三个,问多少天可以吃完?

(五)插件化、模块化、组件化、热修复、增量更新、Gradle

对热修复和插件化的理解
插件化原理分析
模块化实现(好处,原因)
热修复,插件化
项目组件化的理解
描述清点击 Android Studio 的 build 按钮后发生了什么

(六)架构设计和设计模式

  1. 谈谈你对Android设计模式的理解
  2. MVC MVP MVVM原理和区别

使用 MVC,把业务逻辑抽离到 Controller 中,让 View 层专注于显示 UI。
Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第10张图片
View层:对应的则是Android中的layout文件夹中的xml文件,在启动Activity/Fragment的时候,都会加载一个R.layout.xxx的布局文件,使得在视图中显示出我们在xml中定义好的视图。
Controller层:对应的则是Activity/Fragment。当Activity/Fragment加载了layout文件后,我们需要在Activity/Fragment中findViewById(int)去寻找到相对应的view,并对找到的view设置相应的属性以及监听器。而在设置view的属性之前,我们很有可能会先到model中请求一次数据,当数据回调回来后controller就会去更新view了。
Model层:对应的则是一些DataSource以及DataBean的相关对象,这里的DataSource指的是数据的来源。一般数据的来源有2个主要的地方,一个是sqlite,一个是webservice,而我们习惯于将这两种数据的来源封装在一个repository中,对于调用者而言只需要调用repository中的一个获取接口来获取数
Android面试题整理(源自鸿洋大神公众号【201803】的一篇BAT面试题推送)_第11张图片
View层:视图层,它所对应的不只是layout中的xml文件还包括了Activity/Fragment作为视图的显示。这样做是扩大了View层的职责所在,View不仅是设置ui的显示和属性并且还包括了生命周期的回调。
Presenter层:主持者层,它相当于是Controller中的业务逻辑部分,它主要是负责view和model层之间的通信,及时的响应view层的请求并主动的调用model层的数据获取,并且将获取到的数据结果返回给view层中。presenter是另外新建立一个class,并且让view从创建的时候就持有一个presenter的实例,当view发生某些请求响应或者生命周期发生变化,则会迅速的向presenter发起请求,让presenter做出响应的处理,比如:刷新数据、清除数据防止泄露等。
Model层:此处的数据抽象层model和MVC中的model层是一样的,这里就不做更多的叙述。

  1. 你所知道的设计模式有哪些?
    工厂模式,单例模式,Build模式,模版方法模式,策略模式,原型模式,观察者模式,代理模式
    1. 项目中常用的设计模式
      工厂模式,单例模式,Build模式,模版方法模式,策略模式
      手写生产者/消费者模式
      写出观察者模式的代码
      适配器模式,装饰者模式,外观模式的异同?
      用到的一些开源框架,介绍一个看过源码的,内部实现过程。
      谈谈对RxJava的理解
      RxJava的功能与原理实现
      RxJava的作用,与平时使用的异步操作来比的优缺点
      说说EventBus作用,实现方式,代替EventBus的方式
      从0设计一款App整体架构,如何去做?
      说一款你认为当前比较火的应用并设计(比如:直播APP,P2P金融,小视频等)
      谈谈对java状态机理解
      Fragment如果在Adapter中使用应该如何解耦?
      Binder机制及底层实现
      对于应用更新这块是如何做的?(解答:灰度,强制更新,分区域更新)?
      实现一个Json解析器(可以通过正则提高速度)
      统计启动时长,标准

(七)性能优化

  1. 如何对Android 应用进行性能分析以及优化?
    控件优化,布局优化,避免过渡重绘
    代码优化 合理使用四种引用方式,代码可复用率,算法的优化,减少不必要的全局变量
    网络优化 避免频繁的网络请求 ,使用线程池,图片缓存
    Android性能优化:同步请求改异步请求,增加消息缓存、
  2. ddms 和 traceView
    性能优化如何分析systrace?
    用IDE如何分析内存泄漏?
    查看memory曲线图,在退出Activity的时候,内存有无回收
    内存没有回收,
    Java多线程引发的性能问题,怎么解决?
    启动页白屏及黑屏解决?
    启动太慢怎么解决?
    怎么保证应用启动不卡顿?
    App启动崩溃异常捕捉
    自定义View注意事项
    让View支持wrap_content
    直接继承View或ViewGroup的控件 都不支持wrap_content 方法 需要在onMeasure方法中设置
    如果有必要 支持padding
    直接继承View的控件,如果不在onDraw方法中处理padding 那么padding属性是无法起作用的 直接继承ViewGroup的控件,需要处理padding和子元素的margin对其的影响,否则这两个属性也无效
    尽量不要在view中使用handler 因为view提供了post方法
    View内部本身就提供了post系列方法 可以代替handler 但如果明确需要Handler 可以使用
    view中有线程或者动画 要及时停止
    现在下载速度很慢,试从网络协议的角度分析原因,并优化(提示:网络的5层都可以涉及)。
    Https请求慢的解决办法(提示:DNS,携带数据,直接访问IP)
    如何保持应用的稳定性
    RecyclerView和ListView的性能对比
    ListView的优化
    RecycleView优化
    复杂布局的优化:
    1.尽量减少布局嵌套,层级越深,每次测量时间久越久。
    1. 如果布局很复杂,可以考虑自定义布局能不能实现。
      3.尽量减少过度绘制区域。这个可以在开发者选项中看到:调试GPU过度绘制。
      View渲染
      Bitmap如何处理大图,如一张30M的大图,如何预防OOM
      java中的四种引用的区别以及使用场景
      强引用置为null,会不会被回收?

补充:

  1. Service理解
    Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。
    设计模式
    APP 架构分为MVC,MVP,MVVM。
  2. APP 设计模式:
    https://www.jianshu.com/p/a6f4fa072a31
    工厂模式,单例模式,Build模式,模版方法模式,策略模式,原型模式,观察者模式,代理模式
    1.工厂模式,通俗的意思就是在工厂中写各种各样的类型,通过实例化工厂后,在当前类传入类型,就会返回相应的实例化对象。
    2.单例模式 这一模式的目的是使得类的一个对象成为系统中的唯一实例,(从而确保所有对象都访问唯一实例),通常有饿汉式,懒汉式。对于经常使用的。
    3.Build模式 典型应用就是 ,UIL就使用了Build模式。Java Builder模式主要是用一个内部类去实例化一个对象,避免一个类出现过多构造函数,而且构造函数如果出现默认参数的话,很容易出错。
    4.模版模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为
    5.策略模式 通俗的将就是用不同的方法和思路(具体行为不同)完成了一件事情同样的效果,如果我们使用了策略模式,在保证同样的效果的情况下灵活更换使用的方法。看出我们的程序对单一图片加载框架依赖降低,低耦合,由于可以平滑更换具体实现,提高了程序的可扩展性.
    6.原型模式 实现原理就是当前我有一个任务,其他的人和我的任务目标相同但是目标的的信息不通。比如写简历,有相同的也有不同的。
    7.观察者模式 观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监听者,类似监听器的存在,一旦被观察/监听的目标发生的情况,就会被监听者发现,这么想来目标发生情况到观察者知道情况,其实是由目标奖情况发送到观察者的。
    观察者模式多用于实现订阅功能的场景,例如微博的订阅,当我们订阅了某个人的微博账号,当这个人发布了新的消息,就会通知我们
    8.代理模式 给某一个对象提供一个代理,并由代理对象控制对原对象的引用 静态代理和动态代理。静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。
  3. H264中何为sps,pps
    1.使用RTP传输H264的时候,需要用到sdp协议描述,其中有两项:Sequence Parameter Sets (SPS) 和Picture Parameter Set (PPS)需要用到,那么这两项从哪里获取呢?答案是从H264码流中获取.在H264码流中,都是以(三个起始码或者四个起始码)”0x00 0x00 0x01”或者”0x00 0x00 0x00 0x01”为开始码的,找到开始码之后,使用开始码之后的第一个字节的低5位判断是否为7(sps)或者8(pps), 及data[4] & 0x1f == 7 || data[4] & 0x1f == 8.然后对获取的nal去掉开始码之后进行base64编码,得到的信息就可以用于sdp.sps和pps需要用逗号分隔开来.
    2.SDP中的H.264的SPS和PPS串,包含了初始化H.264解码器所需要的信息参数,包括编码所用的profile,level,图像的宽和高,deblock滤波器等。
    由于SDP中的SPS和PPS都是BASE64编码形式的,不容易理解,附件有一个工具软件可以对SDP中的SPS和PPS进行解析。
  4. AOA协议理解
    1.AOA协议拓展了Android设备USB接口的功能,为基于Android系统的智能设备应用于数据采集和设备控制领域提供了条件。介绍了Android系统下USB通信的两种模式(主机模式(Host Mode)和配件模式(Accessory Mode)).
    ①主机模式是指Android设备充当USB主机并为总线供电。此模式下,Android设备需支持USB主机功能或OTG功能,此时Android设备的USB主机称为USB嵌入式主机EH(Embedded Host)[2]。与PC上的USB主机相比,EH设备可能无法为连接到其总线上的未识别外围设备加载驱动程序。
    ②配件模式是指Android设备充当USB从机,外部设备充当主机并为总线供电。此模式下,外部USB设备称为Android配件。该模式为不具备主机功能的Android设备提供与USB设备交互的能力。Android设备和Android配件都必须支持AOA协议。不支持AOA协议的设备可以通过Android配件开发板(ADK板)与Android设备连接,成为Android设备的间接配件。
    2.Android设备端需进行以下两项工作:
    (1)确保设备支持AOA协议。设备对AOA协议是否支持由设备硬件和Android系统版本决定。
    (2)调用ADK中的API开发USB应用程序。
    ADK中与USB配件模式相关的两个类是UsbManager和UsbAccessory。UsbManager用于枚举连接的USB配件并与其通信,UsbAccessory代表Android配件并包含获取配件描述信息的方法。
    在编写程序前,需要为Android工程做以下设置工作[6]:
    (1)配置Android Mainfest文件,声明使用配件模式,指定最低SDK版本,并设置配件过滤意图。
    (2)配置accessory_filter.xml文件。accessory_filter.xml文件用于描述希望Android设备检测的USB配件的描述信息,包含manufacturer、model、version 3个属性。在Android配件的固件程序中,同样包含以上3个属性值。根据AOA协议,当Android配件连接到Android设备上时,配件会发送该属性值到Android设备。Android程序将唯一响应与其accessory_filter.xml中指定的属性值匹配的Android配件。
    Android设备端程序开发的流程如下:
    (1)发现配件并过滤。
    (2)获取与配件通信的权限。通过广播接收器(Broad-castReceiver)的方式以弹出授权对话框的形式询问用户是否允许与Android配件通信。
    (3)开始与配件通信。创建文件输入输出流代表USB批量传输端点,通过独立线程以流传输的方式实现USB通信。
    (4)终止与配件通信。通过广播接收器侦听USB配件与Android设备的断开操作,当事件发生时,关闭文件输入/输出流。

你可能感兴趣的:(Android进阶)