一、冷启动优化
冷启动 TOP 50 从 4.4s 优化至 1.2s TOP 90 从 9.17s 优化至 2.57s
首先说一下数据收集,冷启动的起始收集时机为 Application的 attachBaseContext,终点为,MainActivity 的 onWindowFocusChanged 。 进程开始时机还有 android.os.Process#getStartElapsedRealtime、进程状态文件中解析,但是这两种时机需要兼容不同手机,为了保持数据一致性取 attachBaseContext 为起始。onResume 的时候页面还不可见,onWindowFocusChanged 是页面显示的第一帧,作为冷启动的结束。
现状分析,我们将冷启动中的各个生命周期都进行打点,attachBaseContext、installProvider、onCreate、Activty 生命周期。
拿到数据之后,我们选择耗时最高、优化力度最大的区间,去制定优化方案 优化方案按三个维度去考虑,1.主线程耗时优化、2.后台线程优化 3.全局优化
1.主线程优化,主要就是找,代码上的问题、例如IO耗时、IPC耗时、锁、启动初始化懒加载、启动初始化放后台、contentProvider合并/去除、SplashActivity 与 MainActivity 合并。 2.后台线程优化策略,线程池管控。 3.全局优化、Dex重排序、资源文件重排、触发Dex2oat编译、GC抑制、xml 代码化。
| 治理方案 | 原因 | 手段 |
|---|---|---|
| GC抑制 | GC在进行回收时会挂起所有线程,目的是为了在进行内存对象标活分代时能够线程安全,而挂起线程会导致启动时间增长。 | 通过hook dvm库代码,在启动阶段通过手动抑制GC的触发,从而达成减少由于GC导致所有线程挂起等待的时间。 |
| Dex重排序 | 由于Dex文件在打包编译阶段,是无序的将class文件编译进dex文件,而在启动阶段从持久化存储设备读取文件时,会将一段连续的文件块缓存在内存中,如果启动过程中需要的class文件分散的分布在dex文件的各处,则会在启动阶段触发多次IO操作,引起不必要耗时。 | 收集启动阶段class文件load顺序,明确重排序顺序;根据class文件启动顺序,将dex文件重新排序; |
| Hook ClassVerify | Dalvik 虚拟机在进行load class时有一步verify class,verify class的目的是识别一些非法指令序列。对于一个class verify的时间很短,但是启动阶段会大量load class,总的加起来也是一个长耗时。 | 对于已经成功验证的类,通过禁用verify,节省verify的时间。 |
落地防劣化手段 封装了一套启动框架,首先将冷启动分为不同的阶段,attachBaseContext、onCreate、Activity onCreate、onWindowFocusChange、然后将初始化任务封装为不同的Task,扔到不同的队列中,然后对每一个Task的耗时进行统计。这套方案,比较好的能约束后续开发初始化时的考究、排查耗时问题能更快的定位到对应的Task。
二、卡顿优化
卡顿原因
1、主线程太忙(CPU)
- Vsync 中断消息没有被立马执行
- input 和 animation 事件处理严重耗时
2、绘制任务太重(CPU&&GPU)
- CPU绘制耗时较多
- GPU渲染压力太大
工具:
- GPU 柱状图
- CPU profiler/ TraceView 主要解决进程方法耗时、性能损耗比较大
- Systrace & Perfetto 可以展示整个设备在做什么IO操作,GPU情况,监控系统调用的耗时情况,需要在应用中打点。
- layout inspector 布局层级
- GPU 过度绘制
优化手段
- layerType 合理使用 Hardware Layer,对于复制且不常用的view 可以添加,DisplayList 非常复杂,每次渲染都会去重新画一次,添加了Hardware Layer 就是将最终的产物(纹理/bigmap)存到GPU里面,这样的话就不用频繁去渲染了。
- 预加载,在明确跳转意图的前提下,提前加载数据
- 内存管理、bitmap采样率
- 布局层级降低、自定义组件、过度绘制
- 代码逻辑排查
- 线程管控
三、面试题 QA
-
okhttp 原理、涉及的设计模式
-
jvm
-
泛型以及泛型实例化
本质上就是参数化类型,就相当于一种对数据结构的泛化定义。在类、接口、和方法的创建中。
泛型作用:
- 泛化:可以用T代表任意类型
- 类型安全:泛型的一个主要目标就是提高Java程序的类型安全,使用泛型可以使编译器知道变量的类型限制。如果不用泛型,则必须使用强制类型转换。
- 消除强制类型转换:可以消除源代码中许多强制类型转换,使代码更加易读
泛型的实现方式: Java使用擦拭方实现泛型
- 编译器把类型视为Object
- 编译器根据实现安全的强制转型
不能实例化
-
LinkList 和 ArrayList,使其线程安全的方法
LinkLst 内部实现为双向链表,适用于增加删除。大小不固定。查找使用的类似二分查找 ArrayList 内部基于数组实现,大小固定,需要扩容操作,扩容为原1.5倍,默认为10.
RandomAccess 判断集合是否实现了随机访问,通过此判断选择不同的循环方式
实现线程安全的方案
- Collections.synchronizedList。(SynchronizedList)本质上就是在每个方法上,添加了 synchronized 锁。
SynchronizedList中的iterator和listIterator方法都没有实现同步,所以在获取迭代器的时候不会阻塞。如果需要迭代的话,直接用synchronized包一下队列对象就可以了。 - Vector。本质上是在方法层面上添加了 synchronized 锁。
- CopyOnWriteArrayList。涉及线程安全的部分,是通过写时复制的方式来实现。它内部有个volatile数组来保持数据。在“添加/修改/删除”数据时,会先获取互斥锁,再新建一个数组,并将更新后的数据拷贝到新建的数组中,最后再将该数组赋值给volatile数组,然后再释放互斥锁。
- Collections.synchronizedList。(SynchronizedList)本质上就是在每个方法上,添加了 synchronized 锁。
-
HashMap 原理以及扩容机制
- HashMap数据结构是数组和链表的结合体。put操作,通过key的hashcode配合离散算法 h = hashcode (h^ (h >>> 16))获取hash值,在通过 ((n - 1) & hash)算法进行求余,找到对应的index位置,如果发生hash碰撞,则通过链表的方式拼接,如果链表长度大于阈值8,则转为红黑树结构。
- 负载因子 0.75,经验值,当其较大时,扩容的可能性变少,链上的元素变多,占用额外的内存较少,查询效率变长。
- 扩容算法,超过0.75后,进行扩容,返回大于等于该数的最小2次幂(2^n),选择这个的原因是因为 hash%(2^n)=hash^(2^n-1),可以提高求余的效率
- HashMap 时间复杂度是 O(1)
- HashMap 是先扩容,再树化,因为链表过长而数组过短,会经常发生hash碰撞,这个时候树形化其实是治标不治本,因为引起链表过长的根本原因是数组过短。执行树形化之前,会先检查数组长度,如果长度小于 64,则对数组进行扩容,而不是进行树形化。
- hash 碰撞解决方案。1.开发地址法 2.再哈希法 3. 链地址法 4.公共溢出区
-
HashMap线程安全
- hashTable 所有方法都加上synchronized
- Collections.synchronizedMap 同上原理
- ConcurrentHashMap。底层volatile且自旋CAS操作,并发效率高。Node数组 + 链表/红黑树
-
进程、线程、协程
- 进程是系统进行资源分配的最小单位。操作系统会为进程分配CPU ,内存,磁盘空间等一系列资源。不同进程通过进程间通信来通信。由于进程比较重,占据独立的地址空间,所以进程间上下文切换的开销比较大。
- 线程是CPU调度的最小单位。线程是由进程创建的,共享进程的的内存地址空间,操作系统会为线程创建堆栈的而分配内存,这些开销并不大。线程通过共享变量和锁机制协作。
- 协程,单线程下实现并发,是一种用户态的轻量级线程,及协程由用户程序自己调度的
并发: 需要cpu 调度,多任务是交替运行的。 有可能是模拟的并发,比如1核CPU。
并行: 需要硬件支持, 多和同时处理
-
锁
-
handler
- 原理过程,handler 发送消息,将 Runnable 作为参数,创建出 Message,Message的 callBack 即是 Runnable。记录当前post的时间,插入到消息队列当中,消息队列本质是单链表,插入消息队列时,会根据消息时间进行选择性位置插入。并且在插入队列时,Message 会持有 Handler实例。然后Loop在做循环的时候(loop()),取出当前消息队列中的消息,通过 message.callback.run(); 直接run起来。
- 循环不会影响主线程卡死的原因。是因为使用了 Linux下的通讯机制,epoll机制,当消息队列中没有消息时,主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。本质上就是, 管道就是内存中的一个特殊的一个文件,这个文件有两个句柄(其实就是java中的引用),一个是读的引用,一个是写的引用,当写的一端写入内容时,读的一端就会被唤醒,读的一端在没有读到消息时会休眠
- Handler 被作为非静态内部类或者匿名内部类的时候,handler 默认持有外部类Acitivty的引用。解决方案:a.静态内部类+弱引用(静态内部类不持有外部类引用,如何要使用Activity则增加弱引用) b.Activity 回收时,手动清空消息队列
- 消息分为三种,同步消息、异步消息、同步屏障。同步消息就是普通顺序插入到消息队列的消息,异步消息在没有同步屏障时,调用顺序与同步消息无异。同步屏障的应用在于说,VSYNC 信号来临时,添加同步屏障,阻塞同步消息,将异步消息提前执行,这也是为什么 VSYNC 信号来临之后,可以里面保证CPU执行,而不是等待。
- Handler 是通过 ThreadLocal 保证线程中Looper的唯一的。ThreadLocal 内部有一个 ThreadLocalMap ,其中Key 是 ThreadLocal 本身,一个线程只有一个 ThreadLocalMap,所以保证了唯一性
-
requestLayout 与 invalidate 区别
- requestLayout 会触发 Measure、Layout 过程,如果尺寸发生改变,则会调用 invalidate
- invalidate 只会触发 Draw 过程
- postInvalidate 通过ViewRootImpl 里的handler切换到UI线程,最终执行
invalidate 4.我们调用View的requestLayout或者invalidate时,最终都会触发ViewRootImp执行scheduleTraversals()方法。这个方法中ViewRootImp会通过Choreographer来注册个接收Vsync的监听,并且在主线程中插入消息屏障,当接收到系统体层发送来的Vsync后我们就执行doTraversal()来重新绘制界面。通过上面的分析我们调用invalidate等刷新操作时,系统并不会立即刷新界面,而是等到Vsync消息后才会刷新页面。
-
事件分发机制
-
Android 中常用的手势事件 一般是 DOWN UP MOVE CANCEL、除此之外还有一些多指操作的事件。
-
事件的传递大致流程为 Activity -> PhoneWindow -> DecorView(ViewGroup) -> View
-
DOWN 的事件传递,用户在点击到屏幕的时候,先从触摸的Activity ,Acitivity 的 dispatchTouchEvent 触发后 ,通过调用 PhoneWindow 的方法,将Event 传递给ViewGroup。ViewGroup 接收到事件后,会去先判断是否需要拦截事件传递,如果拦截了,则调用 ViewGroup 自身的 OnTouchEvent 方法。如果不拦截,则传递到View层,因为View 层已经为最底层了,所以主要判断View层是否需要消费,如果都不进行消费,最后会将event 返回到 Activity层,调用 Activity 的 onTouchEvent。
-
onTouchListener > onTouchEvent > onLongClickListener > onClickListener,onLongClickListener 是通过 handler 延迟实现的。
-
Move UP 事件传递是在哪一层消费,就分发到哪一层,不会继续往下传递。
红色的箭头代表ACTION_DOWN 事件的流向
蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向 -
- Integer 是使用==比较还是.equals()比较
- 自动装箱和拆箱就是将基本数据类型和包装类之间进行自动的互相转换。自动装箱:基本类型的数据处于需要对象的环境中时,会自动转为“对象”。自动拆箱:每当需要一个值时,对象会自动转成基本数据类型,没必要再去显式调用。
- Integer 在数字为 -128 - 127 ,可以使用==。原因是因为 valueOf 这个方法会缓存 -128到127的值。
- 内存抖动和内存泄漏
- 内存抖动,主要是指短时间内大量创建与回收对象,频繁触发GC,内存碎片过多后会导致OOM。内存泄露是指内存申请后,在应该释放的时候没有释放,内存被持有。内存泄露多了就会导致内存溢出也就是OOM。
- 内存泄漏的类型。a.非静态匿名内部类 b.注册Listener后没有反注册 c.异步回调 d.静态属性
- 内存优化实践:a. 虚拟内存触顶优化,原理是监控系统GC,当内存不足时销毁后台Activity b. largeHeap 申请,最大512M c. 图片内存治理, 图片以需要的像素大小进行加载、本地大图监控(原理就是遍历View 获取Bitmap,超过阈值进行上报)、图片CDN处理。
- 解决逻辑,1.线上 OOM 信息查看具体发送页面与发送问题的大致操作 2. 编写自动化脚本,复现内存变化,主要查看a.内存激增、b.内存缓步增加 3.抓取线上OOM用户内存快照,查看内存引用链。4. 配合线上代码日志定位问题。
- 内存泄漏监控体系.a. 采用leakCanary监控内存泄漏,发送内存泄漏时,进行上报,拉取当前应用堆栈信息等。b. 大图监控、本地图片大小监控(gradle 脚本方式 获取所有资源,监控超过阈值的图片)c.内存使用量的监控,页面创建与页面销毁时比较内存占用信息,超过阈值触发上报策略。d. 线程数据监控。通过/proc/pid/status 文件内容变化获取。e.OOM 告警,在客户端检测到Crash时收集现场信息后无差别上报Crash平台,在监控平台配置OOM Crash 过滤条件 f. fd监控。proc/pid/fd 监听fd数量
- OOM 可能的原因。a. 内存不足(内存泄露、大图、大对象)b. 内存碎片(循环创建对象、字符串拼接)c.系统底层限制(线程数量、FD数量、其他...)
- 解决方案。a.代码层面的优化 b.统一线程池 将所有创建线程的逻辑收敛到线程池维护。针对第三方SDK可以采用JavaAssit或者ASM在编译器替换创建线程的代码。c.监控进程状态。监听 FileDescriptor 数量,对应 /proc/pid/fd 文件
-
常用的设计模式
- 单例模式
- build 模式 Dialog,将一个复杂对象构建与它的表示分离,通过同样的构建过程可以创建不同的表示
- 工厂模式 BitmapFactory 通过不同的入参来创建Bitmap
- 策略模式,glide 里面的解码? 不同的图片类型对应不同的解码方式
- 观察者模式,broadCast。 主要是为了解耦
- 备忘录模式,Activity 中的 Bundle 里的 savedInstanceState。本质上就是保存当前某个状态,再经历过一些变化过后,还能恢复到这个状态。一般用单独一个类去做状态保存
- 访问者模式,ASM 或者 flutter 里面的 dart 语法解析,同一个数据,需要被不同的解释
- 代理模式,本质上就是给对象包一层,对外提供一些这个对象的本身的能力,或者扩展能力。hook Activity的路由,反射instrumentation 对象,插件化。
- 组合模式,View 与 ViewGroup
- 适配器模式,将一个类的接口转成另一个接口形式,ListView 与 Adapter
- 享元模式,本质上就是缓存池的复用,Message
-
webp、png、svg
- svg 属于可缩放的矢量图形,图片大小不会影响图片质量。webp 压缩率最高,图片大小可以最小
-
Android 的渲染机制
- Android 的绘制分为两个部分,第一部分是构建 DisplayList,第二部分是渲染 DisplayList。首先说一下构建,启动Activity时,performLaunchActivity 会创建PhoneWindow,Activity onCreate 去 setContentView,通过PhoneWindow去创建DecorView,选择不同不同的主题,然后将我们设置的layout 设置到 R.id.content 里面,到了onResume阶段,创建 ViewRootImpl ,在VSYNC来临的时候,会触发 doTraversal 方法的调用,从而开始View树的构建,ViewGroup 循环、measure、layout、draw。在onDraw阶段,构建完成DisplayList树。渲染则是通过openGL 将Display的数据交给GPU栅格化做渲染。
- ViewRootImpl a.功能完成View绘制、b.作为Window与View之间的纽带,实现了View与WindowManager之间的通信,控制将View添加到Window当中。
- SurfaceFlinger 定时发送 VSYNC 信号、负责merge Surface的控制,比如说计算出两个Surface重叠的区域。
- doFrame() 方法会去取出之前放进待执行队列里的任务来执行,取出来的这个任务实际上是 ViewRootImpl 的 doTraversal() 操作;
- Display List,缓存绘制命令的 Buffer,Display List 的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。 视图信息传递流程:Canvas(Java API) —> OpenGL(C/C++ Lib) —> 驱动程序 —> GPU。
-
数据库三大范式
- 第一范式:要求一张表中的数据每一列都是不可分割的原子项数据
- 第二范式:消除部分依赖,要求一张表中的每一列都完全依赖于主键(针对于组合主键),也就是不会出现某一列只和部分主键相关
- 第三范式:消除传递依赖,要求一张表中的每一列都和主键是直接依赖的,不是间接依赖
-
XML 转View的原理
- 底层框架根据布局ID找到布局文件。
- 底层框架解析此布局文件(pull解析)。
- 底层框架通过反射构建布局文件中的元素对象(EditText,TextView等)。
- 底层框架会将元素对象(view)放到Activity中
-
TCP/IP 网络分层模型
- 链接层 :负责在以太网、WiFi这样的底层网络上发送原始数据包,工作在网卡这个层次,使用MAC地址来标记网络上的设备,也叫MAC层
- 网络互连层: IP协议就处在这一层。因为IP协议定义了“IP地址”的概念,在“链接层”的基础上,用IP地址取代MAC地址,把局域网、广域网连接成一个虚拟的巨大网络,在这个网络里找设备时把IP地址再“翻译”成MAC地址即可。
- 传输层:这层次协议的职责是保证数据在IP地址标记的两点之间“可靠”地传输,TCP、UDP协议工作层。
- 应用层 :各种面向具体应用的协议
-
- 三次握手 TCP连接的建立 a. 首先客户端向服务器端发送一段TCP报文。表示
请求建立新连接,随后客户端进入SYN-SENT阶段。b. 服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文。 表示确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接。c. 客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,表示确认收到服务器端同意连接的信号。 - 四次挥手 TCP连接的释放。a.首先客户端想要释放连接,向服务器端发送一段TCP报文。表示
请求释放连接b.服务器端接收到从客户端发出的TCP报文之后,确认了客户端想要释放连接,返回一段TCP报文。表示接收到客户端发送的释放连接的请求c. 服务器端自从发出ACK确认报 文之后,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段TCP报文。表示已经准备好释放连接了。d.客户端收到从服务器端发出的TCP报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器端发送一段报文。表示接收到服务器准备好释放连接的信号。
- 三次握手 TCP连接的建立 a. 首先客户端向服务器端发送一段TCP报文。表示
-
tcp和udp的区别
-
TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
-
TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
-
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低
-
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
-
TCP首部开销20字节;UDP的首部开销小,只有8个字节
-
TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
-
-
HTTP 和 TCP 之间的关系 www.cnblogs.com/cn0wn/p/159…
- TCP 是传输层协议,HTTP 是应用层协议,HTTP 运行在 TCP 之上。
-
http1.0/2.0/3.0的区别
-
1.0的http版本,只会与浏览器保持短暂链接,在连接的时候得需要用到TCP,链接一次用一次TCP,而且不会记录用户得cookie和以前得请求
-
HTTP1.1增加COnnection字段,通过设置Keep-Alive保持HTTP链接不断卡,避免每次客户端与服务器请求都要重复建立释放建立TCP链接,提高了网络的利用率。
-
HTTP2.0,通过二进制分帧,突破了传输性能。实现了真正的并行传输,能够在一个TCP上进行任意数量的http请求。头部压缩,通过decoder方式,减少头部大小。添加了服务器推送功能
-
HTTP3.0 基于google的QUIC协议,而quic协议是使用udp实现的。多路复用,基于UDP,一个连接上的多个stream之间没有依赖,即使丢包,只需要重发丢失的包即可,不需要重传整个连接。更好得移动端表现,QUIC在移动端的表现比TCP好,因为TCP是基于IP识别连接,而QUIC是通过ID识别链接。 无论网络环境如何变化,只要ID不便,就能迅速重新连上。.向前纠错机制,QUIC协议有一个非常独特的特性,称为向前纠错(Foward Error Connec,FEC),每个数据包除了它本身的内容之外还包括了其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。加密认证得根文,TCP协议头没有经过任何加密和认证,在传输过程中很容易被中间网络设备篡改、注入和窃听,QUIC的packet可以说武装到了牙齿,除了个别报文,比如PUBLIC_RESET和CHLO,所有报文头部都是经过认证的,报文Body都是经过加密的.所以只要对 QUIC 做任何更改,接收端都能及时发现,有效地降低了安全风险
-
HTTP 1.0
- 无状态,无连接
- 短连接:每次发送请求都要重新建立tcp请求,即三次握手,非常浪费性能
- 无host头域,也就是http请求头里的host,
- 不允许断点续传,而且不能只传输对象的一部分,要求传输整个对象
-
HTTP 1.1
- 长连接,流水线,使用connection:keep-alive使用长连接
- 请求管道化
- 增加缓存处理(新的字段如cache-control)
- 增加Host字段,支持断点传输等
- 由于长连接会给服务器造成压力
-
HTTP 2.0
- 二进制分帧
- 头部压缩,双方各自维护一个header的索引表,使得不需要直接发送值,通过发送key缩减头部大小
- 多路复用(或连接共享),使用多个stream,每个stream又分帧传输,使得一个tcp连接能够处理多个http请求
- 服务器推送(Sever push)
-
HTTP 3.0
- 基于google的QUIC协议,而quic协议是使用udp实现的
- 减少了tcp三次握手时间,以及tls握手时间
- 解决了http 2.0中前一个stream丢包导致后一个stream被阻塞的问题
- 优化了重传策略,重传包和原包的编号不同,降低后续重传计算的消耗
- 连接迁移,不再用tcp四元组确定一个连接,而是用一个64位随机数来确定这个连接
- 更合适的流量控制
-
-
https 的优化方式
- https 的性能损耗在于a. TLS 协议握⼿过程 b. 握⼿后的对称加密报⽂传输
- 对于硬件优化的方向,因为HTTPS是属于计算密集型,应该选择计算力更强的CPU,而且最好选择支持AES-NI特性的CPU,这个特性可以在硬件级别优化AES对称加密算法,加快应用数据的加解密。
- 对于软件优化
- 协议优化 密钥交换算法应该选择ECDHE算法,而不用RSA算法,因为ECDHE算法具备前向安全性,而且客户端可以在第三次握手之后,就发送加密应用数据,节省了1RTT。 将TSL1.2升级TSL1.3,因为TSL1.3的握手过程只需要1RTT,而且安全性更强。
- 对于证书优化。服务器应该选用ECDSA证书,而非RSA证书,因为在相同安全级别下,ECC的密钥长度比RSA段很多,这样可以提高证书传输的效率; 服务器应该开启OCSP Stapling功能,有服务器预先获得OCSP的响应,并把响应结果缓存起来,这样TLS握手的时候就不用再访问CA服务器,减少了网络通信的开销,提高了证书验证的效率。
-
如何提高UDP传输的可靠性
- 添加seq/ack机制,确保数据发送到对端
- 有序接受。(添加包序号)将数据包进行编号,按照包的顺序接收并存储。
- 超时重传
-
http与 https区别
-
http 端口80 https端口是443
-
http 运行的协议是TCP,传输内容是明文
-
https 运行在SSL/TLS 上,SSL/TLS 是运行在 TCP上,所传输的内容都是经过加密的
-
https 需要购买证书
-
http + 加密 + 认证 + 完整性保护 = https
-
-
https建立过程
-
客户端请求 Https 网址,然后连接到 server 的443端口
-
服务器响应客户端请求,将证书传递给客户端,证书包含公钥和大量其他信息,证书有效日期、颁发机构等。这个颁发证书的机构,颁发的证书会有一个公钥和一个私钥、私钥是存在服务端的,公钥是带在证书里面的
-
客户端解析证书并对其进行验证。如果证书不可信,或者过期,就会提醒用户是否继续。
-
客户端会取出证书里面的公钥,将客户端自己的随机码 KEY 用公钥进行加密发送给服务器
-
服务器收到随机码后,用私钥去解密这个KEY,这个过程其实就是用非对称加密去传递密钥,解决对称加密密钥丢失的问题。
-
服务器使用 这个密钥对数据加密发送给客户端,客户端用这个密钥去对数据解密
- 对称加密:加密和解密都使用同一把密钥
- 非对称加密:一把叫作私有密钥,另一把叫作公开密钥,通过公钥加密的数据只能用私钥进行解密
-
-
https 一定安全吗?
- 不一定,中间人服务器,传递自己的证书。
- 不一定,中间人服务器,传递自己的证书。
-
双亲委派
- 当一个类加载器收到了类加载的请求的时候,它不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。
- 启动类加载器 、 扩展类加载器、应用程序类加载器、用户自定义类加载器
- 双亲委派机制存在的意义:
- 通过委派的方式,可以避免类的重复加载。父加载器已经加载过的,子类就不会重新加载了
- 通过双亲委派的方式,还保证了安全性。最顶层的类加载器只会加载Java_Home 里的类,保证了JDK的安全性
-
ListView 与 RecyclerView 区别
- 布局效果区别。ListView 只能纵向布局。RecyclerView 可以通过layoutManager 设置表格、方向、瀑布流
- 点击事件,RecyclerView 点击事件是业务方自己维护。 ListView 是封装好了,有setOnItemClickListener
- 局部刷新。Listview 是全刷。RecyclerView 提供了局部刷新的能力
- 缓存。
- 层级:ListView 只有两级缓存,缓存了屏幕内的item与屏幕外的view。RecycleView 缓存了四级缓存,除了屏幕内与屏幕外,还支持开发者自定义缓存逻辑,和viewHolder的缓存。第一层,两个组件都是去缓存item,目的是快速滑动的时候能够减少
-
生命周期
- Activity:
-
onCreate():活动第一次创建时被调用。该方法默认继承了super.onCreate(savedInstanceState)用于保存活动之前状态。
-
onStart():在onCreate()方法执行完随便执行的方法,此时视图还是不可见状态。
-
onRestart():当活动执行至onPause()方法,然后又重新启动时调用。
-
onPause():当系统准备将当前活动后台时调用,此时活动处于暂停状态。通常停止一些动画以及比较占用CPU资源的动作,为了尽快的切换到下一个Activity,或者关闭一些专用的入口。
-
onStop():当活动对用户不可见时调用。这可能发送在活动即将被销毁,或者另外一个活动重新启用并覆盖它。
-
onDestroy():在活动销毁前调用。这是活动执行的最后一个方法。
-
- Fragment:
-
onAttach():fragment关联Activity。
-
onCreate():系统创建fragment的时候回调它。 Fragement 也可以 重写 onSaveInstanceState(BundleoutState) 方法, 保存Fragement状态。
-
onCreateView():第一次使用时,fragment会在上面加载已给layout布局出来。
-
onActivityCreated():当Activity中的onCreate方法执行完之后调用,用来判断活动是否已经被创建。
-
onStart():和Activity一致,启动fragment时回调,此时fragment可见。
-
onResume():和Activity一致,在Activity中运行时可见的。fragment为当前视图,可获取焦点。
-
onPause():和Activity一致,其他fragment可以获得焦点,用户当前暂时离开了这个fragment。
-
onStop():和Activity一致,fragment不可见的。
-
onDestroyView():fragment中的布局被移除时调用。表示fragment销毁相关联的UI布局,清除所有跟视图相关的资源。
-
onDestroy():销毁fragment对象。
-
onDetach():解除fragment和Activity的关联时调用。
-
- Activity: