AndroidInterview-Q-A

AndroidInterview-Q-A

Java

ClassNotFoundExceptionNoClassDefFoundError的区别

Exception 和 Error 有何区别

HashMap原理-美团

面试官:

  1. HashMap的数据结构是啥?有啥特点?
  2. 撞库如何处理,成树,成表?
  3. 扩容如何处理?
  4. 知道ArrayMap吗?SparseArray呢,有啥特点?

HashMap常见面试问题

HashMap中的取模和扩容公式推导

HashMap默认加载因子为什么选择0.75?

Java 8系列之重新认识HashMap

Map 综述(一):彻头彻尾理解 HashMap

HashmapHashtable的区别-乐视-小米

Java中hashmap和hashtable的区别

JIT 编译器

面试官:

  1. 什么是JIT 热代码?

Java 的 JIT 知识整理

JIT优化之道

JavaBean HashCode怎么写

Java 各种hash算法分析

Java equals() ,hashCode(),toString() 的推荐写法

LRUCache

LRU缓存机制

Wait、Sleep和Yield方法的区别

面试官:

  1. sleep是可中断的吗?
  2. wait、sleep应用场景有哪些?

Java中Wait、Sleep和Yield方法的区别

内存模型

java线程安全总结

深入理解java内存模型系列文章

动态代理

Java 动态代理详解

多态

Java多态性理解

Java中多态性的实现

什么是多态

面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。

多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)

实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。

多态的作用:消除类型之间的耦合关系。

现实中,关于多态的例子不胜枚举。比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;如果当前在 Word 下弹出的就是 Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。同一个事件发生在不同的对象上会产生不同的结果。 下面是多态存在的三个必要条件,要求大家做梦时都能背出来!

多态存在的三个必要条件 一、要有继承; 二、要有重写; 三、父类引用指向子类对象。

多态的好处:

  1. 可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作

  2. 可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。

  3. 接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如CircleSphere为了实现多态,完善或者覆盖这两个接口方法。

  4. 灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。

  5. 简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

    Java中多态的实现方式:接口实现,继承父类进行方法重写,同一个类中进行方法重载。

大文件排序

5 亿整数的大文件,来排个序?

Java学习笔记:外部排序总结

对一个20GB大的文件排序

容器类

Java容器类

《深入理解Java集合框架》系列文章

Java中的Copy-On-Write容器:CopyOnWriteArrayList

泛型

面试官:

  1. 泛型擦除是怎么回事?
  2. 如果擦除了,那运行时是怎么拿回原来的类型的?
  3. Gson是怎么处理的泛型擦除?

[泛型中 extends 和 super 的区别?](https://itimetraveler.github.io/2016/12/27/【Java】泛型中 extends 和 super 的区别?/)

java泛型背后是什么

关于Gson的TypeToken

详解Gson的TypeToken原理

父类的静态方法能否被子类重写-猎豹

不能

子类继承父类后,用相同的静态方法和非静态方法,这时非静态方法覆盖父类中的方法(即方法重写),父类的该静态方法被隐藏(如果对象是父类则调用该隐藏的方法),另外子类可继承父类的静态与非静态方法,至于方法重载我觉得它其中一要素就是在同一类中,不能说父类中的什么方法与子类里的什么方法是方法重载的体现

线程&&线程池原理-腾讯

腾讯格外注重线程并发及网络知识

线程

并发编程1:全面认识 Thread

Java 线程上下文切换

线程状态:

一张图让你看懂JAVA线程间的状态转换

线程池

并发编程2:认识并发编程的利与弊

并发编程3:线程池的使用与执行流程

线程池ThreadPoolExecutor实现原理

面试必备:Java线程池解析

线程的阻塞

为了解决对共享存储区的访问冲突,Java 引入了同步机制,现在让我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,Java 引入了对阻塞机制的支持.

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

  1. sleep() 方法:sleep() 允许 指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。 典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。
  2. suspend() 和 resume() 方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。
  3. yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程.
  4. wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许 指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用.

初看起来它们与 suspend() 和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。

上述的核心区别导致了一系列的细节上的区别。

首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。

wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和wakeup 原语(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。

关于 wait() 和 notify() 方法最后再说明两点:

第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。

以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() 和 notify() 方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。

虚拟机-百度-乐视

面试官:

  1. 请描述new一个对象的流程
  2. JVM和Dalvik的区别?

JVM类加载过程 & 双亲委派模型

JVM 答疑解惑

关于Dalvik,我们该知道些什么?

进程和线程的区别-猎豹-美团

简而言之,一个程序至少有一个进程,一个进程至少有一个线程。

线程的划分尺度小于进程,使得多线程程序的并发性高。

另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。如果有兴趣深入的话,我建议你们看看《现代操作系统》或者《操作系统的设计与实现》。对就个问题说得比较清楚。

锁&&并发

面试官:

  1. 写一段死锁代码
  2. CAS底层是怎么实现的?
  3. Volatile 怎么实现的可见性?
  4. Synchronized原理?高版本Synchronized是怎么优化的?Synchronized一定比Lock性能差吗?
  5. AQS原理
  6. ConcurrentHashMap底层结构是什么?Put过程?各个版本之间有哪些变化?

并发包

《深入浅出 Java Concurrency》目录

锁:

死锁的产生、防止、避免、检测和解除

Synchronize和ReentrantLock区别

Synchronized高版本优化

深度分析:锁升级过程和锁状态

深入理解Java并发之synchronized实现原理

从 synchronized 到 CAS 和 AQS - 彻底弄懂 Java 各种并发锁

不可不说的Java“锁”事

死锁代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static void main(String[] args) {
final Object a = new Object();
final Object b = new Object();
Thread threadA = new Thread(new Runnable() {
public void run() {
synchronized (a) {
try {
Thread.sleep(1000);
synchronized (b) {
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
});
Thread threadB = new Thread(new Runnable() {
public void run() {
synchronized (b) {
try {
Thread.sleep(1000);
synchronized (a) {
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
});

threadA.start();
threadB.start();
}

并发编程:

理解 Java volatile 关键字

Java多线程编程总结

Java并发编程的总结与思考

从ReentrantLock的实现看AQS的原理及应用

深入分析ConcurrentHashMap

探索 ConcurrentHashMap 高并发性的实现机制

ConcurrentHashMap面试题

并发容器之ConcurrentHashMap详解(JDK1.8版本)与源码分析

Android

ANR定位和修正

如果开发机器上出现问题,我们可以通过查看/data/anr/traces.txt即可,最新的ANR信息在最开始部分。

  • 主线程被IO操作(从4.0之后网络IO不允许在主线程中)阻塞。
  • 主线程中存在耗时的计算
  • 主线程中错误的操作,比如Thread.wait或者Thread.sleep等 Android系统会监控程序的响应状况,一旦出现下面两种情况,则弹出ANR对话框
  • 应用在5秒内未响应用户的输入事件(如按键或者触摸)
  • BroadcastReceiver未在10秒内完成相关的处理
  • Service在特定的时间内无法处理完成 20秒
  • 使用AsyncTask处理耗时IO操作。
  • 使用Thread或者HandlerThread时,调用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同。
  • 使用Handler处理工作线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程。
  • Activity的onCreate和onResume回调中尽量避免耗时的代码
  • BroadcastReceiver中onReceive代码也要尽量减少耗时,建议使用IntentService处理。

AOP技术方案

谈谈Android AOP技术方案

Activity视图层级

Android的Activity视图层级分析

Android图形系统

Android图形系统综述(干货篇)

Android图形系统概述

Apk 打包流程

Apk 打包流程梳理

ArrayMap

深度解读ArrayMap优势与缺陷

ArtDalvik区别

Dalvik 和 ART 有什么区别?深扒 Android 虚拟机发展史,真相却出乎意料!

art和dalvik的区别?

Binder 原理

面试官:

  1. Android为什么使用Binder?用 Linux原有的IPC不行吗?
  2. 为什么需要Binder驱动?放在用户空间行不行?
  3. 匿名共享内存是什么?怎么提高效率的?

入门:

为什么 Android 要采用 Binder 作为 IPC 机制?

说说你对 binder 驱动的了解?

写给-Android-应用工程师的-Binder-原理剖析

推荐

理解Android Binder机制(1/3):驱动篇

理解Android Binder机制(2/3):C++层

理解Android Binder机制(3/3):Java层

gityuan:

Binder系列—开篇

Binder系列1—Binder Driver初探

Binder系列2—Binder Driver再探

Binder系列3—启动ServiceManager

Binder线程池工作过程

老罗系列:

Android进程间通信(IPC)机制Binder简要介绍和学习计划

浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路

浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路

Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析

Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析

Binder匿名共享内存原理

认真分析mmap:是什么 为什么 怎么用

回写时机:内存不足、进程crash、调用msync munmap、不设置 MAP_NOSYNC 情况下 30s-60s(仅限FreeBSD)

ContentProvider-乐视

http://blog.csdn.net/coder_pig/article/details/47858489

Fragment生命周期

Fragment生命周期

Framework

学习Framework相关的东西,推荐一本书《Android内核剖析》,柯元旦老师写的,通俗易懂。

App 启动流程分析

Android应用程序启动过程源代码分析

Android应用程序内部启动Activity过程(startActivity)的源代码分析

startActivity启动过程分析

bindService启动过程分析

Android资源管理框架(Asset Manager)简要介绍和学习计划

WindowManagerService和ActivityManagerService中的Token

AMS

ActivityManagerService启动过程

PMS

Android包管理机制

Glide源码解析

面试官:

  1. Glide 相比其他框架有哪些特点?
  2. Glide 缓存key是怎么生成的?
  3. 各种缓存之间数据是怎么流动的?
  4. Glide内LRUCache底层原理是什么?

Android Glide源码解析

Android 【手撕Glide】–Glide缓存机制

Gradle

Gradle 生命周期

统计Task执行时长

Android 插件的 Transform API

Handler 原理

面试官:

  1. 子线程怎么创建Handler?
  2. Looper无线循环为什么没有ANR?
  3. Handler底层是如何唤醒的?
  4. handler异步消息指的是什么?有什么应用场景?

线程、Handler、Looper之间关系

Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

探索 Android 大杀器—— Handler

Android异步消息处理机制完全解析,带你从源码的角度彻底理解

详解 Android 中的 HandlerThread

Android应用程序消息处理机制(Looper、Handler)分析

Handler异步消息与同步屏障(SyncBarrier)

Kotlin

Kotlin协程中的线程池

LaunchMode应用场景-百度-小米-乐视

standard,创建一个新的Activity。

singleTop,栈顶不是该类型的Activity,创建一个新的Activity。否则,onNewIntent。

singleTask,回退栈中没有该类型的Activity,创建Activity,否则,onNewIntent+ClearTop。

注意:

  1. 设置了”singleTask”启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity等于它的属性值taskAffinity的Task存在; 如果存在这样的Task,它就会在这个Task中启动,否则就会在新的任务栈中启动。因此, 如果我们想要设置了”singleTask”启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。
  2. 如果设置了”singleTask”启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例, 如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity 实例会位于任务的Stack顶端中。
  3. 在一个任务栈中只有一个”singleTask”启动模式的Activity存在。他的上面可以有其他的Activity。这点与singleInstance是有区别的。

singleInstance,回退栈中,只有这一个Activity,没有其他Activity。

singleTop适合接收通知启动的内容显示页面。

例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。

singleTask适合作为程序入口点。

例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。

singleInstance应用场景:

闹铃的响铃界面。 你以前设置了一个闹铃:上午6点。在上午5点58分,你启动了闹铃设置界面,并按 Home 键回桌面;在上午5点59分时,你在微信和朋友聊天;在6点时,闹铃响了,并且弹出了一个对话框形式的 Activity(名为 AlarmAlertActivity) 提示你到6点了(这个 Activity 就是以 SingleInstance 加载模式打开的),你按返回键,回到的是微信的聊天界面,这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了。如果是以 SingleTask 打开 AlarmAlertActivity,那么当闹铃响了的时候,按返回键应该进入闹铃设置界面。

LinearLayout和RelativeLayout性能对比-百度

  1. RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,也会调用子View2次onMeasure
  2. RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。
  3. 在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout。

最后再思考一下文章开头那个矛盾的问题,为什么Google给开发者默认新建了个RelativeLayout,而自己却在DecorView中用了个LinearLayout。因为DecorView的层级深度是已知而且固定的,上面一个标题栏,下面一个内容栏。采用RelativeLayout并不会降低层级深度,所以此时在根节点上用LinearLayout是效率最高的。而之所以给开发者默认新建了个RelativeLayout是希望开发者能采用尽量少的View层级来表达布局以实现性能最优,因为复杂的View嵌套对性能的影响会更大一些。

RecyclerView缓存原理

面试官:

  1. RV相比ListView有哪些改进?
  2. RV缓存存储及获取流程?

RecyclerView缓存原理

深入理解 RecyclerView 的缓存机制

SP原理及优化

面试官:

  1. commit与apply的区别是什么?

全面剖析SharedPreferences

Service与Activity之间通信的几种方式

  • 通过Binder对象
  • 通过broadcast(广播)的形式

ThreadLocal原理

Android 一起来看看 ThreadLocal

Ubuntu编译安卓系统-百度

  1. 进入源码根目录
  2. . build/envsetup.sh
  3. lunch
  4. full(编译全部)
  5. userdebug(选择编译版本)
  6. make -j8(开启8个线程编译)

View绘制流程-百度

面试官:

  1. 在OnResume中可以测量宽高吗?
  2. View的绘制流程是从Activity的哪个生命周期方法开始执行的?

公共技术点之 View 绘制流程

Android View的绘制流程

volley解析-美团-乐视

[Volley 源码解析](http://a.codekk.com/detail/Android/grumoon/Volley 源码解析)

WebView

Android WebView缓存机制和性能优化

Android WebView与 JS 的交互方式

dex,odex,oat,vdex,art文件结构

[Android dex,odex,oat,vdex,art文件结构](https://skytoby.github.io/2019/Android dex,odex,oat,vdex,art文件结构/)

onSaveInstanceState

面试官:

如何save数据的,从原理角度来分析?

关于onSaveInstanceState的那点事

为什么不能使用 Application Context 显示 Dialog

为什么不能使用 Application Context 显示 Dialog?

事件传递流程-小米

面试官:

  1. View怎么阻止ViewGroup拦截事件传递?
  2. onInterceptTouchEvent 方法在整个传递过程中都存在吗?
  3. onTouchListener onTouchEvent onClick 的执行顺序是什么样的?
  4. onTouchEvent如果返回false,onClick还会执行吗?

Android事件分发机制

Android触摸事件传递机制

Android8.0 按键事件处理流程

什么情况导致OOM-乐视-美团

Android内存优化之OOM

  1. 使用更加轻量的数据结构
  2. Android里面使用Enum
  3. Bitmap对象的内存占用
  4. 更大的图片
  5. onDraw方法里面执行对象的创建
  6. StringBuilder

什么情况导致内存泄漏-美团

内存泄漏框架解读

  1. 资源对象没关闭造成的内存泄漏

    描述: 资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。因为有些资源性对象,比如 SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该调用它的close()函数,将其关闭掉,然后才置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。 程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。

  2. 构造Adapter时,没有使用缓存的convertView

    描述: 以构造ListView的BaseAdapter为例,在BaseAdapter中提供了方法: public View getView(int position, ViewconvertView, ViewGroup parent) 来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的 view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。由此可以看出,如果我们不去使用 convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。 ListView回收list item的view对象的过程可以查看: android.widget.AbsListView.java –> voidaddScrapView(View scrap) 方法。 示例代码:

    1
    2
    3
    4
    5
    public View getView(int position, ViewconvertView, ViewGroup parent) {
    View view = new Xxx(...);
    //... ...
    return view;
    }

    修正示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public View getView(int position, ViewconvertView, ViewGroup parent) {
    View view = null;
    if (convertView != null) {
    view = convertView;
    populate(view, getItem(position));
    //...
    } else {
    view = new Xxx(...);
    //...
    }
    return view;
    }
  3. Bitmap对象不在使用时调用recycle()释放内存

    描述: 有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不在被使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。可以看一下代码中的注释:

    1
    /** •Free up the memory associated with thisbitmap's pixels, and mark the •bitmap as "dead", meaning itwill throw an exception if getPixels() or •setPixels() is called, and will drawnothing. This operation cannot be •reversed, so it should only be called ifyou are sure there are no •further uses for the bitmap. This is anadvanced call, and normally need •not be called, since the normal GCprocess will free up this memory when •there are no more references to thisbitmap. */
  4. 试着使用关于application的context来替代和activity相关的context

    这是一个很隐晦的内存泄漏的情况。有一种简单的方法来避免context相关的内存泄漏。最显著地一个是避免context逃出他自己的范围之外。使用Application context。这个context的生存周期和你的应用的生存周期一样长,而不是取决于activity的生存周期。如果你想保持一个长期生存的对象,并且这个对象需要一个context,记得使用application对象。你可以通过调用 Context.getApplicationContext() or Activity.getApplication()来获得。更多的请看这篇文章如何避免 Android内存泄漏。

  5. 注册没取消造成的内存泄漏

    一些Android程序可能引用我们的Anroid程序的对象(比如注册机制)。即使我们的Android程序已经结束了,但是别的引用程序仍然还有对我们的Android程序的某个对象的引用,泄漏的内存依然不能被垃圾回收。调用registerReceiver后未调用unregisterReceiver。 比如:假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个 PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。 但是如果在释放 LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process 进程挂掉。 虽然有些系统程序,它本身好像是可以自动取消注册的(当然不及时),但是我们还是应该在我们的程序中明确的取消注册,程序结束时应该把所有的注册都取消掉。

  6. 集合中对象没清理造成的内存泄漏

    我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。

动画框架实现原理

Android动画实现原理

Animation框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个View,实现原理是每次绘制视图时View所在的ViewGroup中的drawChild函数获取该View的Animation的Transformation值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧,如果动画没有完成,继续调用invalidate()函数,启动下次绘制来驱动动画,动画过程中的帧之间间隙时间是绘制函数所消耗的时间,可能会导致动画消耗比较多的CPU资源,最重要的是,动画改变的只是显示,并不能相应事件。

垃圾回收

面试官:

  1. JVM是分代的,那Dalvik呢?
  2. 垃圾回收算法具体是怎么执行的?
  3. ART相比Dalvik在GC方面有哪些改进?

管理应用的内存

Android 操作系统的内存回收机制

Android GC 原理探究

Java中什么样的对象才能作为gc root,gc roots有哪些呢?

屏幕刷新机制

属性动画特性-乐视-小米

如果你的需求中只需要对View进行移动、缩放、旋转和淡入淡出操作,那么补间动画确实已经足够健全了。但是很显然,这些功能是不足以覆盖所有的场景的,一旦我们的需求超出了移动、缩放、旋转和淡入淡出这四种对View的操作,那么补间动画就不能再帮我们忙了,也就是说它在功能和可扩展方面都有相当大的局限性,那么下面我们就来看看补间动画所不能胜任的场景。

注意上面我在介绍补间动画的时候都有使用“对View进行操作”这样的描述,没错,补间动画是只能够作用在View上的。也就是说,我们可以对一个Button、TextView、甚至是LinearLayout、或者其它任何继承自View的组件进行动画操作,但是如果我们想要对一个非View的对象进行动画操作,抱歉,补间动画就帮不上忙了。可能有的朋友会感到不能理解,我怎么会需要对一个非View的对象进行动画操作呢?这里我举一个简单的例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的,这是它的第一个缺陷。

然后补间动画还有一个缺陷,就是它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?很遗憾,我们只能靠自己去实现了。说白了,之前的补间动画机制就是使用硬编码的方式来完成的,功能限定死就是这些,基本上没有任何扩展性可言。

最后,补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。

插件&&热更

插件

插件化研究之DEXCLASSLOADER

插件化研究代ACTIVITY注册

插件化研究之资源冲突

VirtualAPK 解析

热更

Android热更新方案Robust

《深入探索Android热修复技术原理》

数据库

性能优化

性能优化之数据库优化

[Android SQLite操作性能优化办法-参考GreenDao](http://lcodecorex.github.io/2016/08/12/Android SQLite操作性能优化办法(参考GreenDao))

ORM数据库对比

GreenDao源码解析

本地广播和全局广播有什么差别

因广播数据在本应用范围内传播,不用担心隐私数据泄露的问题。 不用担心别的应用伪造广播,造成安全隐患。 相比在系统内发送全局广播,它更高效。

编码模式

Android App的设计架构:MVC,MVP,MVVM与架构经验谈

MVVM

MVP

组件化

探索 ARouter 原理

WMRouter:美团外卖Android开源路由框架

滴滴路由框架

每个应用程序分配的内存大小是多少-美团

android程序内存一般限制在16M,也有的是24M。近几年手机发展较快,一般都会分配两百兆左右,和具体机型有关。

注解处理器APT

Android编译时注解–入门篇(AbstractProcessor、APT)

Android APT工作原理(annotationProcessor)

渲染机制原理

Android渲染机制

深入理解渲染机制

设计模式

Android源码设计模式分析

专题

图片

面试官:

  1. 一张图片占用的内存怎么计算?
  2. 说下熟悉的图片框架是怎么缓存的?

基础:

Android中一张图片占据的内存大小是如何计算

图片库对比

picasso vs imageloader vs fresco vs glide

Android 三大图片缓存原理、特性对比

图片库源码解析

Glide

Picasso

Fresco

性能优化

工具

HierarchyViewer

TraceView

Systrace

性能工具Systrace

手把手教你使用Systrace(一)

手把手教你使用Systrace(二)——锁优化

Java 层面

Java 代码性能优化

JAVA线程池调优

Java多线程引发的性能问题以及调优策略

Android

移动端性能监控方案Hertz

Android 性能优化必知必会

耗电

Android性能优化系列之电量优化

大众点评App的短视频耗电量优化实战

流量

腾讯TMQ团队移动App的网络优化:24小时流量优化到原来15%历程

Android性能优化典范 - 第1季

  1. Render Performance Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fps,这意味着程序的大多数操作都必须在16ms内完成。我们可以通过一些工具来定位问题,比如可以使用HierarchyViewer来查找Activity中的布局是否过于复杂,也可以使用手机设置里面的开发者选项,打开Show GPU Overdraw等选项进行观察。你还可以使用TraceView来观察CPU的执行情况,更加快捷的找到性能瓶颈。
  2. Understanding Overdraw Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次的UI结构里面,如果不可见的UI也在做绘制的操作,这就会导致某些像素区域被绘制了多次。这就浪费大量的CPU以及GPU资源。Overdraw有时候是因为你的UI布局存在大量重叠的部分,还有的时候是因为非必须的重叠背景。例如某个Activity有一个背景,然后里面的Layout又有自己的背景,同时子View又分别有自己的背景。仅仅是通过移除非必须的背景图片,这就能够减少大量的红色Overdraw区域,增加蓝色区域的占比。这一措施能够显著提升程序性能。
  3. Understanding VSYNC Refresh Rate:代表了屏幕在一秒内刷新屏幕的次数,这取决于硬件的固定参数,例如60Hz。Frame Rate:代表了GPU在一秒内绘制操作的帧数,例如30fps,60fps。通常来说,帧率超过刷新频率只是一种理想的状况,在超过60fps的情况下,GPU所产生的帧数据会因为等待VSYNC的刷新信息而被Hold住,这样能够保持每次刷新都有实际的新的数据可以显示。但是我们遇到更多的情况是帧率小于刷新频率。
  4. Tool:Profile GPU Rendering 性能问题如此的麻烦,幸好我们可以有工具来进行调试。打开手机里面的开发者选项,选择Profile GPU Rendering,选中On screen as bars的选项。
  5. Why 60fps? 我们通常都会提到60fps与16ms,可是知道为何会是以程序是否达到60fps来作为App性能的衡量标准吗?这是因为人眼与大脑之间的协作无法感知超过60fps的画面更新。开发app的性能目标就是保持60fps,这意味着每一帧你只有16ms=1000/60的时间来处理所有的任务。
  6. Android, UI and the GPU 在Android里面那些由主题所提供的资源,例如Bitmaps,Drawables都是一起打包到统一的Texture纹理当中,然后再传递到GPU里面,这意味着每次你需要使用这些资源的时候,都是直接从纹理里面进行获取渲染的。当然随着UI组件的越来越丰富,有了更多演变的形态。例如显示图片的时候,需要先经过CPU的计算加载到内存中,然后传递给GPU进行渲染。文字的显示更加复杂,需要先经过CPU换算成纹理,然后再交给GPU进行渲染,回到CPU绘制单个字符的时候,再重新引用经过GPU渲染的内容。动画则是一个更加复杂的操作流程。为了能够使得App流畅,我们需要在每一帧16ms以内处理完所有的CPU与GPU计算,绘制,渲染等等操作。
  7. Invalidations, Layouts, and Performance 任何时候View中的绘制内容发生变化时,都会重新执行创建DisplayList,渲染DisplayList,更新到屏幕上等一系列操作。这个流程的表现性能取决于你的View的复杂程度,View的状态变化以及渲染管道的执行性能。举个例子,假设某个Button的大小需要增大到目前的两倍,在增大Button大小之前,需要通过父View重新计算并摆放其他子View的位置。修改View的大小会触发整个HierarcyView的重新计算大小的操作。如果是修改View的位置则会触发HierarchView重新计算其他View的位置。如果布局很复杂,这就会很容易导致严重的性能问题。我们需要尽量减少Overdraw。
  8. Overdraw, Cliprect, QuickReject 我们可以通过canvas.clipRect()来帮助系统识别那些可见的区域。这个方法可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视。这个API可以很好的帮助那些有多组重叠组件的自定义View来控制显示的区域。同时clipRect方法还可以帮助节约CPU与GPU资源,在clipRect区域之外的绘制指令都不会被执行,那些部分内容在矩形区域内的组件,仍然会得到绘制。
  9. Memory Churn and performance 执行GC操作的时候,所有线程的任何操作都会需要暂停,等待GC操作完成之后,其他操作才能够继续运行。Memory Churn内存抖动,内存抖动是因为大量的对象被创建又在短时间内马上被释放。瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,剩余空间不够的时候,也会触发GC。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。这个操作有可能会影响到帧率,并使得用户感知到性能问题。
  10. Garbage Collection in Android 原始JVM中的GC机制在Android中得到了很大程度上的优化。Android里面是一个三级Generation的内存模型,最近分配的对象会存放在Young Generation区域,当这个对象在这个区域停留的时间达到一定程度,它会被移动到Old Generation,最后到Permanent Generation区域。如果不小心在最小的for循环单元里面执行了创建对象的操作,这将很容易引起GC并导致性能问题。通过Memory Monitor我们可以查看到内存的占用情况,每一次瞬间的内存降低都是因为此时发生了GC操作,如果在短时间内发生大量的内存上涨与降低的事件,这说明很有可能这里有性能问题。我们还可以通过Heap and Allocation Tracker工具来查看此时内存中分配的到底有哪些对象。
  11. Performance Cost of Memory Leaks 内存泄漏指的是那些程序不再使用的对象无法被GC识别,这样就导致这个对象一直留在内存当中,占用了宝贵的内存空间。显然,这还使得每级Generation的内存区域可用空间变小,GC就会更容易被触发,从而引起性能问题。
  12. Memory Performance 通常来说,Android对GC做了大量的优化操作,虽然执行GC操作的时候会暂停其他任务,可是大多数情况下,GC操作还是相对很安静并且高效的。但是如果我们对内存的使用不恰当,导致GC频繁执行,这样就会引起不小的性能问题。
  13. Tool - Memory Monitor Android Studio中的Memory Monitor可以很好的帮助我们查看程序的内存使用情况。
  14. Battery Performance 我们应该尽量减少唤醒屏幕的次数与持续的时间,使用WakeLock来处理唤醒的问题,能够正确执行唤醒操作并根据设定及时关闭操作进入睡眠状态。某些非必须马上执行的操作,例如上传歌曲,图片处理等,可以等到设备处于充电状态或者电量充足的时候才进行。触发网络请求的操作,每次都会保持无线信号持续一段时间,我们可以把零散的网络请求打包进行一次操作,避免过多的无线信号引起的电量消耗。关于网络请求引起无线信号的电量消耗
  15. Understanding Battery Drain on Android 使用WakeLock或者JobScheduler唤醒设备处理定时的任务之后,一定要及时让设备回到初始状态。每次唤醒无线信号进行数据传递,都会消耗很多电量,它比WiFi等操作更加的耗电
  16. Battery Drain and WakeLocks 这正是JobScheduler API所做的事情。它会根据当前的情况与任务,组合出理想的唤醒时间,例如等到正在充电或者连接到WiFi的时候,或者集中任务一起执行。我们可以通过这个API实现很多免费的调度算法。

Android性能优化典范 - 第2季

  1. Battery Drain and Networking 我们可以有针对性的把请求行为捆绑起来,延迟到某个时刻统一发起请求。这部分主要会涉及到Prefetch(预取)与Compressed(压缩)这两个技术。对于Prefetch的使用,我们需要预先判断用户在此次操作之后,后续零散的请求是否很有可能会马上被触发,可以把后面5分钟有可能会使用到的零散请求都一次集中执行完毕。对于Compressed的使用,在上传与下载数据之前,使用CPU对数据进行压缩与解压,可以很大程度上减少网络传输的时间。
  2. Wear & Sensors 首先我们需要尽量使用Android平台提供的既有运动数据,而不是自己去实现监听采集数据,因为大多数Android Watch自身记录Sensor数据的行为是有经过做电量优化的。其次在Activity不需要监听某些Sensor数据的时候需要尽快释放监听注册。还有我们需要尽量控制更新的频率,仅仅在需要刷新显示数据的时候才触发获取最新数据的操作。另外我们可以针对Sensor的数据做批量处理,待数据累积一定次数或者某个程度的时候才更新到UI上。最后当Watch与Phone连接起来的时候,可以把某些复杂操作的事情交给Phone来执行,Watch只需要等待返回的结果。
  3. Smooth Android Wear Animation 在Android里面一个相对操作比较繁重的事情是对Bitmap进行旋转,缩放,裁剪等等。例如在一个圆形的钟表图上,我们把时钟的指针抠出来当做单独的图片进行旋转会比旋转一张完整的圆形图的所形成的帧率要高56%。
  4. Android Wear Data Batching 仅仅在真正需要刷新界面的时候才发出请求,尽量把计算复杂操作的任务交给Phone来处理,Phone仅仅在数据发生变化的时候才通知到Wear,把零碎的数据请求捆绑一起再进行操作。
  5. Object Pools 使用对象池技术有很多好处,它可以避免内存抖动,提升性能,但是在使用的时候有一些内容是需要特别注意的。通常情况下,初始化的对象池里面都是空白的,当使用某个对象的时候先去对象池查询是否存在,如果不存在则创建这个对象然后加入对象池,但是我们也可以在程序刚启动的时候就事先为对象池填充一些即将要使用到的数据,这样可以在需要使用到这些对象的时候提供更快的首次加载速度,这种行为就叫做预分配。使用对象池也有不好的一面,程序员需要手动管理这些对象的分配与释放,所以我们需要慎重地使用这项技术,避免发生对象的内存泄漏。为了确保所有的对象能够正确被释放,我们需要保证加入对象池的对象和其他外部对象没有互相引用的关系。
  6. To Index or Iterate? for index的方式有更好的效率,但是因为不同平台编译器优化各有差异,我们最好还是针对实际的方法做一下简单的测量比较好,拿到数据之后,再选择效率最高的那个方式。
  7. The Magic of LRU Cache 使用LRU Cache能够显著提升应用的性能,可是也需要注意LRU Cache中被淘汰对象的回收,否者会引起严重的内存泄露。
  8. Using LINT for Performance Tips Lint已经集成到Android Studio中了,我们可以手动去触发这个工具,点击工具栏的Analysis -> Inspect Code,触发之后,Lint会开始工作,并把结果输出到底部的工具栏,我们可以逐个查看原因并根据指示做相应的优化修改。
  9. Hidden Cost of Transparency 通常来说,对于不透明的View,显示它只需要渲染一次即可,可是如果这个View设置了alpha值,会至少需要渲染两次。
  10. Avoiding Allocations in onDraw() 首先onDraw()方法是执行在UI线程的,在UI线程尽量避免做任何可能影响到性能的操作。虽然分配内存的操作并不需要花费太多系统资源,但是这并不意味着是免费无代价的。设备有一定的刷新频率,导致View的onDraw方法会被频繁的调用,如果onDraw方法效率低下,在频繁刷新累积的效应下,效率低的问题会被扩大,然后会对性能有严重的影响。
  11. Tool: Strict Mode Android提供了一个叫做Strict Mode的工具,我们可以通过手机设置里面的开发者选项,打开Strict Mode选项,如果程序存在潜在的隐患,屏幕就会闪现红色。我们也可以通过StrictMode API在代码层面做细化的跟踪,可以设置StrictMode监听那些潜在问题,出现问题时如何提醒开发者,可以对屏幕闪红色,也可以输出错误日志。
  12. Custom Views and Performance Useless calls to onDraw():我们知道调用View.invalidate()会触发View的重绘,有两个原则需要遵守,第1个是仅仅在View的内容发生改变的时候才去触发invalidate方法,第2个是尽量使用ClipRect等方法来提高绘制的性能。Useless pixels:减少绘制时不必要的绘制元素,对于那些不可见的元素,我们需要尽量避免重绘。Wasted CPU cycles:对于不在屏幕上的元素,可以使用Canvas.quickReject把他们给剔除,避免浪费CPU资源。另外尽量使用GPU来进行UI的渲染,这样能够极大的提高程序的整体表现性能。
  13. Batching Background Work Until Later 1.AlarmManager 使用AlarmManager设置定时任务,可以选择精确的间隔时间,也可以选择非精确时间作为参数。除非程序有很强烈的需要使用精确的定时唤醒,否者一定要避免使用他,我们应该尽量使用非精确的方式。2.SyncAdapter 我们可以使用SyncAdapter为应用添加设置账户,这样在手机设置的账户列表里面可以找到我们的应用。这种方式功能更多,但是实现起来比较复杂。我们可以从这里看到官方的培训课程:http://developer.android.com/training/sync-adapters/index.html 3.JobSchedulor 这是最简单高效的方法,我们可以设置任务延迟的间隔,执行条件,还可以增加重试机制。
  14. Smaller Pixel Formats Android的Heap空间是不会自动做兼容压缩的,意思就是如果Heap空间中的图片被收回之后,这块区域并不会和其他已经回收过的区域做重新排序合并处理,那么当一个更大的图片需要放到heap之前,很可能找不到那么大的连续空闲区域,那么就会触发GC,使得heap腾出一块足以放下这张图片的空闲区域,如果无法腾出,就会发生OOM。
  15. Smaller PNG Files 尽量减少PNG图片的大小是Android里面很重要的一条规范。相比起JPEG,PNG能够提供更加清晰无损的图片,但是PNG格式的图片会更大,占用更多的磁盘空间。到底是使用PNG还是JPEG,需要设计师仔细衡量,对于那些使用JPEG就可以达到视觉效果的,可以考虑采用JPEG即可。
  16. Pre-scaling Bitmaps 对bitmap做缩放,这也是Android里面最遇到的问题。对bitmap做缩放的意义很明显,提示显示性能,避免分配不必要的内存。Android提供了现成的bitmap缩放的API,叫做createScaledBitmap()
  17. Re-using Bitmaps 使用inBitmap属性可以告知Bitmap解码器去尝试使用已经存在的内存区域,新解码的bitmap会尝试去使用之前那张bitmap在heap中所占据的pixel data内存区域,而不是去问内存重新申请一块区域来存放bitmap。利用这种特性,即使是上千张的图片,也只会仅仅只需要占用屏幕所能够显示的图片数量的内存大小。
  18. The Performance Lifecycle Gather:收集数据,Insight:分析数据,Action:解决问题

Android性能优化典范 - 第3季

  1. Fun with ArrayMaps 为了解决HashMap更占内存的弊端,Android提供了内存效率更高的ArrayMap。它内部使用两个数组进行工作,其中一个数组记录key hash过后的顺序列表,另外一个数组按key的顺序记录Key-Value值
  2. Beware Autoboxing 有时候性能问题也可能是因为那些不起眼的小细节引起的,例如在代码中不经意的“自动装箱”。我们知道基础数据类型的大小:boolean(8 bits), int(32 bits), float(32 bits),long(64 bits),为了能够让这些基础数据类型在大多数Java容器中运作,会需要做一个autoboxing的操作,转换成Boolean,Integer,Float等对象
  3. SparseArray Family Ties 为了避免HashMap的autoboxing行为,Android系统提供了SparseBoolMap,SparseIntMap,SparseLongMap,LongSparseMap等容器。
  4. The price of ENUMs Android官方强烈建议不要在Android程序里面使用到enum。
  5. Trimming and Sharing Memory Android系统提供了一些回调来通知应用的内存使用情况,通常来说,当所有的background应用都被kill掉的时候,forground应用会收到onLowMemory()的回调。在这种情况下,需要尽快释放当前应用的非必须内存资源,从而确保系统能够稳定继续运行。Android系统还提供了onTrimMemory()的回调,当系统内存达到某些条件的时候,所有正在运行的应用都会收到这个回调
  6. DO NOT LEAK VIEWS 避免使用异步回调,避免使用Static对象,避免把View添加到没有清除机制的容器里面
  7. Location & Battery Drain 其中存在的一个优化点是,我们可以通过判断返回的位置信息是否相同,从而决定设置下次的更新间隔是否增加一倍,通过这种方式可以减少电量的消耗
  8. Double Layout Taxation 布局中的任何一个View一旦发生一些属性变化,都可能引起很大的连锁反应。例如某个button的大小突然增加一倍,有可能会导致兄弟视图的位置变化,也有可能导致父视图的大小发生改变。当大量的layout()操作被频繁调用执行的时候,就很可能引起丢帧的现象。
  9. Network Performance 101 减少移动网络被激活的时间与次数,压缩传输数据
  10. Effective Network Batching 发起网络请求与接收返回数据都是比较耗电的,在网络硬件模块被激活之后,会继续保持几十秒的电量消耗,直到没有新的网络操作行为之后,才会进入休眠状态。前面一个段落介绍了使用Batching的技术来捆绑网络请求,从而达到减少网络请求的频率。那么如何实现Batching技术呢?通常来说,我们可以会把那些发出的网络请求,先暂存到一个PendingQueue里面,等到条件合适的时候再触发Queue里面的网络请求。
  11. Optimizing Network Request Frequencies 前面的段落已经提到了应该减少网络请求的频率,这是为了减少电量的消耗。我们可以使用Batching,Prefetching的技术来避免频繁的网络请求。Google提供了GCMNetworkManager来帮助开发者实现那些功能,通过提供的API,我们可以选择在接入WiFi,开始充电,等待移动网络被激活等条件下再次激活网络请求。
  12. Effective Prefetching 类似上面的情况会频繁触发网络请求,但是如果我们能够预先请求后续可能会使用到网络资源,避免频繁的触发网络请求,这样就能够显著的减少电量的消耗。可是预先获取多少数据量是很值得考量的,因为如果预取数据量偏少,就起不到减少频繁请求的作用,可是如果预取数据过多,就会造成资源的浪费。

Android性能优化典范 - 第4季

  1. Cachematters for networking 想要使得Android系统上的网络访问操作更加的高效就必须做好网络数据的缓存。这是提高网络访问性能最基础的步骤之一。从手机的缓存中直接读取数据肯定比从网络上获取数据要更加的便捷高效,特别是对于那些会被频繁访问到的数据,需要把这些数据缓存到设备上,以便更加快速的进行访问。
  2. Optimizing Network Request Frequencies 首先我们要对网络行为进行分类,区分需要立即更新数据的行为和其他可以进行延迟的更新行为,为不同的场景进行差异化处理。其次要避免客户端对服务器的轮询操作,这样会浪费很多的电量与带宽流量。解决这个问题,我们可以使用Google Cloud Message来对更新的数据进行推送。然后在某些必须做同步的场景下,需要避免使用固定的间隔频率来进行更新操作,我们应该在返回的数据无更新的时候,使用双倍的间隔时间来进行下一次同步。最后更进一步,我们还可以通过判断当前设备的状态来决定同步的频率,例如判断设备处于休眠,运动等不同的状态设计各自不同时间间隔的同步频率。
  3. Effective Prefetching 到底预取多少才比较合适呢?一个比较普适的规则是,在3G网络下可以预取1-5Mb的数据量,或者是按照提前预期后续1-2分钟的数据作为基线标准。在实际的操作当中,我们还需要考虑当前的网络速度来决定预取的数据量,例如在同样的时间下,4G网络可以获取到12张图片的数据,而2G网络则只能拿到3张图片的数据。所以,我们还需要把当前的网络环境情况添加到设计预取数据量的策略当中去。判断当前设备的状态与网络情况,可以使用前面提到过的GCMNetworkManager。
  4. Adapting to Latency 一个典型的网络操作行为,通常包含以下几个步骤:首先手机端发起网络请求,到达网络服务运营商的基站,再转移到服务提供者的服务器上,经过解码之后,接着访问本地的存储数据库,获取到数据之后,进行编码,最后按照原来传递的路径逐层返回。常来说,我们可以把网络请求延迟划分为三档:例如把网络延迟小于60ms的划分为GOOD,大于220ms的划分为BAD,介于两者之间的划分为OK(这里的60ms,220ms会需要根据不同的场景提前进行预算推测)。
  5. Minimizing Asset Payload 为了能够减小网络传输的数据量,我们需要对传输的数据做压缩的处理,这样能够提高网络操作的性能。首先需要做的是减少图片的大小,其次需要做的是减少序列化数据的大小。
  6. Service Performance Patterns Service是Android程序里面最常用的基础组件之一,但是使用Service很容易引起电量的过度消耗以及系统资源的未及时释放。避免错误的使用Service,例如我们不应该使用Service来监听某些事件的变化,不应该搞一个Service在后台对服务器不断的进行轮询(应该使用Google Cloud Messaging)。如果已经事先知道Service里面的任务应该执行在后台线程(非默认的主线程)的时候,我们应该使用IntentService或者结合HanderThread,AsycnTask Loader实现的Service。
  7. Removing unused code Android为我们提供了Proguard的工具来帮助应用程序对代码进行瘦身,优化,混淆的处理。它会帮助移除那些没有使用到的代码,还可以对类名,方法名进行混淆处理以避免程序被反编译。
  8. Removing unused resources 所幸的是,我们可以使用Gradle来帮助我们分析代码,分析引用的资源,对于那些没有被引用到的资源,会在编译阶段被排除在APK安装包之外,要实现这个功能,对我们来说仅仅只需要在build.gradle文件中配置shrinkResource为true就好了
  9. Perf Theory: Caching 当我们讨论性能优化的时候,缓存是最常见最有效的策略之一。无论是为了提高CPU的计算速度还是提高数据的访问速度,在绝大多数的场景下,我们都会使用到缓存。
  10. Perf Theory: Approximation(近似法) 例如使用一张比较接近实际大小的图片来替代原图,换取更快的加载速度。所以对于那些对计算结果要求不需要十分精确的场景,我们可以使用近似法则来提高程序的性能。
  11. Perf Theory: Culling(遴选,挑选) 一个提高性能的方法是逐步对数据进行过滤筛选,减小搜索的数据集,以此提高程序的执行性能。例如我们需要搜索到居住在某个地方,年龄是多少,符合某些特定条件的候选人,就可以通过逐层过滤筛选的方式来提高后续搜索的执行效率。
  12. Perf Theory: Threading 使用多线程并发处理任务,从某种程度上可以快速提高程序的执行性能。对于Android程序来说,主线程通常也成为UI线程,需要处理UI的渲染,响应用户的操作等等。
  13. Perf Theory: Batching 网络请求的批量执行是另外一个比较适合说明batching使用场景的例子,因为每次发起网络请求都相对来说比较耗时耗电,如果能够做到批量一起执行,可以大大的减少电量的消耗。
  14. Serialization performance 数据序列化的行为可能发生在数据传递过程中的任何阶段,例如网络传输,不同进程间数据传递,不同类之间的参数传递,把数据存储到磁盘上等等。通常情况下,我们会把那些需要序列化的类实现Serializable接口(如下图所示),但是这种传统的做法效率不高,实施的过程会消耗更多的内存。但是我们如果使用GSON库来处理这个序列化的问题,不仅仅执行速度更快,内存的使用效率也更高。Android的XML布局文件会在编译的阶段被转换成更加复杂的格式,具备更加高效的执行性能与更高的内存使用效率。
  15. Smaller Serialized Data 数据呈现的顺序以及结构会对序列化之后的空间产生不小的影响。
  16. Caching UI data 缓存UI界面上的数据,可以采用方案有存储到文件系统,Preference,SQLite等等,做了缓存之后,这样就可以在请求数据返回结果之前,呈现给用户旧的数据,而不是使用正在加载的方式让用户什么数据都看不到,当然在请求网络最新数据的过程中,需要有正在刷新的提示。至于到底选择哪个方案来对数据进行缓存,就需要根据具体情况来做选择了。
  17. CPU Frequency Scaling 调节CPU的频率会执行的性能产生较大的影响,为了最大化的延长设备的续航时间,系统会动态调整CPU的频率,频率越高执行代码的速度自然就越快。我们可以使用Systrace工具来导出CPU的执行情况,以便帮助定位性能问题。

Android性能优化典范 - 第5季

  1. Threading Performance AsyncTask: 为UI线程与工作线程之间进行快速的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的使用场景。HandlerThread: 为某些回调方法或者等待某些任务的执行设置一个专属的线程,并提供线程任务的调度机制。ThreadPool: 把任务分解成不同的单元,分发到各个不同的线程上,进行同时并发处理。IntentService: 适合于执行由UI触发的后台Service任务,并可以把后台任务执行的情况通过一定的机制反馈给UI。
  2. Understanding Android Threading 通常来说,一个线程需要经历三个生命阶段:开始,执行,结束。线程会在任务执行完毕之后结束,那么为了确保线程的存活,我们会在执行阶段给线程赋予不同的任务,然后在里面添加退出的条件从而确保任务能够执行完毕后退出。
  3. Memory & Threading 不要在任何非UI线程里面去持有UI对象的引用。系统为了确保所有的UI对象都只会被UI线程所进行创建,更新,销毁的操作,特地设计了对应的工作机制(当Activity被销毁的时候,由该Activity所触发的非UI线程都将无法对UI对象进行操作,否者就会抛出程序执行异常的错误)来防止UI对象被错误的使用。
  4. Good AsyncTask Hunting AsyncTask虽然提供了一种简单便捷的异步机制,但是我们还是很有必要特别关注到他的缺点,避免出现因为使用错误而导致的严重系统性能问题。
  5. Getting a HandlerThread HandlerThread比较合适处理那些在工作线程执行,需要花费时间偏长的任务。我们只需要把任务发送给HandlerThread,然后就只需要等待任务执行结束的时候通知返回到主线程就好了。另外很重要的一点是,一旦我们使用了HandlerThread,需要特别注意给HandlerThread设置不同的线程优先级,CPU会根据设置的不同线程优先级对所有的线程进行调度优化。
  6. Swimming in Threadpools 线程池适合用在把任务进行分解,并发进行执行的场景。通常来说,系统里面会针对不同的任务设置一个单独的守护线程用来专门处理这项任务。
  7. The Zen of IntentService 默认的Service是执行在主线程的,可是通常情况下,这很容易影响到程序的绘制性能(抢占了主线程的资源)。除了前面介绍过的AsyncTask与HandlerThread,我们还可以选择使用IntentService来实现异步操作。IntentService继承自普通Service同时又在内部创建了一个HandlerThread,在onHandlerIntent()的回调里面处理扔到IntentService的任务。所以IntentService就不仅仅具备了异步线程的特性,还同时保留了Service不受主页面生命周期影响的特点。
  8. Threading and Loaders 当启动工作线程的Activity被销毁的时候,我们应该做点什么呢?为了方便的控制工作线程的启动与结束,Android为我们引入了Loader来解决这个问题。我们知道Activity有可能因为用户的主动切换而频繁的被创建与销毁,也有可能是因为类似屏幕发生旋转等被动原因而销毁再重建。在Activity不停的创建与销毁的过程当中,很有可能因为工作线程持有Activity的View而导致内存泄漏(因为工作线程很可能持有View的强引用,另外工作线程的生命周期还无法保证和Activity的生命周期一致,这样就容易发生内存泄漏了)。除了可能引起内存泄漏之外,在Activity被销毁之后,工作线程还继续更新视图是没有意义的,因为此时视图已经不在界面上显示了。
  9. The Importance of Thread Priority 在Android系统里面,我们可以通过android.os.Process.setThreadPriority(int)设置线程的优先级,参数范围从-20到24,数值越小优先级越高。Android系统还为我们提供了以下的一些预设值,我们可以通过给不同的工作线程设置不同数值的优先级来达到更细粒度的控制。
  10. Profile GPU Rendering : M Update 从Android M系统开始,系统更新了GPU Profiling的工具来帮助我们定位UI的渲染性能问题。早期的CPU Profiling工具只能粗略的显示出Process,Execute,Update三大步骤的时间耗费情况。

Android性能优化典范 - 第6季

  1. App Launch time 101 提高程序的启动速度意义重大,很显然,启动时间越短,用户才越有耐心等待打开这个APP进行使用,反之启动时间越长,用户则越有可能来不及等到APP打开就已经切换到其他APP了。程序启动过程中的那些复杂错误的操作很可能导致严重的性能问题。Android系统会根据用户的操作行为调整程序的显示策略,用来提高程序的显示性能。例如,一旦用户点击桌面图标,Android系统会立即显示一个启动窗口,这个窗口会一直保持显示直到画面中的元素成功加载并绘制完第一帧。这种行为常见于程序的冷启动,或者程序的热启动场景(程序从后台被唤起或者从其他APP界面切换回来)。那么关键的问题是,用户很可能会因为从启动窗口到显示画面的过程耗时过长而感到厌烦,从而导致用户没有来得及等程序启动完毕就切换到其他APP了。更严重的是,如果启动时间过长,可能导致程序出现ANR。我们应该避免出现这两种糟糕的情况。
  2. App Launch Time & Activity Creation 优化布局耗时:一个布局层级越深,里面包含需要加载的元素越多,就会耗费更多的初始化时间。关于布局性能的优化,这里就不展开描述了!异步延迟加载:一开始只初始化最需要的布局,异步加载图片,非立即需要的组件可以做延迟加载。
  3. App Launch Time & Bloated Application Objects 优化这些问题的解决方案是做延迟加载,可以在application里面做延迟加载,也可以把一些初始化的操作延迟到组件真正被调用到的时候再做加载。
  4. App Launch Time & Theme Launch Screens 目前大多数开发者都会通过设置启动窗口主题的方式来替换系统默认的启动窗口,通过这种方式只是使用『障眼法』弱化了用户对启动时间的感知,但本质上并没有对启动速度做什么优化。也有些APP通过关闭启动窗口属性android:windowDisablePreview的方式来直接移除系统默认的启动窗口,但是这样的弊端是用户从点击桌面图标到真的看到实际页面的这段时间当中,画面没有任何变化,这样的用户体验是十分糟糕的!
  5. Smaller APKs: A Checklist 1)确保在build.gradle文件中开启了minifEnabled与shrinkResources的属性,这两个属性可以帮助移除那些在程序中使用不到的代码与资源,帮助减少APP的安装包大小。2)有选择性的提供对应分辨率的图片资源,系统会自动匹配最合适分辨率的图片并执行拉伸或者压缩的处理。3)在符合条件的情况下,使用Vertor Drawable替代传统的PNG/JPEG图片,能够极大的减少图片资源的大小。传统模式下,针对不同dpi的手机都需要提供一套PNG/JPEG的图片,而如果使用Vector Drawable的话,只需要一个XML文件即可。4)尽量复用已经存在的资源图片,使用代码的方式对已有的资源进行复用 5)开启MinifEnabled,Proguard。打开这些编译属性之后,程序在打包的时候就不会把没有引用到的代码编译进来,以此达到减少安装包大小的目的。6)注意因为编译行为额外产生的方法数,例如类似Enum,Protocal Buffer可能导致方法数与类的个数增加。7)部分引入到工程中的jar类库可能并不是专门针对移动端APP而设计的,他们最开始可能是运用在PC或者Server上的。使用这些类库不仅仅额外增加了包的大小,还增加了编译时间。单纯依靠Proguard可能无法完全移除那些使用不到的方法,最佳的方式是使用一些更加轻量化,专门为Android APP设计的jar类库。
  6. VectorDrawable for smaller APKs 针对不同的分辨率提供多张精度的图片会额外增加APK的大小,针对这个问题的解决方案是考虑使用VectorDrawable,它仅仅只需要一个文件,能够动态生成对应分辨率的图片。

官方性能优化系列教程

总经理喜欢问的问题

CEO最喜欢的面试问题,HR你问过几个?

数十个创业者推荐的46个面试问题

得到CEO脱不花:面试一个人,你只问他/她四个问题就够了 !

深入研究的知识点

现在Android技术发展到了中后期,针对技术的考核不再纠结于知识点,或者说技术人员之间在知识点上已经无太大区别,这个时候面试更加倾向于知识深入的能力、技术思维等,比如面试中面试官经常会说,你有没有研究的一些深入的点,如果没有准备经常会一脸懵逼,也不知道这个代表什么意思。

我们获取知识,基本有两种,一种是主动根据知识图谱学习,还有就是根据经历的业务,这两种方式能使某一个点的深入研究达到业界水平,基本上也是由于业务本身的需要,像一些阿里的高P,基本都是跟着淘宝的黄金十年成长起来的。大部分人都是没机会接触到这样的业务,但并不代表能力不行。下面我会举几个值得深入研究的点,供大家参考。

TextView性能问题

直播间聊天框或者大量图文混排业务,使用原生TextView会出现性能问题,相关研究方案如下:

TextView性能瓶颈,渲染优化,以及StaticLayout的一些用处

[译]Instagram是如何提升TextView渲染性能的

TextView预渲染研究

日志性能

日志相关的可以参考微信的研究,这里面有很多可以具体深入的点:mmap、压缩算法等,甚至可以扩展到Linux系统是如何做存储的,相关的一整套方案

微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog

认真分析mmap:是什么 为什么 怎么用

存储技术原理分析_基于Linux 2.6内核源代码

研究过大型开源项目源码

深入阅读过一些大型开源项目的源码,也是一种可深入的能力体现

[IntelliJ IDEA 引用搜索原理](http://www.jackywang.tech/2018/05/22/IntelliJ IDE 搜索引用如何实现?/)

Android 源码分析项目

内存优化

内存优化也可以作为一个专项研究,像内存分类、线程消耗等等

我这样减少了26.5M Java内存!

垃圾回收

Android GC 原理探究

java.lang.UnsatisfiedLinkError

如果项目中大量使用到so库的话,可能会经常碰到这个error,可以从这个点下去研究so库是如何加载,以及如何解决这个问题

Android 动态链接库加载原理及 HotFix 方案介绍

浏览器相关

性能优化:

WebView性能、体验分析与优化

  1. 全局的webview,预加载
  2. 初始化、加载数据并行
  3. DNS采用和客户端API相同的域名

70%以上业务由H5开发,手机QQ Hybrid 的架构如何优化演进?

安全:

【腾讯御安全】Android安全开发之WebView中的地雷

简析Chrome和Webkit的渊源

内核历史

浅读V8——强大的JavaScript引擎

Android Chromium WebView学习启动篇

Webview 渲染机制

Chromium网页渲染机制简要介绍和学习计划

Android WebView硬件加速渲染网页UI的过程分析

视频

Chromium视频标签 video 简要介绍和学习计划

多进程

Chromium多进程架构简要介绍和学习计划

百度

笔试题

  1. 冒泡排序
  2. 四大组件的生命周期和简单用法
  3. 图片框架缓存实现
  4. 设计网络请求框架

问答

  1. LruCache默认缓存大小
  2. httpurlconnection 和 okhttp关系(HTTPurlconnection 底层基于OKHTTP)
  3. 开源库源码分析
  4. 从0设计一款App整体架构(可以结合微信和淘宝架构演进方面进行回答)

算法

算法是其次,主要是写码能力与熟练度。 作者:文航 链接 来源:知乎

算法虽然在平常工作中接触的可能不多,但是面试还会可能遇到,尤其是一些知名外企如微软、Facebook、Google,因为面试的人都很优秀,所以算法基础、专业技术能力、衍生技术能力都需要,但也并不是绝对。

刷题的话建议大家直接使用VSCode安装Leetcode插件。

以下内容来自极客时间覃超老师算法课内容,只是为了面试可以只刷下面列的重点题。

刷题方式:

五毒神掌

第一遍:不要死磕 要看代码学习(一定要看国际版的高票回答)

第二遍:自己写

第三遍:24小时后

第四遍:一周后

第五遍:面试前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
刷题第一遍
- 5~10分钟读题加思考
- 然后直接看解法。注意!多解法,比较解法优劣
- 背诵、默写好的解法

刷题第二遍
- 马上自己写->提交LeetCode反复debug至通过
- 多种解法比较、体会->优化。LeetCode有统计执行时间和内存消耗

刷题第三遍
- 过了一天后,再重复做题
- 不同解法的熟练程度->专项练习

刷题第四遍
过了一周再反复回来练习,同时对于不熟练的题目再进行专项练习

刷题第五遍
面试前一周恢复性训练

数组 链表

  1. 两数之和
  2. 盛最多水的容器
  3. 移动零
  4. 爬楼梯
  5. 三数之和
  6. 反转链表
  7. 两两交换链表中的节点
  8. 环形链表
  9. 环形链表 II
  10. K 个一组翻转链表 (困难)

栈 队列 优先队列 双端队列

  1. 有效的括号
  2. 最小栈
  3. 柱状图中最大的矩形(困难)
  4. 滑动窗口最大值(困难)
  5. 设计循环双端队列

哈希表 映射 集合

  1. 有效的字母异位词
  2. 字母异位词分组
  3. 两数之和

树 二叉树 二叉搜索树

堆和二叉堆、图

泛型递归、树的递归

分治 回溯

深度优先搜索和广度优先搜索

贪心算法

二分

动态规划

字典树和并查集

高级搜索

红黑树和AVL树

漫画:什么是红黑树?

位运算

布隆过滤器 LRU缓存

LRU 缓存机制

排序算法

高级动态规划

字符串算法

刷题路线

基础

深度优先搜索

回溯

分治

动态规划

网络

面试官:

  1. Http & Https的区别?
  2. Https 的三次握手是怎样的过程?
  3. 为啥要用Https?
  4. 对称加密 & 非对称加密?
  5. Http 1.0 vs Http 2.0?
  6. 为什么需要三次握手?两次会有什么问题?为什么需要四次挥手,两次行不行?
  7. DNS 有啥缺点?为啥国内要用HttpDNS?
  8. 网络如何分层的?5层分别是啥?为啥要做5层分层?每层都分别干啥事情?

协议

http 1.0 vs http 2.0s

网络编程

流行网络库对比

第三方库源码解析

网络优化

常问问题

腾讯

腾讯音乐

2000万个整数,找出第五十大的数字?

冒泡、选择、建堆

从网络加载一个10M的图片,说下注意事项

图片缓存、异常恢复、质量压缩

自定义View注意事项

渲染帧率、内存

项目中常用的设计模式

单例、观察者、适配器、建造者。。

JVM的理解

深入理解 Java 内存模型(一)——基础

线程sleep对消息的影响

如果在当前线程内使用Handler postdelayed 两个消息,一个延迟5s,一个延迟10s,然后使当前线程sleep 5秒,以上消息的执行时间会如何变化? 答:照常执行

扩展:sleep时间<=5 对两个消息无影响,5< sleep时间 <=10 对第一个消息有影响,第一个消息会延迟到sleep后执行,sleep时间>10 对两个时间都有影响,都会延迟到sleep后执行。

触摸事件传递流程

手写锁竞争

算法-实现开平方函数

逻辑思维

阿里面试题

LeakCanary 实现原理

LeakCanary核心原理源码浅析

内存泄露的本质

无法回收无用的对象

Handler 队列阻塞算法、在Android中的地位、如何自己实现?

Android应用程序消息处理机制(Looper、Handler)分析

C 和 java 如何通信?JNI原理

Java JNI实现原理初探

比较熟悉的开源项目源码分析

okhttp、glide

线程池定制、源码分析

虚拟机如何实现的synchronized?

Java SE1.6中的Synchronized

一个文件中有100万个整数,由空格分开,在程序中判断用户输入的整数是否在此文件中。说出最优的方法

两个进程同时要求写或者读,能不能实现?如何防止进程的同步?

volatile 的意义?

防止CPU指令重排序

单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton{
private volatile static Singleton mSingleton;
private Singleton(){
}
public static Singleton getInstance(){
if(mSingleton == null){\\A
synchronized(Singleton.class){\\C
if(mSingleton == null)
mSingleton = new Singleton();\\B
}
}
return mSingleton;
}
}

Given a string, determine if it is a palindrome(回文,如果不清楚,按字面意思脑补下), considering only alphanumeric characters and ignoring cases.

For example, “A man, a plan, a canal: Panama” is a palindrome. “race a car” is not a palindrome.

Note: Have you consider that the string might be empty? This is a good question to ask during an interview. For the purpose of this problem, we define empty string as valid palindrome.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public boolean isPalindrome(String palindrome){
char[] palindromes = palidrome.toCharArray();
if(palindromes.lengh == 0){
return true
}
Arraylist<Char> temp = new Arraylist();
for(int i=0;i<palindromes.length;i++){
if((palindromes[i]>'a' && palindromes[i]<'z')||palindromes[i]>'A' && palindromes[i]<'Z')){
temp.add(palindromes[i].toLowerCase());
}
}
for(int i=0;i<temp.size()/2;i++){
if(temp.get(i) != temp.get(temp.size()-i)){
//
return false;
}
}
return true;
}

烧一根不均匀的绳,从头烧到尾总共需要1个小时。现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢

A绳从两头烧,同时B绳只烧一头,30分钟后,A烧完了,同时B绳另一头也点燃,开始两头烧,烧完是15分钟

加起来就是45分钟。

一个小时十五分钟,那就再取一根C绳从两头烧,烧完30分钟,正好!