安逸久了就容易迷失方向,多看看高质量的面试题找找差距,然后查漏补缺!周末再来复习一下,巩固基础和技术知识。
1. 谈谈冷启动与热启动
app 冷启动:
当应用启动时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就叫做冷启动((后台不存在该应用进程) 冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化 Application 类,再创建和初始化 MainActivity 类(包括一系列的测量、布局、绘制),最后显示在界面上。
app 热启动:
当应用已经被打开, 但是被按下返回键、Home 键等按键时回到桌面或者是其他程序的时候,再重新打开该 app 时,这个方式叫做热启动(后台已经存在该应用进程)。
热启动因为会从已有的进程中来启动,所以热启动就不会走 Application 这步了,而是直接走 MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个 MainActivity 就行了,而不必创建和初始化 Application。
冷启动的流程
当点击 app 的启动图标时,安卓系统会从 Zygote
进程中 fork 创建出一个新的进程分配给该应用,之后会依次创建和初始化 Application
类、创建 MainActivity
类、加载主题样式 Theme
中的 windowBackground
等属性设置给 MainActivity 以及配置 Activity 层级上的一些属性、再 inflate
布局、当 onCreate/onStart/onResume
方法都走完了后最后才进行 contentView
的measure/layout/draw
显示在界面上。
冷启动的生命周期简要流程:
Application 构造方法
–> attachBaseContext()
–> onCreate
–> Activity 构造方法
–> onCreate()
–> 配置主体中的背景等操作
–> onStart()
–> onResume()
–> 测量、布局、绘制显示
冷启动的优化
主要是 视觉上的优化,解决白屏问题,提高用户体验,所以通过上面冷启动的过程。能做的优化如下:
1) 减少 onCreate()方法的工作量
2) 不要让 Application 参与业务的操作
3) 不要在 Application 进行耗时操作
4) 不要以静态变量的方式在 Application 保存数据
5) 减少布局的复杂度和层级
6) 减少主线程耗时
2. XML 文档定义有几种形式?它们之间有何本质区别?解析 XML 文档有哪几种方式?
XML 文档定义分为 DTD
和Schema
两种形式;二者都是对 XML 语法的约束,其本质区别在于 Schema 本身也是一个 XML 文件,可以被 XML 解析器解析,而且可以为 XML 承载的数据定义类型,约束能力较之 DTD 更强大。
对 XML 的解析主要有:
DOM
(文档对象模型,Document Object Model)、SAX
(Simple API for XML)和StAX
(Java 6 中引入的新的解析 XML 的方式,Streaming API for XML),其中 DOM 处理大型文件时其性能下降的非常厉害,这个问题是由 DOM 树结构占用的内存较多造成的,而且 DOM 解析方式必须在解析文件之前把整个文档装入内存,适合对 XML 的随机访问(典型的用空间换取时间的策略);
SAX 是事件驱动型的 XML 解析方式,它顺序读取 XML 文件,不需要一次全部装载整个文件。
当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过事件回调代码来处理 XML 文件,适合对 XML 的顺序访问;顾名思义,StAX 把重点放在流上,实际上 StAX 与其他解析方式的本质区别就在于应用程序能够把 XML 作为一个事件流来处理。
将 XML 作为一组事件来处理的想法并不新颖(SAX 就是这样做的),但不同之处在于 StAX 允许应用程序代码把这些事件逐个拉出来,而不用提供在解析器方便时从解析器中接收事件的处理程序。
3. Java nio 和 io 的区别
1)Java NIO 提供了与标准 IO 不同的 IO 工作方式:
- Channels and Buffers(通道和缓冲区):
标准的 IO 基于字节流和字符流进行操作的,而 NIO 是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
- Asynchronous IO(异步 IO):
Java NIO 可以让你异步的使用 IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似
- Selectors(选择器):
Java NIO 引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。
2)阻塞 IO 和非阻塞 IO
- Java IO 流都是阻塞的 ,这意味着,当一条线程执行 read() 或者 write()方法时,这条线程会一直阻塞直到读取到了一些数据或者要写出去的数据已经全部写出,在这期间这条线程不能做任何其他的事情。
- java NIO 的非阻塞模式 (Java NIO 有阻塞模式和非阻塞模式,阻塞模式的 NIO 除了使用 Buffer 存储数据外和 IO 基本没有区别) 允许一条线程从 channel 中读取数据,通过返回值来判断 buffer 中是否有数据,如果没有数据,NIO 不会阻塞,因为不阻塞这条线程就可以去做其他的事情,过一段时间再回来判断一下有没有数据。NIO 的写也是一样的,一条线程将 buffer 中的数据写入 channel,它不会等待数据全部写完才会返回,而是调用完 write()方法就会继续向下执行
3)面向流与面向缓冲
Java IO 和 NIO 之间第一个最大的区别是,IO 是面向流的,NIO 是面向缓冲区的。Java IO 面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。
此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。Java NIO 的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
4. String 为什么要设计成不可变的
1)字符串池的需求字符串池是方法区(Method Area)中的一块特殊的存储区域。当一个字符串已经被创建并且该字符串在池中,该字符串的引用会立即返回给变量,而不是重新创建一个字符串再将引用返回给变量。如果字符串不是不可变的,那么改变一个引用(如: string2)的字符串将会导致另一个引用(如: string1)出现脏数据。
2)允许字符串缓存哈希码在 java 中常常会用到字符串的哈希码,例如:HashMap。String 的不变性保证哈希码始终一,因此,他可以不用担心变化的出现。这种方法意味着不必每次使用时都重新计算一次哈希码——这样,效率会高很多。
3)安全 String 广泛的用于 java 类中的参数,如:网络连接(Network connetion),打开文件(opening files)等等。如果 String 不是不可变的,网络连接、文件将会被改变——这将会导致一系列的安全威胁。操作的方法本以为连接上了一台机器,但实际上却不是。由于反射中的参数都是字符串,同样,也会引起一系列的安全问题。
5. HashMap 排序
已知一个 HashMap<Integer,User> 集合,User 有 name(String)和 age(int)属性。请写一个方法 实现对 HashMap 的排序功能,要求对 HashMap 中的 User 的 age 倒序进行排序。
Tips:
HashMap 本身就是不可排序的,但是该道题偏偏让给 HashMap 排序,那我们就得想在 API 中有没有这样的 Map 结构是有序的,LinkedHashMap
,对,就是他,他是 Map 结构,也是链表结构,有序的,更可喜的是他是 HashMap 的子类,我们返回 LinkedHashMap<Integer,User> 即可。
但凡是 对集合的操作 ,我们应该保持一个原则就是 能用 JDK 中的 API 就用 JDK 中的 API,比如排序算法我们不应该去用冒泡或者选择,而 是首先想到用 Collections 集合工具类。
public class HashMapTest {public static void main(String[] args) {HashMap<Integer, User> users = new HashMap<>();
users.put(1, new User("张三", 25));
users.put(3, new User("李四", 22));
users.put(2, new User("王五", 28));
System.out.println(users);
HashMap<Integer,User> sortHashMap = sortHashMap(users);
System.out.println(sortHashMap);
/**
* 控制台输出内容
* {1=User [name= 张三, age=25], 2=User [name= 王五, age=28], 3=User [name= 李四, age=22]}
* {2=User [name= 王五, age=28], 1=User [name= 张三, age=25], 3=User [name= 李四, age=22]}
*/
}
public static HashMap<Integer, User> sortHashMap(HashMap<Integer, User> map) {
// 首先拿到 map 的键值对集合
Set<Entry<Integer, User>> entrySet = map.entrySet();
// 将 set 集合转为 List 集合,为什么,为了使用工具类的排序方法
List<Entry<Integer, User>> list = new ArrayList<Entry<Integer, User>>(entrySet);
// 使用 Collections 集合工具类对 list 进行排序,排序规则使用匿名内部类来实现
Collections.sort(list, new Comparator<Entry<Integer, User>>() {
@Override
public int compare(Entry<Integer, User> o1, Entry<Integer, User> o2) {
// 按照要求根据 User 的 age 的倒序进行排
return o2.getValue().getAge()-o1.getValue().getAge();
}
});
// 创建一个新的有序的 HashMap 子类的集合
LinkedHashMap<Integer, User> linkedHashMap = new LinkedHashMap<Integer, User>();
// 将 List 中的数据存储在 LinkedHashMap 中
for(Entry<Integer, User> entry : list){linkedHashMap.put(entry.getKey(), entry.getValue());
}
return linkedHashMap;
}
}
文末
有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如 Handler 机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
想要成长为一个移动互联网架构师,一些技能是必不可少的,小编自己在一些平台收集到了许多学习资源和个人总结的一些经验,一线互联网公司得面试经历面试 PDF 在这里分享给大家,包括不限于 高级 UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter 等全方面的 Android 进阶实践技术还有相关的面试专题 PDF;希望能帮助到大家,也节省大家在网上搜索的时间来学习,也可以分享动态给身边好友一起学习!
加我 wx:X1524478394 免费领取 Android 高阶学习视频和 BAT 面试专题 PDF
一些相关的 PDF:
扫描二维码即可:
专注分享大型 Bat 面试知识,后续会持续更新,希望通过这些高级面试题能够降低面试 Android 岗位的门槛,让更多的 Android 工程师理解 Android 系统,掌握 Android 系统。喜欢的话麻烦点击一个喜欢在关注一下~