应该是史上最全最新Java和Android面试题目(自己总结和收集的)
16lz
2021-01-25
Android面试题目
- Java
- 基础
- int占用几个字节
- 讲一下常见编码方式?
- UTF-8编码下中文占几个字节
- int和Interger的区别
- int、char、long各占多少字节数
- string 转换成 integer的方式及原理
- java中==和equals和hashCode的区别
- 字节流和字符流的区别
- 字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元(2个字节)。
- 字节流默认不使用缓冲区;字符流使用缓冲区。
- 字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。
- 序列化的方法:serializable和parcelable的优缺点
- 谈谈对java多态的理解
- 什么是内部类?内部类的作用
- 静态内部类的设计意图
- 成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用
- 闭包和局部内部类的区别
- 抽象类的意义
- 抽象类与接口的应用场景
- 抽象类跟接口的区别
- 接口的意义
- 父类的静态方法能否被子类重写
- 静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
- 抽象类是否可以没有方法和属性?
- 进程和线程的区别
- 泛型的通配符(上下通配符) extend和super
- 谈谈对kotlin的理解
- 哪些情况下的对象会被垃圾回收机制处理掉?
- 反射(Reflection)
- Java类是被Java虚拟机加载,如果Java类不被Java虚拟机加载,就不能正常运行,正常情况下,我们运行所有程序在编译期时候就已经把那个类被加载了。
- Java的反射机制是在编译时并不确定是哪个类被加载了,而是在程序运行的时候才被加载、探知、自审。使用的是在编译期并不知道的类,这就是Java反射的特点。
- Java反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。如eclipse中,一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择,这就是利用反射机制,做到代码的智能提示。
- 反射使用场景
- 1.工厂模式:Factory类中用反射的话,添加了一个新的类之后,就不需要再修改工厂类Factory了
- 2.数据库JDBC中通过Class.forName(Driver).来获得数据库连接驱动
- 3.分析类文件:毕竟能得到类中的方法等等
- 4.访问一些不能访问的变量或属性:破解别人代码
- 注解(Annotation )
- 注解是 Java 5 的一个新特性。注解是插入你代码中的一种注释或者说是一种元数据(meta data)。这些注解信息可以在编译期使用预编译工具进行处理(pre-compiler tools),也可以在运行期使用 Java反射机制进行处理
- Annotation 对程式运行没有影响,它的目的在对编译器或分析工具说明程式的某些资讯,您可以在包、类、方法、成员等加上Annotation,每一个Annotation 对应于一个实际的Annotation 型态,您可以从java.lang.Override、java.lang.Deprecated、java.lang.SuppressWarnings 这三个J2SE 5.0 中标准的Annotation 型态开始了解Annotation 的作用。
- 注解使用场景
- 1,设计一个原始码分析工具,分析代码等
- 2,日志信息打
- String,StringBuffer, StringBuilder 的区别是什么?String为什么是不可变的?
- final、finally、finalize区别
- final 可以修饰类、变量和方法。修饰类代表这个类不可被继承。修饰变量代表此变量不可被改变。修饰方法表示此方法不可被重写 (override)。
- finally 是保证重点代码一定会执行的一种机制。通常是使用 try-finally 或者 try-catch-finally 来进行文件流的关闭等操作。
- finalize 是 Object 类中的一个方法,它的设计目的是保证对象在垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9已经被标记为 deprecated。
- 谈谈你对解析与分派的认识
- Java的异常体系
- Exception 和 Error的区别
- Exception 和 Error 都继承于 Throwable,在 Java 中,只有 Throwable 类型的对象才能被 throw 或者 catch,它是异常处理机制的基本组成类型。
- Exception 和 Error 体现了 Java 对不同异常情况的分类。Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应的处理。
- Error 是指在正常情况下,不大可能出现的情况,绝大部分 Error 都会使程序处于非正常、不可恢复的状态。既然是非正常,所以不便于也不需要捕获,常见的 OutOfMemoryError 就是 Error 的子类。
- Exception 又分为 checked Exception 和 unchecked Exception。
- checked Exception 在代码里必须显式的进行捕获,这是编译器检查的一部分。
- unchecked Exception 也就是运行时异常,类似空指针异常、数组越界等,通常是可以避免的逻辑错误,具体根据需求来判断是否需要捕获,并不会在编译器强制要求
- 静态代理和动态代理的区别,什么场景使用?
- 动态代理
- 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理
- 静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。
- 动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。
- 一个典型的动态代理创建对象过程可分为以下四个步骤:
- 1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
- 2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
- Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
- 3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
- Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
- 4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
- Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler))
- 1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
- 2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
- Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
- 3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
- Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
- 4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
- Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
- 修改对象A的equals方法的签名,那么使用HashMap存放这个对象实例的时候,会调用哪个equals方法?
- Java中实现多态的机制是什么?
- 如何将一个Java对象序列化到文件里?
- 说说你对Java注解的理解
- 说说你对依赖注入的理解
- 说一下泛型原理,并举例说明
- Java中String的了解
- String为什么要设计成不可变的?
- Object类的equal和hashCode方法重写,为什么?
- 集合
- 常用数据结构简介
- 并发集合了解哪些?
- 列举java的集合以及集合之间的继承关系
- 集合类以及集合框架
- 容器类介绍以及之间的区别(Java容器主要可以划分为4个部分:List列表、Set集合、Map映射、工具类(Iterator迭代器、Enumeration枚举类、Arrays和Collections)
- List,Set,Map的区别
- List和Map的实现方式以及存储方式
- ArrayList 结构
- Vector、ArrayList 和 LinkedList 的区别?造成区别的原因是什么,为什么删查的复杂度不同
- HashMap 实现原理 数据结构和源码理解
- 什么时候会使用HashMap?他有什么特点?
- 是基于Map接口的实现,存储键值对时,它可以接收null的键值,是非同步的,HashMap存储着Entry(hash, key, value, next)对象。
- HashMap的工作原理
- 通过hash的方法,通过put和get存储和获取对象。存储对象时,我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Facotr则resize为原来的2倍)。获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。如果发生碰撞的时候,Hashmap通过链表将产生碰撞冲突的元素组织起来,在Java 8中,如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。
- 你知道get和put的原理吗?equals()和hashCode()的都有什么作用?
- 通过对key的hashCode()进行hashing,并计算下标( n-1 & hash),从而获得buckets的位置。如果产生碰撞,则利用key.equals()方法去链表或树中去查找对应的节点
- 你知道hash的实现吗?为什么要这样实现?
- 在Java 1.8的实现中,是通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在bucket的n比较小的时候,也能保证考虑到高低bit都参与到hash的计算中,同时不会有太大的开销。
- 如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
- 如果超过了负载因子(默认0.75),则会重新resize一个原来长度两倍的HashMap,并且重新调用hash方法。
- 我们可以使用自定义的对象作为键吗?
- 当然你可能使用任何对象作为键,只要它遵守了equals()和hashCode()方法的定义规则,并且当对象插入到Map中之后将不会再改变了。如果这个自定义对象时不可变的,那么它已经满足了作为键的条件,因为当它创建之后就已经不能改变了。
- 我们可以使用CocurrentHashMap来代替Hashtable吗?
- 因为ConcurrentHashMap越来越多人用了。我们知道Hashtable是synchronized的,但是ConcurrentHashMap同步性能更好,因为它仅仅根据同步级别对map的一部分进行上锁。ConcurrentHashMap当然可以代替HashTable,但是HashTable提供更强的线程安全性
- hashcode和equal这两者有什么区别?
- 在Java中任何一个对象都具备equals(Object obj)和hashcode()这两个方法,因为他们是在Object类中定义的。
- equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。
- hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。
- 1、如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。
- 2、如果两个对象不equals,他们的hashcode有可能相等。
- 3、如果两个对象hashcode相等,他们不一定equals。
- 4、如果两个对象hashcode不相等,他们一定不equals。
- 什么时候会使用HashMap?他有什么特点?
- HashMap如何put数据(从HashMap源码角度讲解)?
- HashMap怎么手写实现?
- TreeMap具体实现
- ArrayMap和HashMap的对比
- HashTable实现原理
- HashMap与HashSet的区别
- HashSet与HashMap怎么判断集合元素重复?
- 集合Set实现Hash怎么防止碰撞
- HashMap和HashTable的区别
- 1.HashMap支持null Key和null Value;Hashtable不允许。这是因为HashMap对null进行了特殊处理,将null的hashCode值定为了0,从而将其存放在哈希表的第0个bucket。
- 2.HashMap是非线程安全,HashMap实现线程安全方法为Map map = Collections.synchronziedMap(new HashMap());Hashtable是线程安全
- 3.HashMap默认长度是16,扩容是原先的2倍;Hashtable默认长度是11,扩容是原先的2n+1
- 4.HashMap继承AbstractMap;Hashtable继承了Dictionary
- LinkedHashMap
- HashMap + LinkedList,有序的map,LinkedList可以维护元素的先后顺序,可以实现LRU算法缓存
- ConcurrentHashMap 实现原理
- ConcurrentHashMap线程安全,引入“分段锁”概念,通过将整个map分为多个segment(类似HashTable)可以提供相同的线程安全,效率默认提供16倍
- HashTable, HashMap,TreeMap区别?
- 数组和链表的区别
- 二叉树的深度优先遍历和广度优先遍历的具体实现
- 堆的结构
- 堆和树的区别
- 堆和栈在内存中的区别是什么(解答提示:可以从数据结构方面以及实际实现方面两个方面去回答)?
- 什么是深拷贝和浅拷贝
- 手写链表逆序代码
- 讲一下对树,B+树的理解
- 讲一下对图的理解
- 判断单链表成环与否?
- 链表翻转(即:翻转一个单项链表)
- 合并多个单有序链表(假设都是递增的)
- Java虚拟机
- Java的引用类型 强引用 软引用 弱引用 虚引用
- 强引用: 强引用指的是通过 new 对象创建的引用,垃圾回收器即使是内存不足也不会回收强引用指向的对象。
- 软引用: 软引用是通过 SoftRefrence 实现的,它的生命周期比强引用短,在内存不足,抛出 OOM 之前,垃圾回收器会回收软引用引用的对象。软引用常见的使用场景是存储一些内存敏感的缓存,当内存不足时会被回收。
- 弱引用: 弱引用是通过 WeakRefrence 实现的,它的生命周期比软引用还短,GC 只要扫描到弱引用的对象就会回收。弱引用常见的使用场景也是存储一些内存敏感的缓存。
- 虚引用:虚引用是通过 FanttomRefrence 实现的,它的生命周期最短,随时可能被回收。如果一个对象只被虚引用引用,我们无法通过虚引用来访问这个对象的任何属性和方法。它的作用仅仅是保证对象在 finalize 后,做某些事情。虚引用常见的使用场景是跟踪对象被垃圾回收的活动,当一个虚引用关联的对象被垃圾回收器回收之前会收到一条系统通知。
- JVM内存结构
- 线程私有区域
- 程序计数器。 每个线程有有一个私有的程序计数器,任何时间一个线程都只会有一个方法正在执行,也就是所谓的当前方法。程序计数器存放的就是这个当前方法的JVM指令地址。
- JVM虚拟机栈。 创建线程的时候会创建线程内的虚拟机栈,栈中存放着一个个的栈帧,对应着一个个方法的调用。JVM 虚拟机栈有两种操作,分别是压栈和出站。栈帧中存放着局部变量表、方法返回值和方法的正常或异常退出的定义等等。
- 本地方法栈。 跟 JVM 虚拟机栈比较类似,只不过它支持的是 Native 方法。
- 线程共享区域
- 堆。 堆是内存管理的核心区域,用来存放对象实例。几乎所有创建的对象实例都会直接分配到堆上。所以堆也是垃圾回收的主要区域,垃圾收集器会对堆有着更细的划分,最常见的就是把堆划分为新生代和老年代。
- 方法区。方法区主要存放类的结构信息,比如静态属性和方法等等。
- 运行时常量池。运行时常量池位于方法区中,主要存放各种常量信息。
- 哪些区域会发生 OOM
- 堆。 通常发生的 OOM 都会发生在堆中,最常见的可能导致 OOM 的原因就是内存泄漏。
- JVM虚拟机栈和本地方法栈。 当我们写一个递归方法,这个递归方法没有循环终止条件,最终会导致 StackOverflow 的错误。当然,如果栈空间扩展失败,也是会发生 OOM 的
- 方法区。方法区现在基本上不太会发生 OOM,但在早期内存中加载的类信息过多的情况下也是会发生 OOM 的。
- 线程私有区域
- JVM内存模型
- java类加载过程和机制
- 双亲委派原理
- 类加载器大致分为3类:启动类加载器、扩展类加载器、应用程序类加载器。
- ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。
- SecureClassLoader继承了抽象类ClassLoader,但SecureClassLoader并不是ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性。
- URLClassLoader继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源。
- ExtClassLoader和AppClassLoader都继承自URLClassLoader,它们都是Launcher 的内部类,Launcher 是Java虚拟机的入口应用,ExtClassLoader和AppClassLoader都是在Launcher中进行初始化的。
- 双亲委派模型就是当加载一个类时,会优先使用父类加载器加载,当父类加载器无法加载时才会使用子类加载器去加载。这么做的目的是为了避免类的重复加载
- 类加载过程
- Java 中类加载分为 3 个步骤:加载、链接、初始化。
- 加载
- 加载是将字节码数据从不同的数据源读取到JVM内存,并映射为 JVM 认可的数据结构,也就是 Class 对象的过程。数据源可以是 Jar 文件、Class 文件等等。如果数据的格式并不是 ClassFile 的结构,则会报 ClassFormatError。
- 链接
- 验证。 验证是保证JVM安全的重要步骤。JVM需要校验字节信息是否符合规范,避免恶意信息和不规范数据危害JVM运行安全。如果验证出错,则会报VerifyError。
- 准备。 这一步会创建静态变量,并为静态变量开辟内存空间。
- 解析。 这一步会将符号引用替换为直接引用。
- 初始化
- 初始化会为静态变量赋值,并执行静态代码块中的逻辑。
- 双亲委派原理
- 垃圾回收机制
- 判断GC对象
- 引用计数法:当一个对象被引用时,它的引用计数器会加一,垃圾回收时会清理掉引用计数为0的对象。但这种方法有一个问题,比方说有两个对象 A 和 B,A 引用了 B,B 又引用了 A,除此之外没有别的对象引用 A 和 B,那么 A 和 B 在我们看来已经是垃圾对象,需要被回收,但它们的引用计数不为 0,没有达到回收的条件。正因为这个循环引用的问题,Java 并没有采用引用计数法。
- 可达性分析法: 我们把 Java 中对象引用的关系看做一张图,从根级对象不可达的对象会被垃圾收集器清除。根级对象一般包括 Java 虚拟机栈中的对象、本地方法栈中的对象、方法区中的静态对象和常量池中的常量。
- GC算法
- 标记清除算法: 顾名思义分为两步,标记和清除。首先标记到需要回收的垃圾对象,然后回收掉这些垃圾对象。标记清除算法的缺点是清除垃圾对象后会造成内存的碎片化。
- 复制算法: 复制算法是将存活的对象复制到另一块内存区域中,并做相应的内存整理工作。复制算法的优点是可以避免内存碎片化,缺点也显而易见,它需要两倍的内存。
- 标记整理算法: 标记整理算法也是分两步,先标记后整理。它会标记需要回收的垃圾对象,清除掉垃圾对象后会将存活的对象压缩,避免了内存的碎片化。
- 分代算法:分代算法将对象分为新生代和老年代对象。那么为什么做这样的区分呢?主要是在Java运行中会产生大量对象,这些对象的生命周期会有很大的不同,有的生命周期很长,有的甚至使用一次之后就不再使用。所以针对不同生命周期的对象采用不同的回收策略,这样可以提高GC的效率。
- 新生代对象分为三个区域:Eden 区和两个 Survivor 区。新创建的对象都放在 Eden区,当 Eden 区的内存达到阈值之后会触发 Minor GC,这时会将存活的对象复制到一个 Survivor 区中,这些存活对象的生命存活计数会加一。这时 Eden 区会闲置,当再一次达到阈值触发 Minor GC 时,会将Eden区和之前一个 Survivor 区中存活的对象复制到另一个 Survivor 区中,采用的是我之前提到的复制算法,同时它们的生命存活计数也会加一。
- GC原理时机以及GC对象
- Java的引用类型 强引用 软引用 弱引用 虚引用
- 线程、多线程和线程池
- 开启线程的三种方式?
- 线程和进程的区别?
- 为什么要有线程,而不是仅仅用进程?
- run()和start()方法区别
- 如何控制某个方法允许并发访问线程的个数?
- 在Java中wait和seelp方法的不同;
- 谈谈wait/notify关键字的理解
- 什么导致线程阻塞?
- 线程如何关闭?
- 讲一下java中的同步的方法
- 数据一致性如何保证?
- 如何保证线程安全?
- 如何实现线程同步?
- 两个进程同时要求写或者读,能不能实现?如何防止进程的同步?
- 线程间操作List
- Java中对象的生命周期
- ThreadLocal原理,实现及如何保证Local属性
- Volatile 原理
- 谈谈volatile关键字的用法
- 谈谈volatile关键字的作用
- synchronize 原理
- sychronized的应用
- 锁的类型
- 用于方法跟静态方法的区别
- 死锁的概念
- 谈谈对Synchronized关键字,类锁,方法锁,重入锁的理解
- static synchronized 方法的多线程访问和作用
- 同一个类里面两个synchronized方法,两个线程同时访问的问题
- ThreadLocal和Volatile的区别
- Volatile可以看做是一个轻量级的synchronize,它的开销比synchronize低,使用成本更小,但是不具备操作的原子性。ThreadLocal并不是用来解决在多线程并发环境下资源的共享问题的,而是用来提供线程内的局部变量。ThreadLocal变量属于线程内部管理
- synchronized与Lock的区别
- ReentrantLock 、synchronized和volatile比较
- ReentrantLock的内部实现
- lock原理
- 如何确定线程的执行顺序
- 死锁的四个必要条件?
- 怎么避免死锁?
- 对象锁和类锁是否会互相影响?
- 什么是线程池,如何使用?
- Java的并发、多线程、线程模型
- 谈谈对多线程的理解
- 多线程有什么要注意的问题?
- 谈谈你对并发编程的理解并举例说明
- 谈谈你对多线程同步机制的理解?
- 如何保证多线程读写文件的安全?
- 多线程断点续传原理
- 断点续传的实现
- 线程状态转换
- 锁的类型
- CAS机制
- AQS机制
- 手写生产者/消费者问题
- 基础
- Android
- Android基础知识点
- 四大组件是什么
- 四大组件的生命周期和简单用法
- Activity之间的通信方式
- Activity各种情况下的生命周期
- 横竖屏切换的时候,Activity 各种情况下的生命周期
- Activity与Fragment之间生命周期比较
- Activity上有Dialog的时候按Home键时的生命周期
- 两个Activity 之间跳转时必然会执行的是哪几个方法?
- 前台切换到后台,然后再回到前台,Activity生命周期回调方法。弹出Dialog,生命值周期回调方法。
- Activity的四种启动模式对比
- Activity状态保存于恢复
- Activity
- Activity的内部机制
- Activity生命周期
- Activity的启动模式
- Standard
- SingleTop
- SingleTask
- SingleInstance
- Intent的Action、Data、Category的用法和作用
- onNewIntent()的调用时机
- 前提:ActivityA已经启动过,处于当前应用的Activity堆栈中;
- 当ActivityA的LaunchMode为SingleTop时,如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent()方法
- 当ActivityA的LaunchMode为SingleInstance,SingleTask时,如果已经ActivityA已经在堆栈中,那么此时会调用onNewIntent()方法
- 当ActivityA的LaunchMode为Standard时,由于每次启动ActivityA都是启动新的实例,和原来启动的没关系,所以不会调用原来ActivityA的onNewIntent方法,仍然调用的是onCreate方法
- Service
- Service生命周期
- HandlerThread
- IntentService
- 如何保证Service不被杀死?如何保证进程不被杀死?
- Activity 怎么和Service 绑定?
- 怎么在Activity 中启动自己对应的Service?
- service和activity怎么进行数据交互?
- Service的开启方式
- BroadcastReceiver
- BroadcastReceiver,LocalBroadcastReceiver 区别
- 请描述一下广播BroadcastReceiver的理解
- 广播的分类
- 广播使用的方式和场景
- 在manifest 和代码中如何注册和使用BroadcastReceiver?
- 本地广播和全局广播有什么差别?
- BroadcastReceiver,LocalBroadcastReceiver 区别
- 谈谈你对ContentProvider的理解
- 说说ContentProvider、ContentResolver、ContentObserver 之间的关系
- SharedPreference原理,能否跨进程?如何实现?
- fragment各种情况下的生命周期
- Fragment状态保存startActivityForResult是哪个类的方法,在什么情况下使用?
- 如何实现Fragment的滑动?
- fragment之间传递数据的方式?
- 什么是fragment,有什么好处
- Fragment的生命周期
- 这张图是Fragment生命周期和Activity生命周期对比图,可以看到两者还是有很多相似的地方,比如都有onCreate(),onStart(),onPause(),onDestroy()等等,因为Fragment是被托管到Activity中的,所以多了两个onAttach()和onDetach()。这里讲讲与Activity生命周期不一样的方法。
- onAttach()Fragment和Activity建立关联的时候调用,被附加到Activity中去。
- onCreate()系统会在创建Fragment时调用此方法。可以初始化一段资源文件等等。
- onCreateView()系统会在Fragment首次绘制其用户界面时调用此方法。 要想为Fragment绘制 UI,从该方法中返回的 View 必须是Fragment布局的根视图。如果Fragment未提供 UI,您可以返回 null。
- onViewCreated()在Fragment被绘制后,调用此方法,可以初始化控件资源。
- onActivityCreated()当onCreate(),onCreateView(),onViewCreated()方法执行完后调用,也就是Activity被渲染绘制出来后。
- onPause()系统将此方法作为用户离开Fragment的第一个信号(但并不总是意味着此Fragment会被销毁)进行调用。 通常可以在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。
- onDestroyView():Fragment中的布局被移除时调用。
- onDetach():Fragment和Activity解除关联的时候调用。
- Fragment的生命周期
- AlertDialog,popupWindow,Activity区别
- Application 和 Activity 的 Context 对象的区别
- Android属性动画特性
- 如何导入外部数据库?
- LinearLayout、RelativeLayout、FrameLayout的特性及对比,并介绍使用场景。
- 谈谈对接口与回调的理解
- 回调的原理
- 写一个回调demo
- 介绍下SurfView
- RecycleView的使用
- 序列化的作用,以及Android两种序列化的区别
- 差值器
- 估值器
- Android中数据存储方式
- surfaceView与TextureView
- Android源码相关分析
- Android动画框架实现原理
- Android各个版本API的区别
- Requestlayout,onlayout,onDraw,DrawChild区别与联系
- invalidate和postInvalidate的区别及使用
- Activity-Window-View三者的差别
- 谈谈对Volley的理解
- 如何优化自定义View
- 低版本SDK如何实现高版本api?
- 描述一次网络请求的流程
- HttpUrlConnection 和 okhttp关系
- Bitmap对象的理解
- looper架构
- ActivityThread,AMS,WMS的工作原理
- 自定义View如何考虑机型适配
- 自定义View的事件
- AstncTask+HttpClient 与 AsyncHttpClient有什么区别?
- LaunchMode应用场景
- AsyncTask 如何使用?
- 请介绍下ContentProvider 是如何实现数据共享的?
- AndroidService与Activity之间通信的几种方式
- IntentService原理及作用是什么?
- 说说Activity、Intent、Service 是什么关系
- ApplicationContext和ActivityContext的区别
- SP是进程同步的吗?有什么方法做到同步?
- 谈谈多线程在Android中的使用
- 进程和 Application 的生命周期
- 封装View的时候怎么知道view的大小
- RecycleView原理
- AndroidManifest的作用与理解
- SpareArray原理
- SparseArray,通常来讲是 Android 中用来替代 HashMap 的一个数据结构。 准确来讲,是用来替换key为 Integer 类型,value为Object 类型的HashMap。需要注意的是 SparseArray 仅仅实现了 Cloneable 接口,所以不能用Map来声明。 从内部结构来讲,SparseArray 内部由两个数组组成,一个是 int[]类型的 mKeys,用来存放所有的键;另一个是 Object[]类型的 mValues,用来存放所有的值。 最常见的是拿 SparseArray 跟HashMap 来做对比,由于 SparseArray 内部组成是两个数组,所以占用内存比 HashMap 要小。我们都知道,增删改查等操作都首先需要找到相应的键值对,而 SparseArray 内部是通过二分查找来寻址的,效率很明显要低于 HashMap 的常数级别的时间复杂度。提到二分查找,这里还需要提一下的是二分查找的前提是数组已经是排好序的,没错,SparseArray 中就是按照key进行升序排列的。 综合起来来说,SparseArray 所占空间优于 HashMap,而效率低于 HashMap,是典型的时间换空间,适合较小容量的存储。 从源码角度来说,我认为需要注意的是 SparseArray的remove()、put()和 gc()方法。
- remove()。SparseArray 的 remove()方法并不是直接删除之后再压缩数组,而是将要删除的 value 设置为 DELETE 这个 SparseArray 的静态属性,这个 DELETE 其实就是一个 Object 对象,同时会将 SparseArray 中的 mGarbage 这个属性设置为 true,这个属性是便于在合适的时候调用自身的 gc()方法压缩数组来避免浪费空间。这样可以提高效率,如果将来要添加的key等于删除的key,那么会将要添加的 value 覆盖 DELETE。
- gc()。SparseArray 中的 gc()方法跟 JVM 的 GC 其实完全没有任何关系。``gc()` 方法的内部实际上就是一个for循环,将 value 不为 DELETE 的键值对往前移动覆盖value 为DELETE的键值对来实现数组的压缩,同时将 mGarbage 置为 false,避免内存的浪费。
- put()。put 方法是这么一个逻辑,如果通过二分查找 在 mKeys 数组中找到了 key,那么直接覆盖 value 即可。如果没有找到,会拿到与数组中与要添加的 key 最接近的 key 索引,如果这个索引对应的 value 为 DELETE,则直接把新的 value 覆盖 DELET 即可,在这里可以避免数组元素的移动,从而提高了效率。如果 value 不为 DELETE,会判断 mGarbage,如果为 true,则会调用 gc()方法压缩数组,之后会找到合适的索引,将索引之后的键值对后移,插入新的键值对,这个过程中可能会触发数组的扩容。
- RecyclerView相比ListView有哪些优势
- RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由
- 其次RecyclerView提供了添加、删除item的动画 效果,而且可以自定义
- RecyclerView相比ListView优势在于可以轻松实现:
- ListView的功能
- GridView的功能
- 横向ListView的功能
- 横向ScrollView的功能
- 瀑布流效果
- 便于添加Item增加和移除动画
- 不过一个挺郁闷的地方就是,系统没有提供ClickListener和LongClickListener。
- 不过我们也可以自己去添加,只是会多了些代码而已。
- 实现的方式比较多,你可以通过mRecyclerView.addOnItemTouchListener去监听然后去判断手势,
- 当然你也可以通过adapter中自己去提供回调
- RecyclerView的缓存复用机制
- View绘制流程
- ViewRootIml
- onMeasure()
- 影响子view绘制的因素
- MeasureSpec是由父View的MeasureSpec和子View的LayoutParams
- 父View 3种情况对应于MeasureSpec一共有三种模式
- UPSPECIFIED : 父容器对于子容器没有任何限制,子容器想要多大就多大
- EXACTLY: 父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间。
- AT_MOST:子容器可以是声明大小内的任意大小
- 子View 3种情况对应于子View 的layout_xxxx
- MATCH_PARENT
- WRAP_CONTENT
- 确定的值(200dp)
- onLayout()
- onDraw()
- invalidate方法,有带4个参数的,和不带参数有什么区别
- invalidate、postInvalidate和requestLayout的区别
- requestLayout触发measure和layout,如何实现局部重新测量,避免全局重新测量问题
- 事件分发机制
- dispatchTouchEvent()
- onInterceptTouchEvent()
- onTouchEvent
- onTouchEvent()、onTouchListener、onClickListener的先后顺序
- 消息分发机制
- 首先在主线程创建一个Handler对象 ,并重写handleMessage()方法。然后当在子线程中需要进行更新UI的操作,我们就创建一个Message对象,并通过handler发送这条消息出去。之后这条消息被加入到MessageQueue队列中等待被处理,通过Looper对象会一直尝试从Message Queue中取出待处理的消息,最后分发会Handler的handler Message()方法中。
- Handler、Looper、Message、MessageQueue
- Message
- target 其实就是发送消息的 Handler 对象
- callback 是当调用 handler.post(runnable)时传入的 Runnable 类型的任务。post 事件的本质也是创建了一个 Message,将我们传入的这个 runnable 赋值给创建的Message的 callback 这个成员变量。
- MessageQueue
- 消息队列很明显是存放消息的队列,值得关注的是 MessageQueue 中的 next() 方法,它会返回下一个待处理的消息。
- Looper
- Looper 消息轮询器其实是连接 Handler 和消息队列的核心。首先我们都知道,如果想要在一个线程中创建一个 Handler,首先要通过 Looper.prepare()创建 Looper,之后还得调用 Looper.loop()开启轮询。我们着重看一下这两个方法。
- prepare()。这个方法做了两件事:首先通过ThreadLocal.get()获取当前线程中的Looper,如果不为空,则会抛出一个RunTimeException,意思是一个线程不能创建2个Looper。如果为null则执行下一步。第二步是创建了一个Looper,并通过 ThreadLocal.set(looper)。将我们创建的Looper与当前线程绑定。这里需要提一下的是消息队列的创建其实就发生在Looper的构造方法中。
- loop()。这个方法开启了整个事件机制的轮询。它的本质是开启了一个死循环,不断的通过 MessageQueue的next()方法获取消息。拿到消息后会调用 msg.target.dispatchMessage()来做处理。其实我们在说到 Message 的时候提到过,msg.target其实就是发送这个消息的 handler。这句代码的本质就是调用 handler的dispatchMessage()。
- Handler
- Handler 的分析着重在两个部分:发送消息和处理消息。
- 发送消息。其实发送消息除了 sendMessage 之外还有 sendMessageDelayed 和 post 以及 postDelayed 等等不同的方式。但它们的本质都是调用了 sendMessageAtTime。在 sendMessageAtTime 这个方法中调用了 enqueueMessage。在 enqueueMessage 这个方法中做了两件事:通过 msg.target = this实现了消息与当前 handler 的绑定。然后通过 queue.enqueueMessage实现了消息入队。
- 处理消息。消息处理的核心其实就是dispatchMessage()这个方法。这个方法里面的逻辑很简单,先判断 msg.callback是否为 null,如果不为空则执行这个 runnable。如果为空则会执行我们的handleMessage方法。
- Message
- 为什么一个线程只有一个Looper、只有一个MessageQueue?
- 如何获取当前线程的Looper?是怎么实现的?(理解ThreadLocal)
- 是不是任何线程都可以实例化Handler?有没有什么约束条件?
- Looper.loop是一个死循环,拿不到需要处理的Message就会阻塞,那在UI线程中为什么不会导致ANR?
- Handler.sendMessageDelayed()怎么实现延迟的?结合Looper.loop()循环中,Message=messageQueue.next()和MessageQueue.enqueueMessage()分析。
- Binder机制,进程通信
- Binder是Android系统中的一种IPC进程间通信结构
- Binder的整个设计是C/S结构,客户端进程通过获取服务端进程的代理,并通过向这个代理接口方法中读写数据来完成进程间的数据通信。
- 安全,每个进程都会被Android系统分配UID和PID,不像传统的在数据里加入UID,这就让那些恶意进程无法直接和其他进程通信,进程间通信的安全性得到提升。
- 高效,像Socket之类的IPC每次数据拷贝都需要2次,而Binder只要1次,在手机这种资源紧张的情况下很重要。
- 常见的一些原理性问题
- Handler机制和底层实现
- Handler、Thread和HandlerThread的差别
- handler发消息给子线程,looper怎么启动?
- 关于Handler,在任何地方new Handler 都是什么线程下?
- ThreadLocal原理,实现及如何保证Local属性?
- 请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系
- 请描述一下View事件传递分发机制
- Touch事件传递流程
- 事件分发中的onTouch 和onTouchEvent 有什么区别,又该如何使用?
- View和ViewGroup分别有哪些事件分发相关的回调方法
- View刷新机制
- View绘制流程
- 自定义控件原理
- 自定义View如何提供获取View属性的接口?
- Android代码中实现WAP方式联网
- AsyncTask机制
- AsyncTask原理及不足
- 如何取消AsyncTask?
- 为什么不能在子线程更新UI?
- ANR产生的原因是什么?
- ANR定位和修正
- oom是什么?
- 什么情况导致oom?
- 有什么解决方法可以避免OOM?
- Oom 是否可以try catch?为什么?
- 内存泄漏是什么?
- 什么情况导致内存泄漏?
- 如何防止线程的内存泄漏?
- 内存泄露场的解决方法
- 内存泄漏和内存溢出区别?
- LruCache默认缓存大小
- ContentProvider的权限管理(解答:读写分离,权限控制-精确到表级,URL控制)
- 如何通过广播拦截和abort一条短信?
- 广播是否可以请求网络?
- 广播引起anr的时间限制是多少?
- 计算一个view的嵌套层级
- Activity栈
- Android线程有没有上限?
- 线程池有没有上限?
- ListView重用的是什么?
- Android为什么引入Parcelable?
- 有没有尝试简化Parcelable的使用?
- 开发中常见的一些问题
- ListView 中图片错位的问题是如何产生的?
- 混合开发有了解吗?
- 知道哪些混合开发的方式?说出它们的优缺点和各自使用场景?(解答:比如:RN,weex,H5,小程序,WPA等。做Android的了解一些前端js等还是很有好处的);
- 屏幕适配的处理技巧都有哪些?
- 服务器只提供数据接收接口,在多线程或多进程条件下,如何保证数据的有序到达?
- 动态布局的理解
- 怎么去除重复代码?
- 画出 Android 的大体架构图
- Recycleview和ListView的区别
- ListView图片加载错乱的原理和解决方案
- 动态权限适配方案,权限组的概念
- Android系统为什么会设计ContentProvider?
- 下拉状态栏是不是影响activity的生命周期
- 如果在onStop的时候做了网络请求,onResume的时候怎么恢复?
- Bitmap 使用时候注意什么?
- Bitmap的recycler()
- Android中开启摄像头的主要步骤
- ViewPager使用细节,如何设置成每次只初始化当前的Fragment,其他的不初始化?
- 点击事件被拦截,但是想传到下面的View,如何操作?
- 微信主页面的实现方式
- 微信上消息小红点的原理
- CAS介绍
- 混合开发面试题
- Hybrid做过吗?
- Hybrid通信原理是什么,有做研究吗?
- react native有多少了解?讲一下原理。
- weex了解吗?如何自己实现类似技术?
- flutter了解吗?内部是如何实现跨平台的?
- Dart语言有研究贵吗?
- 快应用了解吗?跟其她方式相比有什么优缺点?
- 说说你用过的混合开发技术有哪些?各有什么优缺点?
- Python会吗?
- 会不会PHP?
- Gradle了解多少?groovy语法会吗?
- Android系统原理问题
- Activity的启动流程
- Service的启动流程
- ActivityThread的理解
- Activity、Window、View三者的关系?
- 观察Activity源码:Activity.attch() -> PolicyManager -> Policy -> PhoneWindow -> mLayoutInflater.inflate()&mContentParent.addView()这只是一个简单的跟踪过程描述。通过跟踪源代码,就可以很清晰的看出他们三者的关系。
- Activity是整个模型的控制单元,Window属于承载模型,负责承载视图,View是视图显示模型。
- View是Android中的视图呈现方式,但是View不能单独存在,它必须附着在Window这个抽象的概念上面,因此有视图的地方就有Window。那些地方有视图呢?Android中可以提供视图的地方有Activity,DIalog,Toast,除此之外,还有一些依托Window而实现的视图,比如PopupWindow,Meun,他们也是视图,有视图的地方就有Window,因此Activity,Dialog,Toast等视图都对应着一个Window。
- 那View是怎样绑定在Window上的呢?还要介绍下Window和View之间的纽带:ViewRoot。ViewRoot对应于ViewRootImpl类,它是连接WindowManager和Decorview的纽带,View的三大流程(measure,layout,draw)均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorVidew添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。
- 一个比喻总结下Activity Window View三只之间的关系:Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)。
- 1)一个Activity构造的时候会初始化一个Window,准确的说是PhoneWindow。
- 2)这个PhoneWindow有一个“ViewRoot”,引号是说其实这个“ViewRoot”是一个View或者说ViewGroup,是最初始的根视图。
- 3)“ViewRoot”通过addView方法来一个个的添加View。比如TextView,Button等
- 4)这些View的事件监听,是由WindowManagerService来接受消息,并且回调Activity函数。比如onClickListener,onKeyDown等。
- Window加载视图过程
- Android系统启动流程
- ActivityManagerService
- AMS是系统的引导服务,应用进程的启动、切换和调度、四大组件的启动和管理都需要AMS的支持。
- WindowManagerService
- ActivityManagerService
- PackageManagerService
- App 是如何沙箱化,为什么要这么做
- Dalvik和JVM有什么区别
- 1)Dalvik虚拟机支持的是.dex文件,而JVM支持的是.class文件
- (2)Dalvik是基于寄存器的,而JVM是基于栈的。
- (3)相比之下,Dalvik虚拟机占用更少的空间;
- (4)Dalvik常量池只采用32位索引;
- (5)标准Java字节码实行8位堆栈指令,Dalvik使用16位指令集直接作用于局部变量。局部变量通常来自4位的“虚拟寄存器”区。这样减少了Dalvik的指令计数,提高了翻译速度。
- Dalvik和art虚拟机有什么区别
- 在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。
- 优点:
- 1、系统性能的显著提升。
- 2、应用启动更快、运行更快、体验更流畅、触感反馈更及时。
- 3、更长的电池续航能力。
- 4、支持更低的硬件。
- 缺点:
- 1.机器码占用的存储空间更大,字节码变为机器码之后,可能会增加10%-20%(不过在应用包中,可执行的代码常常只是一部分。比如最新的 Google+ APK 是 28.3 MB,但是代码只有 6.9 MB。)
- 2.应用的安装时间会变长。
- 线程间通信和进程间通信有什么不同,Android开发过程中是怎么实现的
- 进程和 Application 的生命周期
- 进程防杀保活方案
- Android中的类加载器
- PathClassLoader,只能加载系统中已经安装过的 apk
- DexClassLoader,可以加载 jar/apk/dex,可以从 SD卡中加载未安装的 apk
- DexClassLoader可以加载 dex 文件以及包含 dex 的 apk 文件或 jar 文件,也支持从 SD 卡进行加载,这也就意味着 DexClassLoader可以在应用未安装的情况下加载 dex 相关文件。因此,它是热修复和插件化技术的基础。
- Android中的动画有哪几类,它们的特点和区别是什么
- Android中动画大致分为3类:帧动画、补间动画(View Animation)、属性动画(Object Animation)。
- 帧动画:通过xml配置一组图片,动态播放。很少会使用。
- 补间动画(View Animation):大致分为旋转、透明、缩放、位移四类操作。很少会使用。
- 属性动画(Object Animation):属性动画是现在使用的最多的一种动画,它比补间动画更加强大。属性动画大致分为两种使用类型,分别是 ViewPropertyAnimator 和 ObjectAnimator。前者适合一些通用的动画,比如旋转、位移、缩放和透明,使用方式也很简单通过 View.animate()即可得到 ViewPropertyAnimator,之后进行相应的动画操作即可。后者适合用于为我们的自定义控件添加动画,当然首先我们应该在自定义 View 中添加相应的 getXXX()和 setXXX()相应属性的 getter 和 setter 方法,这里需要注意的是在 setter 方法内改变了自定义 View 中的属性后要调用 invalidate()来刷新View的绘制。之后调用 ObjectAnimator.of属性类型()返回一个 ObjectAnimator,调用 start()方法启动动画即可。
- 补间动画与属性动画的区别:
- 补间动画是父容器不断的绘制 view,看起来像移动了效果,其实 view 没有变化,还在原地。
- 是通过不断改变 view 内部的属性值,真正的改变 view。
- 动态权限适配问题 底层的权限是如何进行管理 的
- gson、json json的三种加载方式
- Android屏幕适配解决方案
- 相关概念
- 屏幕尺寸
- 含义:手机对角线的物理尺寸
- 单位:英寸(inch),1英寸=2.54cm
- 屏幕分辨率
- 手机在横向、纵向上的像素点数总和
- 单位:px(pixel),1px=1像素点
- Android手机常见的分辨率:1080x1920
- 屏幕像素密度
- 含义:每英寸的像素点数
- 单位:dpi(dots per ich)
- 安卓手机对于每类手机屏幕大小都有一个相应的屏幕像素密度:
- 密度类型代表的分辨率(px)屏幕像素密度(dpi)
- 低密度(ldpi) 240x320 120
- 中密度(mdpi) 320x480 160
- 高密度(hdpi) 480x800 240
- 超高密度(xhdpi) 720x1280 320
- 超超高密度(xxhdpi) 1080x1920 480
- 屏幕尺寸、分辨率、像素密度三者关系
- 密度无关像素
- 含义:density-independent pixel,叫dp或dip,与终端上的实际物理像素点无关。
- 单位:dp,可以保证在不同屏幕像素密度的设备上显示相同的效果
- Android开发时用dp而不是px单位设置图片大小,是Android特有的单位
- 在Android中,规定以160dpi(即屏幕分辨率为320x480)为基准:1dp=1px
- 独立比例像素
- 含义:scale-independent pixel,叫sp或sip
- 单位:sp
- Android开发时用此单位设置文字大小,可根据字体大小首选项进行缩放
- 屏幕尺寸
- 屏幕适配的原因和本质
- Android系统碎片化:小米定制的MIUI、魅族定制的flyme、华为定制的EMUI等等
- Android机型屏幕尺寸碎片化:5寸、5.5寸、6寸等等
- Android屏幕分辨率碎片化:320x480、480x800、720x1280、1080x1920
- 为了保证用户获得一致的用户体验效果:使得某一元素在Android不同尺寸、不同分辨率的手机上具备相同的显示效果
- 屏幕适配问题的本质
- 使得“布局”、“布局组件”、“图片资源”、“用户界面流程”匹配不同的屏幕尺寸
- 使得布局、布局组件自适应屏幕尺寸;
- 根据屏幕的配置来加载相应的UI布局、用户界面流程
- 使得“图片资源”匹配不同的屏幕密度
- 使得“布局”、“布局组件”、“图片资源”、“用户界面流程”匹配不同的屏幕尺寸
- 解决方案
- 相关概念
- 换肤实现原理
- Notification 相关
- Android 5.0 (API level 21) 开始通知可以出现在锁屏页面
- Android 7.0 (API level 24) 开始可以在通知中直接输入文本或执行一些自定义操作,如直接回复按钮
- Android 8.0 (API level 26) 开始所有的通知必须属于一个 channel,channel 被用户看作是 categories,即通知类别,用户通过通知类别来精确管理各个应用或一个应用内的通知。一个应用可以有多个通知类别,如私信类别、好友请求类别、应用更新类别等等。可以给每个通知类别指定通知的 importance,即重要程度,Urgent(紧急)会发出提示音并在屏幕上弹出通知,High(高)会发出提示音,Medium(中)不发出提示音,Low(低)不发出提示音并且不会出现在状态栏中。在 Android 8.0 (API level 26) 以下的系统中通知的重要程度表现为 priority,即优先级。对应关系分别为: IMPORTANCE_HIGH对应 PRIORITY_HIGH或 PRIORITY_MAX,IMPORTANCE_DEFAULT对应 PRIORITY_DEFAULT,IMPORTANCE_LOW对应 PRIORITY_LOW,IMPORTANCE_MIN对应 PRIORITY_MIN。在应用启动时可以执行下面的代码创建通知类别,可以无副作用地多次执行
- AsyncTask源码分析及其优劣性
- Android Toast原理分析
- Android与 js 是如何交互的
- 在 Android 中,Android 与js 的交互分为两个方面:Android 调用 js 里的方法、js 调用 Android 中的方法。
- Android调js
- WebView.loadUrl("javascript:js中的方法名")。这种方法的优点是很简洁,缺点是没有返回值,如果需要拿到js方法的返回值则需要js调用Android中的方法来拿到这个返回值。
- WebView.evaluateJavaScript("javascript:js中的方法名",ValueCallback)。这种方法比 loadUrl 好的是可以通过 ValueCallback 这个回调拿到 js方法的返回值。缺点是这个方法 Android4.4 才有,兼容性较差。不过放在 2018 年来说,市面上绝大多数 App 都要求最低版本是 4.4 了,所以我认为这个兼容性问题不大。
- js 调 Android
- WebView.addJavascriptInterface()。这是官方解决 js 调用 Android 方法的方案,需要注意的是要在供 js 调用的 Android 方法上加上 @JavascriptInterface注解,以避免安全漏洞。这种方案的缺点是 Android4.2 以前会有安全漏洞,不过在 4.2 以后已经修复了。同样,在 2018 年来说,兼容性问题不大。
- 重写 WebViewClient的shouldOverrideUrlLoading()方法来拦截url,拿到 url 后进行解析,如果符合双方的规定,即可调用 Android 方法。优点是避免了 Android4.2 以前的安全漏洞,缺点也很明显,无法直接拿到调用 Android 方法的返回值,只能通过 Android 调用 js 方法来获取返回值。
- 重写 WebChromClient 的 onJsPrompt()方法,同前一个方式一样,拿到 url 之后先进行解析,如果符合双方规定,即可调用Android方法。最后如果需要返回值,通过 result.confirm("Android方法返回值")即可将 Android 的返回值返回给 js。方法的优点是没有漏洞,也没有兼容性限制,同时还可以方便的获取 Android 方法的返回值。其实这里需要注意的是在 WebChromeClient 中除 了 onJsPrompt 之外还有 onJsAlert 和 onJsConfirm 方法。那么为什么不选择另两个方法呢?原因在于 onJsAlert 是没有返回值的,而 onJsConfirm 只有 true 和 false 两个返回值,同时在前端开发中 prompt 方法基本不会被调用,所以才会采用 onJsPrompt。
- ANR出现的场景及解决方案
- 在Android中,应用的响应性被活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务所监视。当用户触发了输入事件(如键盘输入,点击按钮等),如果应用5秒内没有响应用户的输入事件,那么,Android会认为该应用无响应,便弹出ANR对话框。而弹出ANR异常,也主要是为了提升用户体验。
- 解决方案是对于耗时的操作,比如访问网络、访问数据库等操作,需要开辟子线程,在子线程处理耗时的操作,主线程主要实现UI的操作
- 实现原理代码分析
- 组件化
- 为什么要组件化
- APP迭代维护成本增高
- 投资界,新芽,项目工厂等APP自身在飞速发展,版本不断迭代,新功能不断增加,业务模块数量不断增加,业务上的处理逻辑越变越复杂,同时每个模块代码也变得越来越多,这就引发一个问题,所维护的代码成本越来越高,稍微一改动可能就牵一发而动全身,改个小的功能点就需要回归整个APP测试,这就对开发和维护带来很大的挑战。
- 多人组合需要组件化
- APP 架构方式是单一工程模式,业务规模扩大,随之带来的是团队规模扩大,那就涉及到多人协作问题,每个移动端软件开发人员势必要熟悉如此之多代码,如果不按照一定的模块组件机制去划分,将很难进行多人协作开发,随着单一项目变大,而且Andorid项目在编译代码方面就会变得非常卡顿,在单一工程代码耦合严重,每修改一处代码后都需要重新编译打包测试,导致非常耗时。
- APP迭代维护成本增高
- 什么是组件化
- 什么是组件化呢?
- 组件(Component)是对数据和方法的简单封装,功能单一,高内聚,并且是业务能划分的最小粒度。
- 组件化是基于组件可重用的目的上,将一个大的软件系统按照分离关注点的形式,拆分成多个独立的组件,使得整个软件系统也做到电路板一样,是单个或多个组件元件组装起来,哪个组件坏了,整个系统可继续运行,而不出现崩溃或不正常现象,做到更少的耦合和更高的内聚。
- 区分模块化与组件化
- 模块化
- 模块化就是将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块只包含与其功能相关的内容,模块我们相对熟悉,比如登录功能可以是一个模块,搜索功能可以是一个模块等等。
- 组件化
- 组件化就是更关注可复用性,更注重关注点分离,如果从集合角度来看的话,可以说往往一个模块包含了一个或多个组件,或者说模块是一个容器,由组件组装而成。简单来说,组件化相比模块化粒度更小,两者的本质思想都是一致的,都是把大往小的方向拆分,都是为了复用和解耦,只不过模块化更加侧重于业务功能的划分,偏向于复用,组件化更加侧重于单一功能的内聚,偏向于解耦。
- 模块化
- 组件化需要考虑问题
- 代码解耦。
- 如何将一个庞大的工程分成有机的整体?这个需要一步步来了!
- 对已存在的项目进行模块拆分,模块分为两种类型,一种是功能组件模块,封装一些公共的方法服务等,作为依赖库对外提供;另一种是业务组件模块,专门处理业务逻辑等功能,这些业务组件模块最终负责组装APP。
- 组件单独运行。
- 因为每个组件都是高度内聚的,是一个完整的整体,如何让其单独运行和调试?
- 通过 Gradle脚本配置方式,进行不同环境切换,我自己操作是添加一个boolean值的开关。比如只需要把 Apply plugin: 'com.android.library' 切换成Apply plugin: 'com.android.application' 就可以独立运行呢!
- 需要注意:当切换到application独立运行时,需要在AndroidManifest清单文件上进行设置,因为一个单独调试需要有一个入口的Activity。
- 组件间通信。
- 由于每个组件具体实现细节都互相不了解,但每个组件都需要给其他调用方提供服务,那么主项目与组件、组件与组件之间如何通信就变成关键?
- 这个我是直接用阿里开源的路由框架,当然你可以根据需要选择其他大厂的开源路由库。引用阿里的ARouter框架,通过注解方式进行页面跳转。
- 组件生命周期。
- 这里的生命周期指的是组件在应用中存在的时间,组件是否可以做到按需、动态使用、因此就会涉及到组件加载、卸载等管理问题。
- 集成调试。
- 在开发阶段如何做到按需编译组件?一次调试中可能有一两个组件参与集成,这样编译时间就会大大降低,提高开发效率。
- 代码隔离。
- 组件之间的交互如果还是直接引用的话,那么组件之间根本没有做到解耦,如何从根本上避免组件之间的直接引用?目前做法是主项目和业务组件都会依赖公共基础组件库,业务组件通过路由服务依赖库按需进行查找,用于不同组件之间的通信。
- 告别结构臃肿,让各个业务变得相对独立,业务组件在组件模式下可以独立开发,而在集成模式下又可以变为AAR包集成到“APP壳工程”中,组成一个完整功能的 APP。
- 代码解耦。
- 为什么要组件化
- 插件化
- 百度百科里是这么定义插件的:「 是一种遵循一定规范的应用程序接口编写出来的程序,只能运行在程序规定的系统平台下,而不能脱离指定的平台单独运行。」,也就是说,插件可以提供一种动态扩展能力,使得应用程序在运行时加载原本不属于该应用的功能,并且做到动态更新和替换。
- 宿主,就是需要能提供运行环境,给资源调用提供上下文环境,一般也就是我们主 APK ,要运行的应用,它作为应用的主工程所在,实现了一套插件的加载和管理的框架,插件都是依托于宿主的APK而存在的。
- 插件可以想象成每个独立的功能模块封装为一个小的 APK ,可以通过在线配置和更新实现插件 APK 在宿主 APK 中的上线和下线,以及动态更新等功能。
- 优势
- 让用户不用重新安装 APK 就能升级应用功能,减少发版本频率,增加用户体验。
- 提供一种快速修复线上 BUG 和更新的能力。
- 按需加载不同的模块,实现灵活的功能配置,减少服务器对旧版本接口兼容压力。
- 模块化、解耦合、并行开发、 65535 问题。
- 插件化需要的知识
- Binder机制
- App打包流程
- App安装流程
- App启动流程
- 资源加载机制
- Gradle
- 实现原理
- 在Android中应用插件化技术,其实也就是动态加载的过程,分为以下几步
- 把可执行文件( .so/dex/jar/apk 等)拷贝到应用 APP 内部
- 加载可执行文件,更换静态资源
- 调用具体的方法执行业务逻辑
- 动态加载技术按照加载的可执行文件的不同大致可以分为两种:
- 动态加载 .so 库
- Android 中 NDK 中其实就使用了动态加载,动态加载 .so 库并通过 JNI 调用其封装好的方法。后者一 般是由 C/C++ 编译而成,运行在 Native 层,效率会比执行在虚拟机层的 Java 代码高很多,所以 Android 中经常通过动态加载 .so 库来完成一些对性能比较有需求的工作(比如 Bitmap 的解码、图片高斯模糊处理等)。此外,由于 .so 库是由 C/C++ 编译而来的,只能被反编译成汇编代码,相比中 dex 文件反编译得到的 Smali 代码更难被破解,因此 .so 库也可以被用于安全领域。
- 动态加载 dex/jar/apk文件(现在动态加载普遍说的是这种)
- “基于 ClassLoader 的动态加载 dex/jar/apk 文件”,就是我们指在 Android 中 动态加载由 Java 代码编译而来的 dex 包并执行其中的代码逻辑,这是常规 Android 开发比较少用到的一种技术,目前说的动态加载指的就是这种。
- Android 项目中,所有 Java 代码都会被编译成 dex 文件,Android 应用运行时,就是通过执行 dex 文件里的业务代码逻辑来工作的。使用动态加载技术可以在 Android 应用运行时加载外部的 dex 文件,而通过网络下载新的 dex 文件并替换原有的 dex 文件就可以达到不安装新 APK 文件就升级应用(改变代码逻辑)的目的。
- 所以说,在 Android 中的 ClassLoader 机制主要用来加载 dex 文件,系统提供了两个 API 可供选择:
- PathClassLoader:只能加载已经安装到 Android 系统中的 APK 文件。因此不符合插件化的需求,不作考虑。
- DexClassLoader:支持加载外部的 APK、Jar 或者 dex 文件,正好符合文件化的需求,所有的插件化方案都是使用 DexClassloader 来加载插件 APK 中的 .class文件的。
- 动态加载 .so 库
- 主流框架
- Android 中实现插件化框架,需要解决的问题主要如下:
- 资源和代码的加载
- Android 生命周期的管理和组件的注册
- 宿主 APK 和插件 APK 资源引用的冲突解决
- DL 动态加载框架
- 是基于代理的方式实现插件框架,对 App 的表层做了处理,通过在 Manifest 中注册代理组件,当启动插件组件时,首先启动一个代理组件,然后通过这个代理组件来构建,启动插件组件。 需要按照一定的规则来开发插件APK,插件中的组件需要实现经过改造后的 Activity、FragmentActivity、Service 等的子类。
- 优点如下:
- 插件需要遵循一定的规则,因此安全方面可控制。
- 方案简单,适用于自身少量代码的插件化改造。
- 缺点如下:
- 不支持通过 This 调用组件的方法,需要通过 that 去调用。
- 由于 APK 中的 Activity 没有注册,不支持隐式调用 APK 内部的 Activity。
- 插件编写和改造过程中,需要考虑兼容性问题比较多,联调起来会比较费时费力。
- DroidPlugin
- DroidPlugin 是 360 手机助手实现的一种插件化框架,它可以直接运行第三方的独立 APK 文件,完全不需要对 APK 进行修改或安装。一种新的插件机制,一种免安装的运行机制,是一个沙箱(但是不完全的沙箱。就是对于使用者来说,并不知道他会把 apk 怎么样), 是模块化的基础。
- 实现原理
- 共享进程:为android提供一个进程运行多个 apk 的机制,通过 API 欺骗机制瞒过系统。
- 占坑:通过预先占坑的方式实现不用在 manifest 注册,通过一带多的方式实现服务管理。
- Hook 机制:动态代理实现函数 hook ,Binder 代理绕过部分系统服务限制,IO 重定向(先获取原始 Obje ct –> Read ,然后动态代理 Hook Object 后–> Write 回去,达到瞒天过海的目的)。
- 优点如下:
- 支持 Android 四大组件,而且插件中的组件不需要在宿主 APK 中注册。
- 支持 Android 2.3 及以上系统,支持所有的系统 API。
- 插件与插件之间,插件与宿主之间的代码和资源完全隔阂。
- 实现了进程管理,插件的空进程会被及时回收,占用内存低。
- 缺点如下:
- 插件 APK 中不支持自定义资源的 Notification,通知栏限制。
- 插件 APK 中无法注册具有特殊的 IntentFilter 的四大组件。
- 缺乏对 Native 层的 Hook 操作,对于某些带有 Native 代码的插件 APK 支持不友好,可能无法正常运行。
- 由于插件与插件,插件与宿主之间的代码完全隔离,因此,插件与插件,插件与宿主之间的通信只能通过 And roid 系统级别的通信方式。
- 安全性担忧(可以修改,hook一些重要信息)。
- 机型适配(不是所有机器上都能行,因为大量用反射相关,如果rom厂商深度定制了framework层,反射的方法或者类不在,容易插件运用失败)
- Small
- Small 是一种实现轻巧的跨平台插件化框架,基于“轻量、透明、极小化、跨平台”的理念
- 实现原理:
- 动态加载类:我们知道插件化很多都从 DexClassLoader 类有个 DexPathList 清单,支持 dex/jar/zip/apk 文件格式,却没有支持 .so 文件格式,因此 Small 框架则是把 .so 文件包装成 zip 文件格式,插入到 Dex PathList 集合中,改写动态加载的代码。
- 资源分段:由于 Android 资源的格式是 0xPPTTNNNN ,PP 是包 ID ,00-02 是属于系统,7f 属于应用程序,03-7e 则保留,可以在这个范围内做文章 , TT 则是 Type 比如,attr 、layout 、string 等等,NN则是资源全局 ID。那么这个框架则是对资源包进行重新打包,每个插件重新分配资源 ID ,这样就保证了宿主和插件的资源不冲突。
- 动态代理注册:在 Android 中要使用四大组件,都是需要在 manifest 清单中注册,这样才可以使用,那如何在不注册情况也能使用呢,这里就是用到动态代理机制进行 Hook ,在发送 AMS 之前用占坑的组件来欺骗系统,通过认证后,再把真正要调用的组件还原回来,达到瞒天过海目的。
- 优点如下:
- 所有插件支持内置宿主包中。
- 插件的编码和资源文件的使用与普通开发应用没有差别。
- 通过设定 URI ,宿主以及 Native 应用插件,Web 插件,在线网页等能够方便进行通信。
- 支持 Android 、 iOS 、和 Html5 ,三者可以通过同一套 Javascript 接口实现通信。
- 缺点如下:
- 暂不支持 Service 的动态注册,不过这个可以通过将 Service 预先注册在宿主的 AndroidManifest.xml 文件中进行规避,因为 Service 的更新频率通常非常低。
- VirtualAPK
- 实现思路:
- VirtualAPK 对插件没有额外的约束,原生的 apk 即可作为插件。插件工程编译生成 apk后,即可通过宿主 App 加载,每个插件 apk 被加载后,都会在宿主中创建一个单独的 LoadedPlugin 对象。通过这些 LoadedPlugin 对象,VirtualAPK 就可以管理插件并赋予插件新的意义,使其可以像手机中安装过的 App 一样运行。
- 合并宿主和插件的ClassLoader 需要注意的是,插件中的类不可以和宿主重复
- 合并插件和宿主的资源 重设插件资源的 packageId,将插件资源和宿主资源合并
- 去除插件包对宿主的引用 构建时通过 Gradle 插件去除插件对宿主的代码以及资源的引用
- RePlugin
- RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案,由360手机卫士的RePlugin Team研发,也是业内首个提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。
- 热更新、热修复
- 代码修复
- 类加载方案
- Dex分包原理
- 单个Dex文件里面方法数不能超过65536个方法。
- 因为android会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的, short占两个字节(保存-2的15次方到2的15次方-1,即-32768~32767),最大保存的数量就是65536。
- 解决方案:
- 精简方法数量,删除没用到的类、方法、第三方库。
- 使用ProGuard去掉一些未使用的代码
- 对部分模块采用本地插件化的方式。
- 分割Dex
- Dex分包方案主要做的是在打包时将应用代码分成多个Dex,将应用启动时必须用到的类和这些类的直接引用类放到主Dex中,其他代码放到次Dex中。当应用启动时先加载主Dex,等到应用启动后再动态的加载次Dex。
- 类加载修复方案
- 如果Key.Class文件中存在异常,将该Class文件修复后,将其打入Patch.dex的补丁包
- (1) 方案一:
- 通过反射获取到PathClassLoader中的DexPathList,然后再拿到 DexPathList中的Element数组,将Patch.dex放在Element数组dexElements的第一个元素,最后将数组进行合并后并重新设置回去。在进行类加载的时候,由于ClassLoader的双亲委托机制,该类只被加载一次,也就是说Patch.dex中的Key.Class会被加载。
- (2)方案二:
- 提供dex差量包patch.dex,将patch.dex与应用的classes.dex合并成一个完整的dex,完整dex加载后得到dexFile对象,作为参数构建一个Element对象,然后整体替换掉旧的dex-Elements数组。(Tinker)
- 类加载方案的限制
- 方案一:
- 由于类是无法进行卸载,所以类如果需要重新加载,则需要重启App,所以类加载修复方案不是即时生效的。
- 在ART模式下,如果类修改了结构,就会出现内存错乱的问题。为了解决这个问题,就必须把所有相关的调用类、父类子类等等全部加载到patch.dex中,导致补丁包大,耗时严重。
- 方案二:
- 下次启动修复
- dex合并内存消耗可能导致OOM,最终dex合并失败
- Dex分包原理
- 底层替换方案
- 基本方案
- 主要是在Native层替换原有方法,ArtMethod结构体中包含了Java方法的所有信息,包括执行入口、访问权限、所属类和代码执行地址等。替换ArtMethod结构体中的字段或者替换整个ArtMethod结构体,就是底层替换方案。由于直接替换了方法,可以立即生效不需要重启。
- 优缺点
- (1)缺点
- 不能够增减原有类的方法和字段,如果我们增加了方法数,那么方法索引数也会增加,这样访问方法时会无法通过索引找到正确的方法。
- 平台兼容性问题,如果厂商对ArtMethod结构体进行了修改,替换机制就有问题。
- (2)优点
- Bug修复的即时性
- 生成的PATCH体积小,性能影响低
- 资源修复
- so库修复
- 谈一谈Proguard混淆技术
- 简介
- 压缩 --检查并移除代码中无用的类
- 压缩 --检查并移除代码中无用的类
- 优化--对字节码的优化,移除无用的字节码
- 混淆--混淆定义的名称,避免反编译
- 预监测--在java平台对处理后的代码再次进行检测
- 代码混淆只在上线时才会用到,debug模式下会关闭,是一种可选的技术。
- 压缩 --检查并移除代码中无用的类
- 优化--对字节码的优化,移除无用的字节码
- 混淆--混淆定义的名称,避免反编译
- 预监测--在java平台对处理后的代码再次进行检测
- 代码混淆只在上线时才会用到,debug模式下会关闭,是一种可选的技术。
- 压缩 --检查并移除代码中无用的类
- 优化--对字节码的优化,移除无用的字节码
- 混淆--混淆定义的名称,避免反编译
- 预监测--在java平台对处理后的代码再次进行检测
- 代码混淆只在上线时才会用到,debug模式下会关闭,是一种可选的技术。
- 优化--对字节码的优化,移除无用的字节码
- 混淆--混淆定义的名称,避免反编译
- 预监测--在java平台对处理后的代码再次进行检测
- 代码混淆只在上线时才会用到,debug模式下会关闭,是一种可选的技术。
- 为什么要使用代码混淆
- 因为Java是一种跨平台的解释性开发语言,而java的源代码会被编译成字节码文件,存储在.class文件中,由于跨平台的需要,java的字节码中包含了很多源代码信息,诸如变量名、方法名等等。并且通过这些名称来访问变量和方法,这些变量很多是无意义的,但是又很容易反编译成java源代码,为了防止这种现象,我们就需要通过proguard来对java的字节码进行混淆,混淆就是对发布的程序进行重新组织和处理,使得处理后的代码与处理前的代码有相同的功能,和不同的代码展示,即使被反编译也很难读懂代码的含义,哪些混淆过的代码仍能按照之前的逻辑执行得到一样的结果。
- 压缩 --检查并移除代码中无用的类
- 下面这些代码混淆的时候要注意保留,不能混淆
- Android系统组件,系统组件有固定的方法被系统调用。
- 被Android Resource 文件引用到的。名字已经固定,也不能混淆,比如自定义的View 。
- Android Parcelable ,需要使用android 序列化的。
- 其他Anroid 官方建议 不混淆的,如
- android.app.backup.BackupAgentHelper
- android.preference.Preference
- com.android.vending.licensing.ILicensingService
- Java序列化方法,系统序列化需要固定的方法。
- 枚举 ,系统需要处理枚举的固定方法。
- 本地方法,不能修改本地方法名
- annotations 注释
- 数据库驱动
- 有些resource 文件
- 用到反射的地方
- 简介
- Android app的编译过程
- (1)aapt(Android Asset Packaging Tool,android构建工具,在android-sdk的build-tool目录下)它的主要工作就是把项目中使用到的资源文件打包成R.java文件;
- (2)aidl工具会将aidl接口转换为java接口
- (3)java编译器就会将上述准备好的文件和我们在项目敲得java源文件打包成.class文件
- R.java文件+aidl接口+java源文件===>.class字节码文件;
- (4)如果是java程序,把.class文件交给java虚拟机就可以了,但是android使用的不是java虚拟机,是davlik虚拟机,所以编译成.class文件还不行,还需要通过dex工具把.class文件打包成.dex文件,这里如果你项目中使用了第三方的库,也会在这里一起打包成.dex文件;
- (5)通过apkbuilder工具将编译过的文件和那些没有编译过的文件(图片,视频等)加上上述的.dex文件一起打包成.apk文件;
- (6)这时候的.apk文件还无法去使用,还需要通过Jarsigner这个工具对.apk进行签名,至于签名的原因:为了保证每个应用程序开发商合法ID,防止部分开放商可能通过使用相同的Package Name来混淆替换已经安装的程序,我们需要对我们发布的APK文件进行唯一签名,保证我们每次发布的版本的一致性(如自动更新不会因为版本不一致而无法安装)。
- (7)签名过后的.apk文件其实就可以使用了,但是这时候的.apk文件太过杂乱,还需要Zipalign工具进行.apk文件的对其,减少内存,整理apk文件;
- Android app的安装流程
- android app的安装方式大致分为四种:
- (1)系统应用安装---开机完成,没有安装界面;
- (2)网络下载应用安装----通过mark应用完成,没有安装界面
- (3)adb工具安装----没有安装界面
- (4)第三方应用安装----通过sd卡中的apk文件安装,有安装界面,由Packageinstaller.apk应用处理完成及卸载
- 四大目录:
- system/app-----系统自带的应用程序,获得adb root权限才能够访问
- data /app-----用户程序安装目录,用户安装时将apk文件拷贝至此目录
- data/data-----存放应用程序的数据
- data/dalvik-cache----将apk中的dex文件安装到此目录下
- 安装过程(用户程序):复制APK安装包到data/app目录下,解压并扫描安装包,把dex文件(Dalvik字节码)保存到dalvik-cache目录,并data/data目录下创建对应的应用数据目录。
- 安装步骤
- (1) 拷贝apk文件到指定目录
- 在Android系统中,apk安装文件是会被保存起来的,默认情况下,用户安装的apk首先会被拷贝到 /data/app 目录下。/data/app目录是用户有权限访问的目录,在安装apk的时候会自动选择该目录存放用户安装的文件,而系统出厂的apk文件则被放到了 /system 分区下,包括 /system/app,/system/vendor/app,以及 /system/priv-app 等等,该分区只有Root权限的用户才能访问,这也就是为什么在没有Root手机之前,我们无法删除系统出厂的app的原因了。
- (2) 解压apk,拷贝文件,创建应用的数据目录
- 为了加快app的启动速度,apk在安装的时候,会首先将app的可执行文件(dex)拷贝到 /data/dalvik-cache 目录,缓存起来。然后,在/data/data/目录下创建应用程序的数据目录(以应用的包名命名),存放应用的相关数据,如数据库、xml文件、cache、二进制的so动态库等等。
- (3) 解析apk的AndroidManifinest.xml文件
- Android系统中,也有一个类似注册表的东西,用来记录当前所有安装的应用的基本信息,每次系统安装或者卸载了任何apk文件,都会更新这个文件。这个文件位于如下目录:/data/system/packages.xml。系统在安装apk的过程中,会解析apk的AndroidManifinest.xml文件,提取出这个apk的重要信息写入到packages.xml文件中,这些信息包括:权限、应用包名、APK的安装位置、版本、userID等等。由此,我们就知道了为啥一些应用市场和软件管理类的app能够很清楚地知道当前手机所安装的所有的app,以及这些app的详细信息了。另外一件事就是Linux的用户Id和用户组Id,以便他可以获得合适的运行权限。以上这些都是由PackageServiceManager完成的,下面我们会重点介绍PackageServiceManager。
- (4) 显示快捷方式
- 这些应用程序只是相当于在PackageManagerService服务注册好了,如果我们想要在Android桌面上看到这些应用程序,还需要有一个Home应用程序,负责从PackageManagerService服务中把这些安装好的应用程序取出来,并以友好的方式在桌面上展现出来,例如以快捷图标的形式。在Android系统中,负责把系统中已经安装的应用程序在桌面中展现出来的Home应用程序就是Launcher了
- (1) 拷贝apk文件到指定目录
- android app的安装方式大致分为四种:
- 对热修复和插件化的理解
- 插件化原理分析
- 模块化实现(好处,原因)
- 热修复,插件化
- 项目组件化的理解
- 描述清点击 Android Studio 的 build 按钮后发生了什么
- 谈谈你对安卓签名的理解。
- 请解释安卓为啥要加签名机制?
- 视频加密传输
- App 是如何沙箱化,为什么要这么做?
- 权限管理系统(底层的权限是如何进行 grant 的)?
- 性能优化
- Android的内存优化在我看来分为两点:避免内存泄漏、扩大内存,其实就是开源节流
- 如何对Android 应用进行性能分析以及优化?
- ddms 和 traceView
- 性能优化如何分析systrace?
- 用IDE如何分析内存泄漏?
- Java多线程引发的性能问题,怎么解决?
- 启动页白屏及黑屏解决?
- 启动太慢怎么解决?
- 怎么保证应用启动不卡顿?
- App启动崩溃异常捕捉
- 自定义View注意事项
- 现在下载速度很慢,试从网络协议的角度分析原因,并优化(提示:网络的5层都可以涉及)。
- Https请求慢的解决办法(提示:DNS,携带数据,直接访问IP)
- 如何保持应用的稳定性
- RecyclerView和ListView的性能对比
- ListView的优化
- RecycleView优化
- View渲染
- Bitmap如何处理大图,如一张30M的大图,如何预防OOM
- java中的四种引用的区别以及使用场景
- 强引用置为null,会不会被回收?
- 内存泄露
- 内存泄露的原因分析、如何避免
- 自定义Handler时如何避免内存泄漏
- 一般非静态内部类持有外部类的引用的情况下,造成外部类在使用完成后不能被系统回收内存,从而造成内存泄漏。为了避免这个问题,我们可以自定义的Handler声明为静态内部类形式,然后通过弱引用的方式,让Handler持有外部类的引用,从而可避免内存泄漏问题。
- 自定义Handler时如何避免内存泄漏
- 如何使用更高效的ArrayMap容器
- 优化工具的使用
- Lint
- MAT
- LeakCarary
- TraceView
- 内存管理机制
- GC垃圾回收机制
- 数据传输的效率优化
- ProtoBuffer提升传输效率
- 如何优化后台服务的内存消耗
- 如何保障服务常驻内存
- 双进程守护
- 多线程并发的性能问题
- 阻塞式队列
- 锁机制原理分析 竞争锁
- 原子锁、对象锁
- nio与bio的区别
- 混合式优化
- 避免render方法过渡重绘
- ScrollView内存优化实战(可见与不可见的终极解决方案)
- 分析程序启动流程、优化启动流程和提速
- 一些细节点
- 1.当查询完数据库之后,及时关闭Cursor对象。
- 2.记得在Activity的onPause方法中调用unregisterReceiver()方法,反注册广播
- 3.避免Content内存泄漏,比如在4.0.1之前的版本上不要讲Drawer对象置为static。当一个Drawable绑定到了View上,实际上这个View对象就会成为这个Drawable的一个callback成员变量,上面的例子中静态的sBackground持有TextView对象lable的引用,而lable只有Activity的引用,而Activity会持有其他更多对象的引用。sBackground生命周期要长于Activity。当屏幕旋转时,Activity无法被销毁,这样就产生了内存泄露问题。
- 4.尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,当非静态内部类的引用的声明周期长于Activity的声明周期时,会导致Activity无法被GC正常回收掉。
- 5.谨慎使用线程Thread!!这条是很多人会犯的错误: Java中的Thread有一个特点就是她们都是直接被GC Root所引用,也就是说Dalvik虚拟机对所有被激活状态的线程都是持有强引用,导致GC永远都无法回收掉这些线程对象,除非线程被手动停止并置为null或者用户直接kill进程操作。所以当使用线程时,一定要考虑在Activity退出时,及时将线程也停止并释放掉
- 6.使用Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露
- 对bitmap的优化
- ①及时回收BitMap的内存。
- ②捕获异常。
- ③缓存常用的BitMap对象。
- ④压缩图片。详细介绍请查看:
- 内存泄露的原因分析、如何避免
- 安装包优化
- 使用混淆,可以在一定程度上减少 apk 体积,但实际效果微乎其微
- 减少应用中不必要的资源文件,比如图片,在不影响 APP 效果的情况下尽量压缩图片,有一定的效果
- 在使用了 SO 库的时候优先保留 v7 版本的 SO 库,删掉其他版本的SO库。原因是在 2018 年,v7 版本的 SO 库可以满足市面上绝大多数的要求,可能八九年前的手机满足不了,但我们也没必要去适配老掉牙的手机。实际开发中减少 apk 体积的效果是十分显著的,如果你使用了很多 SO 库,比方说一个版本的SO库一共 10M,那么只保留 v7 版本,删掉 armeabi 和 v8 版本的 SO 库,一共可以减少 20M 的体积。
- UI优化
- a.合理选择RelativeLayout、LinearLayout、FrameLayout,RelativeLayout会让子View调用2次onMeasure,而且布局相对复杂时,onMeasure相对比较复杂,效率比较低,LinearLayout在weight>0时也会让子View调用2次onMeasure。LinearLayout weight测量分配原则。
- b.使用标签
- c.减少布局层级,可以通过手机开发者选项>GPU过渡绘制查看,一般层级控制在4层以内,超过5层时需要考虑是否重新排版布局。
- d.自定义View时,重写onDraw()方法,不要在该方法中新建对象,否则容易触发GC,导致性能下降
- e.使用ListView时需要复用contentView,并使用Holder减少findViewById加载View。
- f.去除不必要背景,getWindow().setBackgroundDrawable(null)
- g.使用TextView的leftDrawabel/rightDrawable代替ImageView+TextView布局
- 内存优化
- 主要为了避免OOM和频繁触发到GC导致性能下降
- a.Bitmap.recycle(),Cursor.close,inputStream.close()
- b.大量加载Bitmap时,根据View大小加载Bitmap,合理选择inSampleSize,RGB_565编码方式;使用LruCache缓存
- c.使用 静态内部类+WeakReference 代替内部类,如Handler、线程、AsyncTask
- d.使用线程池管理线程,避免线程的新建
- e.使用单例持有Context,需要记得释放,或者使用全局上下文
- f.静态集合对象注意释放
- g.属性动画造成内存泄露
- h.使用webView,在Activity.onDestory需要移除和销毁,webView.removeAllViews()和webView.destory()
- 备:使用LeakCanary检测内存泄露
- 响应速度优化
- Activity如果5秒之内无法响应屏幕触碰事件和键盘输入事件,就会出现ANR,而BroadcastReceiver如果10秒之内还未执行操作也会出现ANR,Serve20秒会出现ANR 为了避免ANR,可以开启子线程执行耗时操作,但是子线程不能更新UI,因此需要Handler消息机制、AsyncTask、IntentService进行线程通信。
- 布局优化
- 在 LinearLayout 和 RelativeLayout 都可以完成布局的情况下优先选择 RelativeLayout,可以减少 View 的层级
- 将常用的布局组件抽取出来使用 \< include \>标签
- 通过 \< ViewStub \>标签来加载不常用的布局
- 使用 \< Merge \>标签来减少布局的嵌套层次
- 网络优化
- 尽量减少网络请求,能够合并的就尽量合并
- 避免 DNS 解析,根据域名查询可能会耗费上百毫秒的时间,也可能存在DNS劫持的风险。可以根据业务需求采用增加动态更新 IP 的方式,或者在 IP 方式访问失败时切换到域名访问方式。
- 大量数据的加载采用分页的方式
- 网络数据传输采用 GZIP 压缩
- 加入网络数据的缓存,避免频繁请求网络
- 上传图片时,在必要的时候压缩图片
- 其他
- a.常量使用static final修饰
- b.使用SparseArray代替HashMap
- c.使用线程池管理线程
- d.ArrayList遍历使用常规for循环,LinkedList使用foreach
- e.不要过度使用枚举,枚举占用内存空间比整型大
- f.字符串的拼接优先考虑StringBuilder和StringBuffer
- g.数据库存储是采用批量插入+事务
- 开源框架
- 网络框架库 Okhttp
- Okhttp 连接池,拦截器。如何给某些特定域名的url增加header,如果是自己封装的代码,可以在封装Request中可以解决,也可以增加拦截器,通过拦截器去做。
- Okhttp的如何缓存链接的
- 网络框架库 volley 和okhttp的使用区别
- 依赖注入型框架Dagger
- 视图注入型框架ButterKnife框架的使用源码及其实现原理
- RxJava
- Rxjava的操作符
- RxJava的消息订阅和线程切换原理
- 消息通知 EventBus
- EventBus原理,内部实现:观察者模式+注解+反射
- register
- 获取订阅者的 Class 对象
- 使用反射查找订阅者中的事件处理方法集合
- 遍历事件处理方法集合,调用 subscribe(subscriber,subscriberMethod) 方法,在 subscribe 方法内:
- 通过 subscriberMethod 获取处理的事件类型 eventType
- 将订阅者 subscriber 和方法 subscriberMethod 绑在一起形成一个 Subscription 对象
- 通过 subscriptionsByEventType.get(eventType)获取 Subscription 集合
- 如果 Subscription 集合为空则创建一个新的集合,这一步目的是延迟集合的初始化
- 拿到 Subscription 集合后遍历这个集合,通过比较事件处理的优先级,将新的 Subscription 对象加入合适的位置
- 通过typesBySubscriber.get(subscriber)获取事件类型集合
- 如果事件类型集合为空则创建一个新的集合,这一步目的是延迟集合的初始化
- 拿到事件类型集合后将新的事件类型加入到集合中
- 判断当前事件类型是否是 sticky
- 如果当前事件类型不是 sticky(粘性事件),subscribe(subscriber,subscriberMethod)到此终结
- 如果是 sticky,判断 EventBus 中的一个事件继承性的属性,默认是 true
- 如果事件继承性为 true,遍历这个 Map 类型的 stickEvents,通过 isAssignableFrom 方法判断当前事件是否是遍历事件的父类,如果是则发送事件
- 如果事件继承性为 false,通过 stickyEvents.get(eventType)获取事件并发送
- post
- postSticky
- 将事件加入到 stickyEvents 这个 Map 类型的集合中
- 调用 post 方法
- post
- 将事件加入当前线程的事件队列中
- 通过 while 循环不断从事件队列中取出事件并调用 postSingleEvent 方法发送事件
- 在 postSingleEvent 中,判断事件继承性,默认为true
- 事件继承性为true,找到当前事件所有的父类型并调用 postSingleEventForEventType 方法发送事件
- 事件继承性为 false,只发送当前事件类型的事件
- 在 postSingleEventForEventType 中,通过 subscriptionsByEventType.get(eventClass)获取 Subscription 类型集合
- 遍历这个集合,调用 postToSubscription 发送事件
- 在 postToSubscription 中分为四种情况
- POSTING,调用 invokeSubscriber(subscription, event)处理事件,本质是 method.invoke()反射
- MAIN,如果在主线程直接 invokeSubscriber 处理;反之通过 handler 切换到主线程调用 invokeSubscriber 处理事件
- BACKGROUND,如果不在主线程直接 invokeSubscriber 处理事件;反之开启一条线程,在线程中调用 invokeSubscriber 处理事件
- ASYNC,开启一条线程,在线程中调用 invokeSubscriber 处理事件
- 在 postToSubscription 中分为四种情况
- postSticky
- unregister:
- 删除 subscriptionsByEventType 中与订阅者相关的所有 subscription
- 删除 typesBySubscriber 中与订阅者相关的所有类型
- register
- EventBus是异步还是同步
- EventBus可否跨进程问题?代替EventBus的方法(RxBus)
- EventBus原理,内部实现:观察者模式+注解+反射
- 图片库对比
- 图片库的源码分析
- 图片框架缓存实现
- LRUCache原理
- 图片加载原理
- 自己去实现图片库,怎么做?
- Glide源码解析
- Glide使用什么缓存?
- Glide内存缓存如何控制大小?
- 图片加载库(Fresco、Glide、Picasso)
- .内存缓存LruCache的使用
- LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序排序的。
- 当调用put()方法时,就会在结合中添加元素,并调用trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队首元素,即近期最少访问的元素。
- 当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队尾。
- 硬盘缓存DisLruCache的使用
- Android 弱引用和软引用
- Glide缓存策略
- Glide缓存机制大致分为三层:内存缓存、弱引用缓存、磁盘缓存。
- 存的顺序是:弱引用、内存、磁盘
- 取的顺序是:内存、弱引用、磁盘
- 项目中选择了哪个图片加载库?为什么选择它?其他库不好吗?这几个库的区别
- 项目中选择图片库它的原理,如Glide(LruCache结合弱引用),那么面试官会问LruCache原理,进而问LinkedHashMap原理,这样一层一层地问,所以建议看到不懂的追进去看。如Fresco是用来MVC设计模式,5.0以下是用了共享内存,那共享内存怎么用?Fresco怎么实现圆角?Fresco怎么配置缓存?
- 假设让你设计一个图片加载器,你会怎么设计?
- ImageLoader的工作原理:在显示图片的时候,它会先在内存中查找,如果没有找到,就去本地查找,如果还没有,就开一个新的线程去下载这张图片,下载成功会把图片同时缓存到内存本地去。
- Freso的原理:设计一个Image Pipeline的概念,它负责先后检查内存,磁盘文件,如果都没有就老老实实从网络下载图片。
- .内存缓存LruCache的使用
- 消息推送Push
- 项目中消息推送是自己做的还是用了第三方?如极光。还有没有用过其他的?这几家有什么优势区别,基于什么原因选择它的?
- 消息推送原理是什么?如何实现心跳连接?
- 数据库框架GreenDAO
- 网络框架库 Okhttp
- 软件架构
- MVC、MVP、MVVM:比较异同
- MVC最核心就是通过Control层来进行调控。Model主要是对数据的操作,View层主要是各种组件和自定义View,Control则主要是Activity。通过Control调控,将View展示和Model操作分离,但是两者还是有交互,有一定的耦合。另外,Activity除了负责业务逻辑处理还要承载View,处理UI视图显示,所以会比较臃肿。
- MVP是对MVC的一种改良模式。Presenter层隔断了View层和Model层,降低了耦合度。Activity不再同时承载Control和UI显示,只单独负责View展示。缺点就是Presenter层左右View层和Model层的桥梁,会很臃肿。而且基本上每个Activity要有相应的Presenter来处理。
- MVVM将Presenter替换成了ViewModel,并通过双向的数据绑定来实现视图和数据的交互。简化了开发,数据和视图只需要进行一次绑定即可。缺点就是实现方式还比较不完善规范
- 谈谈你对Android设计模式的理解
- MVC MVP MVVM原理和区别
- 你所知道的设计模式有哪些?
- 项目中常用的设计模式
- 手写生产者/消费者模式
- 写出观察者模式的代码
- 适配器模式,装饰者模式,外观模式的异同?
- 用到的一些开源框架,介绍一个看过源码的,内部实现过程。
- 谈谈对RxJava的理解
- RxJava的功能与原理实现
- RxJava的作用,与平时使用的异步操作来比的优缺点
- 说说EventBus作用,实现方式,代替EventBus的方式
- 从0设计一款App整体架构,如何去做?
- 说一款你认为当前比较火的应用并设计(比如:直播APP,P2P金融,小视频等)
- 谈谈对java状态机理解
- Fragment如果在Adapter中使用应该如何解耦?
- Binder机制及底层实现
- 对于应用更新这块是如何做的?(解答:灰度,强制更新,分区域更新)?
- 实现一个Json解析器(可以通过正则提高速度)
- 统计启动时长,标准
- MVC、MVP、MVVM:比较异同
- NDK、jni、Binder、AIDL、进程通信有关
- 请介绍一下NDK
- 什么是NDK库?
- jni用过吗?
- 动态注册和静态注册
- 共享库和静态库的区别和使用场景
- JNI中C/C++和Java相互调用各自使用的机制是什么?
- 如何在jni中注册native函数,有几种注册方式?
- Java如何调用c、c++语言?
- jni如何调用java层代码?
- 进程间通信的方式?
- Binder机制
- 简述IPC?
- 什么是AIDL?
- AIDL解决了什么问题?
- AIDL如何使用?
- Android 上的 Inter-Process-Communication 跨进程通信时如何工作的?
- 多进程场景遇见过么?
- Android进程分类?
- 进程和 Application 的生命周期?
- 进程调度
- 谈谈对进程共享和线程安全的认识
- 谈谈对多进程开发的理解以及多进程应用场景
- 什么是协程?
- 跨平台技术
- React Native
- Weex
- Flutter
- framework层、ROM定制、Ubuntu、Linux之类的问题
- java虚拟机的特性
- 谈谈对jvm的理解
- JVM内存区域,开线程影响哪块内存
- 对Dalvik、ART虚拟机有什么了解?
- Art和Dalvik对比
- 虚拟机原理,如何自己设计一个虚拟机(内存管理,类加载,双亲委派)
- 谈谈你对双亲委派模型理解
- JVM内存模型,内存区域
- 类加载机制
- 谈谈对ClassLoader(类加载器)的理解
- 谈谈对动态加载(OSGI)的理解
- 内存对象的循环引用及避免
- 内存回收机制、GC回收策略、GC原理时机以及GC对象
- 垃圾回收机制与调用System.gc()区别
- Ubuntu编译安卓系统
- 系统启动流程是什么?(提示:Zygote进程 –> SystemServer进程 –> 各种系统服务 –> 应用进程)
- 大体说清一个应用程序安装到手机上时发生了什么
- 简述Activity启动全部过程
- App启动流程,从点击桌面开始
- 逻辑地址与物理地址,为什么使用逻辑地址?
- Android为每个应用程序分配的内存大小是多少?
- Android中进程内存的分配,能不能自己分配定额内存?
- 进程保活的方式
- 如何保证一个后台服务不被杀死?(相同问题:如何保证service在后台不被kill?)比较省电的方式是什么?
- App中唤醒其他进程的实现方式
- Android基础知识点
- 网络
- 网络层关系
- GET,POST区别?
- cookie、session
- TCP和UDP的区别
- 1.基于连接与无连接
- 2.TCP要求系统资源较多,UDP较少;
- 3.UDP程序结构较简单
- 4.流模式(TCP)与数据报模式(UDP);
- 5.TCP保证数据正确性,UDP可能丢包
- 6.TCP保证数据顺序,UDP不保证
- UDP应用场景:
- 1.面向数据报方式
- 2.网络数据大多为短消息
- 3.拥有大量Client
- 4.对数据安全性无特殊要求
- 5.网络负担非常重,但对响应速度要求高
- TCP三次握手(一定要讲清楚,SYN、ACK等标记位怎样的还有报文结构都需要熟悉下),四次挥手。为什么要三次握手?DDoS攻击。为什么握手三次,挥手要四次?
- Http报文结构
- 一次网络请求的过程是怎样的?
- Http和Https有什么不同?
- http 是超文本传输协议,而 https 可以简单理解为安全的 http 协议。https 通过在 http 协议下添加了一层 ssl 协议对数据进行加密从而保证了安全。https 的作用主要有两点:建立安全的信息传输通道,保证数据传输安全;确认网站的真实性。
- https 需要到 CA 申请证书,很少免费,因而需要一定的费用
- http 是明文传输,安全性低;而 https 在 http 的基础上通过 ssl 加密,安全性高
- 二者的默认端口不一样,http 使用的默认端口是80;https使用的默认端口是 443
- HTTPS 的工作流程
- 加密算法分为两类:对称加密和非对称加密。
- 对称加密: 加密和解密用的都是相同的秘钥,优点是速度快,缺点是安全性低。常见的对称加密算法有 DES、AES 等等。
- 非对称加密: 非对称加密有一个秘钥对,分为公钥和私钥。一般来说,私钥自己持有,公钥可以公开给对方,优点是安全性比对称加密高,缺点是数据传输效率比对称加密低。采用公钥加密的信息只有对应的私钥可以解密。常见的非对称加密包括RSA等。
- 在正式的使用场景中一般都是对称加密和非对称加密结合使用,使用非对称加密完成秘钥的传递,然后使用对称秘钥进行数据加密和解密。二者结合既保证了安全性,又提高了数据传输效率。
- 具体流程
- 1、客户端(通常是浏览器)先向服务器发出加密通信的请求
- 支持的协议版本,比如 TLS 1.0版
- 一个客户端生成的随机数 random1,稍后用于生成"对话密钥"
- 支持的加密方法,比如 RSA 公钥加密
- 支持的压缩方法
- 2、服务器收到请求,然后响应
- 确认使用的加密通信协议版本,比如 TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信
- 一个服务器生成的随机数 random2,稍后用于生成"对话密钥"
- 确认使用的加密方法,比如 RSA 公钥加密
- 服务器证书
- 3、客户端收到证书之后会首先会进行验证
- 首先验证证书的安全性
- 验证通过之后,客户端会生成一个随机数 pre-master secret,然后使用证书中的公钥进行加密,然后传递给服务器端
- 4、服务器收到使用公钥加密的内容,在服务器端使用私钥解密之后获得随机数 pre-master secret,然后根据 radom1、radom2、pre-master secret 通过一定的算法得出一个对称加密的秘钥,作为后面交互过程中使用对称秘钥。同时客户端也会使用 radom1、radom2、pre-master secret,和同样的算法生成对称秘钥。
- 5、然后再后续的交互中就使用上一步生成的对称秘钥对传输的内容进行加密和解密。
- 1、客户端(通常是浏览器)先向服务器发出加密通信的请求
- 加密算法分为两类:对称加密和非对称加密。
- HTTPS中SSL证书认证的过程
- SSL/TLS是怎么进行加密握手的?证书怎么校验?
- 对称性加密算法和非对称加密算法有哪些?挑一个熟悉的加密算法简单介绍下?
- DNS解析是怎样的?
- 网络框架对比和源码分析
- 自己去设计网络请求框架,怎么做?
- okhttp源码
- 网络请求缓存处理,okhttp如何处理网络缓存的
- 从网络加载一个10M的图片,说下注意事项
- TCP的3次握手和四次挥手
- TCP与UDP的区别
- TCP与UDP的应用
- HTTP协议
- HTTP1.0与2.0的区别
- HTTP报文结构
- HTTP与HTTPS的区别以及如何实现安全性
- 如何验证证书的合法性?
- https中哪里用了对称加密,哪里用了非对称加密,对加密算法(如RSA)等是否有了解?
- client如何确定自己发送的消息被server收到?
- 谈谈你对WebSocket的理解
- WebSocket与socket的区别
- 设计模式
- 设计模式六大原则
- 单一原则。就是说一个类只有一个明确的职责,不易过多职责封装在一个类里面。
- 开闭原则。对于修改是关闭的,对于扩展的开放的,这样主要目的是为了提高可扩展性。
- 依赖倒置原则。高层不依赖于低层,两者都依赖于抽象,抽象不依赖于细节实现,具体实现依赖于抽象,也就是说要面向接口编程。
- 里氏替换原则。也就说子类运行的功能,父类也能运行,强调继承的重要性
- 迪米特原则。一个类要了解另外一个类最少的内容,强调低耦合,耦合分解
- 接口隔离原则。一个类不要继承不需要的接口,接口可拆分,不要冗余在一个总接口中,实现自己所需接口即可。
- 1.单例模式:好几种写法,要求会手写,分析优劣。一般双重校验锁中用到volatile,需要分析volatile的原理
- 2.观察者模式:要求会手写,有些面试官会问你在项目中用到了吗?实在没有到的可以讲一讲EventBus,它用到的就是观察者模式
- 3.适配器模式:要求会手写,有些公司会问和装饰器模式、代理模式有什么区别?
- 4.建造者模式+工厂模式:要求会手写
- 5.策略模式:这个问得比较少,不过有些做电商的会问
- 设计模式六大原则
- 数据结构
- 数组
- 栈
- 队列
- 链表
- 二叉树
- 霍夫曼树
- 红黑树
- 1.HashMap、LinkedHashMap、ConcurrentHashMap,在用法和原理上有什么差异,很多公司会考HashMap原理,通过它做一些扩展,比如中国13亿人口年龄的排序问题,年龄对应桶的个数,年龄相同和hash相同问题类似。
- 2.ArrayList和LinkedList对比,这个相对简单一点。
- 3.平衡二叉树、二叉查找树、红黑树
- 4.Set原理,这个和HashMap考得有点类似,考hash算法相关,被问到过常用hash算法。HashSet内部用到了HashMap
- 算法
- 排序算法
- 内部排序
- 插入排序
- 直接插入排序
- 希尔排序
- 选择排序
- 简单选择排序
- 堆排序
- 交换排序
- 冒泡排序
- 快速排序
- 归并排序
- 基数排序
- 插入排序
- 外部排序
- 内部排序
- 查找算法
- 二分查找
- 排序算法有哪些?
- 最快的排序算法是哪个?
- 手写一个冒泡排序
- 手写快速排序代码
- 快速排序的过程、时间复杂度、空间复杂度
- 手写堆排序
- 堆排序过程、时间复杂度及空间复杂度
- 写出你所知道的排序算法及时空复杂度,稳定性
- 二叉树给出根节点和目标节点,找出从根节点到目标节点的路径
- 给阿里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个苹果,一天只能吃一个、两个、或者三个,问多少天可以吃完?
- 排序算法
- 非技术问题
- 介绍你做过的哪些项目
- 都使用过哪些框架、平台?
- 都使用过哪些自定义控件?
- 研究比较深入的领域有哪些?
- 对业内信息的关注渠道有哪些?
- 最近都读哪些书?
- 有没有什么开源项目?
- 自己最擅长的技术点,最感兴趣的技术领域和技术点
- 项目中用了哪些开源库,如何避免因为引入开源库而导致的安全性和稳定性问题
- 实习过程中做了什么,有什么产出?
- HR提出的面试问题
- 您在前一家公司的离职原因是什么?
- 讲一件你印象最深的一件事情
- 介绍一个你影响最深的项目
- 介绍你最热爱最擅长的专业领域
- 公司实习最大的收获是什么?
- 与上级意见不一致时,你将怎么办?
- 自己的优点和缺点是什么?并举例说明?
- 你的学习方法是什么样的?实习过程中如何学习?实习项目中遇到的最大困难是什么以及如何解决的?
- 说一件最能证明你能力的事情
- 针对你你申请的这个职位,你认为你还欠缺什么
- 如果通过这次面试我们单位录用了你,但工作一段时间却发现你根本不适合这个职位,你怎么办?
- 项目中遇到最大的困难是什么?如何解决的?
- 你的职业规划以及个人目标、未来发展路线及求职定位
- 如果你在这次面试中没有被录用,你怎么打算?
- 评价下自己,评价下自己的技术水平,个人代码量如何?
- 通过哪些渠道了解的招聘信息,其他同学都投了哪些公司?
- 业余都有哪些爱好?
- 你做过的哪件事最令自己感到骄傲?
- 假如你晚上要去送一个出国的同学去机场,可单位临时有事非你办不可,你怎么办?
- 就你申请的这个职位,你认为你还欠缺什么?
- 当前的offer状况;如果BATH都给了offer该如何选?
- 你对一份工作更看重哪些方面?平台,技术,氛围,城市,还是money?
- 理想薪资范围;杭州岗和北京岗选哪个?
- 理想中的工作环境是什么?
- 谈谈你对跳槽的看法
- 说说你对行业、技术发展趋势的看法
- 实习过程中周围同事/同学有哪些值得学习的地方?
- 家人对你的工作期望及自己的工作期望
- 如果你的工作出现失误,给本公司造成经济损失,你认为该怎么办?
- 若上司在公开会议上误会你了,该如何解决?
- 是否可以实习,可以实习多久?
- 在五年的时间内,你的职业规划
- 你看中公司的什么?或者公司的那些方面最吸引你?
更多相关文章
- 创建点对点WiFi直连——翻译自developer.android.com Training
- android接口回调的两中简单写法
- libevent 多线程IO
- android面试2
- 吐血总结 2020 Android(安卓)实习面经
- activity使用theme.dialog且activity中有dialog时按返回按钮dial
- android根据经纬度查询位置名称
- 各版本安卓手机USB调试模式打开方法
- Android学习小Demo(15)一个自定义AlertDialog的实现