- java 写一个死锁
- Java多线程:写一下两个线程交替打印 0~100 的奇偶数
- HTTP 保持长连接
13、面试题:字节安卓客户端日常实习,123面+HR面面经
10、Android 内存泄漏、内存优化、内存溢出:
- 内存抖动 :
内存抖动是指内存频繁的分配和回收,占用内存忽高忽低,内存占用图形上呈现锯齿状。
- 1.频繁 GC 会导致卡顿
在传统的 GC 模式下,当虚拟机触发一次 GC,会先暂停所有线程。当频繁的 GC 这样 Android 主线程会被频繁的暂停,势必会引发卡顿。- 2.GC 会导致内存碎片化
在传统的 GC 模式下,回收一次后,会导致内存碎片化,即导致很多内存块不连续,导致寻址变慢拖慢程序。极端情况,内存碎片化严重,这也导致无法为新的对象申请一块连续的内存,极大降低对内存的利用
- android内存泄露及解决办法:
- 单例设计模式中要注意传入的数据,因为单例会一直持有该对象的应用,导致该对象无法被回收。特别是context:Application的context没问题,但是activity的context就存在问题(可以将之转化;如果一定要用activity的context,可以采用弱引用)
- 非静态内存类创建静态对象 (持有对外界的引用) 造成的内存泄漏:
比如一个activity在创建一个非静态内部类时,在这个非静态的内部类中创建了静态对象,activity在销毁时,这个静态内部对象是不会被销毁的,多次创建这个activity,造成的内存泄漏会更严重。解决办法:
1、将这个内部类改为静态内部类,传入外部资源的弱引用
2、将这个内部类封装为一个单例- handler造成的内存泄漏:在sendMessage方法中,看博客第三条
- 线程使用不恰当造成的内存泄漏,在线程中执行耗时操作,当activity被销毁时,子线程中的任务还没有执行结束,使得activity的内存资源无法被回收。可以使用静态内部类,做法参考博客第四条。
- 资源未关闭造成的内存泄漏。
- 内存优化方向:
- 内存泄露: 集合类、 Static关键字修饰的成员变量、 非静态内部类 / 匿名类、 资源对象使用后未关闭
- 内存抖动:尽量避免频繁创建大量、临时的小对象
- 图片Bitmap相关
- 代码质量 & 数量
- 日常使用不正确:使用代码本身的质量(如 数据结构、数据类型等) & 数量(代码量的大小)可能会导致大量的内存问题,如占用内存大、内存利用率低等。优化方案:主要从代码总量、数据结构、数据类型、 & 数据对象引用 方面优化
- Android 内存优化技巧
9、xUtils的实现,OKHTTP的实现:
- OKHttp使用详解:看代码
1、Android中怎么进行一个网络请求:
- 步骤:
用path路径创建一个URL,用URL创建一个HTTPURLCollection,在用HTTPURLCollection打开一个InputStream,对输入流的数据就行接收就行了:
// 请求的参数转换为byte数组
byte[] postData = params.getBytes();
// 新建一个URL对象
URL url = new URL(baseUrl);
// 打开一个HttpURLConnection连接
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
// 设置连接超时时间
urlConn.setConnectTimeout(5 * 1000);
//设置从主机读取数据超时
urlConn.setReadTimeout(5 * 1000);
// Post请求必须设置允许输出 默认false
urlConn.setDoOutput(true);
//设置请求允许输入 默认是true
urlConn.setDoInput(true);
// Post请求不能使用缓存
urlConn.setUseCaches(false);
// 设置为Post请求
urlConn.setRequestMethod("POST");
//设置本次连接是否自动处理重定向
urlConn.setInstanceFollowRedirects(true);
// 配置请求Content-Type
urlConn.setRequestProperty("Content-Type", "application/json");
// 开始连接
urlConn.connect();
// 判断请求是否成功
if (urlConn.getResponseCode() == 200) {
// 获取返回的数据
String result = streamToString(urlConn.getInputStream());
// 存储数据
DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream());
dos.write(postData);
dos.flush();
dos.close();
} else {
Log.e(TAG, "Post方式请求失败");
}
// 关闭连接
urlConn.disconnect();
- OKHTTP的优点:
支持http请求,https请求、支持同步异步
支持文件下载、加载图片、基于Http的文件上传
使用的是HttpURLConnection,不用担心android版本的变换
利用响应缓存来避免重复的网络请求
支持 SPDY,允许连接同一主机的所有请求分享一个socket,如果SPDY不可用,会使用连接池减少请求延迟
使用gzip压缩下载内容,且压缩操作对用户是透明的
- Android:HttpURLConnection:看代码
1、HTTP缓存的实现:
- 缓存分为强制缓存和协商缓存,一般配合使用
- HTTP 缓存机制
- 浏览器缓存过程:
- 浏览器第一次加载资源,服务器返回200,浏览器将资源文件从服务器上请求下载下来,并把response header及该请求的返回时间(要与Cache-Control和Expires对比)一并缓存;
- 下一次加载资源时,先比较当前时间和上一次返回200时的时间差,如果没有超过Cache-Control设置的max-age,则没有过期,命中强缓存,不发请求直接从本地缓存读取该文件(如果浏览器不支持HTTP1.1,则用Expires判断是否过期);
如果时间过期,则向服务器发送header带有If-None-Match和If-Modified-Since 的请求;- 服务器收到请求后,优先根据Etag的值判断被请求的文件有没有做修改,Etag值一致则没有修改,命中协商缓存,返回304;如果不一致则有改动,直接返回新的资源文件带上新的Etag值并返回 200;
- 如果服务器收到的请求没有Etag值,则将If-Modified-Since和被请求文件的最后修改时间做比对,一致则命中协商缓存,返回304;不一致则返回新的last-modified和文件并返回 200;
7、自定义控件时获取控件的尺寸:
- 在Activity的onCreate()生命周期方法中调用getWidth()和getHeight()方法并不能获得控件的宽和高,因为activity的生命周期和view的绘制并没有关联。不过可以在onWindowFocusChanged(boolean hasFocus)这个方法中获得控件的大小。当前窗口的Activity在获得或者失去焦点的时候就会调用这个方法,它是这个Activity是否对用户可见的最好标志(在onResume方法中会调用view的addView()方法,在该方法中调用performTraversesals()方法,在这个方法会开启viewgroup方法绘制流程:
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
int height = titleLeftBtn.getHeight();
int width = titleLeftBtn.getWidth();
}
- Android获取控件大小的方法
- Android获取控件宽高的几种方式
2、子线程怎么将数据传递给主线程:hander、接口回调:
- 线程数据传递:volatile(可见性)、atamic(原子性)、加锁、使用java.lang.current包下的类(例如currentHashMap)
2、锁的分类:
- ReentrantLock: 默认是非公平锁,但是可以通过构造方法指定为公平锁。与synchronized关键字相比,它更灵活、更强大,增加了轮询、超时、中断等高级功能。非公平锁容易造成线程饥饿现象。
- 可重入锁:可重入锁又称递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,前提是同一个锁对象。ReentrantLock是一个可重入锁: Re entrant Lock。Synchronized 也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。如果不是可重入锁的话,entranslock2可能不会被当前线程执行,可能造成死锁。:
public synchronized void entranslock1(){
Log.e("tag", "来了老弟A");
entranslock2();
}
public synchronized void entranslock2() {
Log.e("tag", "来了老弟B");
}
- ReentrantLock是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
- 互斥锁在Java中的具体实现就是ReentrantLock
- 读写锁在Java中的具体实现就是ReadWriteLock
- 乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。
- 分段锁:ConcurrentHashMap
- 自旋锁:在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
- Java1.6为Synchronized做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度,但是在最终转变为重量级锁之后,性能仍然较低。所以原子性的操作性能比加锁更高。Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守。CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。所以在并发量高的情况下,反而使用Synchronize更合适。
- CAS的缺点:CPU开销大、不能保证代码块的原子性, 只能保证变量的原子性
- ABA问题:当一个值从A变为B,又更新回A,普通的CAS机制会误判通过检测。解决办法就是利用版本号可以有效的解决ABA问题,Java中就提供AtomicStampedReference类实现版本号比较的CAS机制。
3、cas机制,运用在哪些方面:
- 比如JDK中的Atomic以及Lock的底层实现也是用CAS机制。
- Android 并发之CAS(原子操作)简单介绍
- Atomic包中的类基本的特性就是在多线程环境下,当有多个线程同时对单个(包括基本类型及引用类型)变量进行操作时,具有排他性,即当多个线程同时对该变量的值进行更新时,仅有一个线程能成功,而未成功的线程可以向自旋锁一样,继续尝试,一直等到执行成功。
12、Scanner的运用(笔试)
- 可以通过 Scanner 类来获取用户的输入。
- 创建: Scanner s = new Scanner(System.in);
- 判断是否有输入:hasNext() 方法,也可以具体到数据的类型,例如:hasNextInt()
- 获取用户的输入:next() , 可以具体到数据类型nextInt() ; 获取用户输入的字符串时,一般用nextLine() 方法(可以获取到空格,其他方法获取不到)
- 在需要重复使用scanner接收的一个值时,需要用一个变量接收该输入值:int a = scanner.nextInt(); 不能用scanner.nextInt(),这个方法每次调用的不是同一个值。
- nextLine()获取输入的字符串时,一般结合String.split()方法使用,例如:
String yk = new String("1,2,3");
String[] a = yk.split(",");
yk.split(",",1);
ArrayList integers = new ArrayList<>();
// 转化为int类型
for (int i = 0; i < a.length; i++){
integers.add(Integer.valueOf(a[i]));
}
4、gson的底部实现:Android项目之JSON解析
- json数据的特点:使用键值对保存数据;在解析过程中可以根据键获取对应的值
- 我觉得json解析就是将键对应的值提取出来,用java对象进行接收,就转化为了java对象
11、HTTP的不同版本的优点:
- 1.1相对于1.0:
- 支持长连接:HTTP 1.0需要使用keep-alive参数来告知服务器端要建立一个长连接,而HTTP1.1默认支持长连接。
- 增加了host域:在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。 HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。此外,服务器应该接受以绝对路径标记的资源请求。
- 带宽优化(range:支持断点重传、请求数据的一部分):HTTP/1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了。例如,客户端只需要显示一个文档的部分内容,又比如下载大文件时需要支持断点续传功能,而不是在发生断连后不得不重新下载完整的包。HTTP/1.1中在请求消息中引入了range头域,它允许只请求资源的某个部分。在响应消息中Content-Range头域声明了返回的这部分对象的偏移值和长度。如果服务器相应地返回了对象所请求范围的内容,则响应码为206(Partial Content),它可以防止Cache将响应误以为是完整的一个对象。
另外一种情况是请求消息中如果包含比较大的实体内容,但不确定服务器是否能够接收该请求(如是否有权限),此时若贸然发出带实体的请求,如果被拒绝也会浪费带宽。HTTP/1.1加入了一个新的状态码100(Continue)。客户端事先发送一个只带头域的请求,如果服务器因为权限拒绝了请求,就回送响应码401(Unauthorized);如果服务器接收此请求就回送响应码100,客户端就可以继续发送带实体的完整请求了。注意,HTTP/1.0的客户端不支持100响应码。但可以让客户端在请求消息中加入Expect头域,并将它的值设置为100-continue。
- HTTP2.0的核心优点有哪些呢?
- 1、采用二进制格式传输数据,而非文本格式,二进制格式在协议的解析和优化扩展上带来更多的优势和可能
- 2、对消息头进行压缩传输,能够节省消息头占用的网络的流量,而 http1.1每次请求,都会携带大量冗余头信息,浪费了很多带宽资源,头压缩能够很好的解决该问题
- 3、多路复用,就是多个请求都是通过一个TCP连接并发完成,http1.1虽然通过pipeline也能并发请求,但是多个请求之间的响应会被阻塞的,所以pipeline 至今也没有被普及应用,而 http2.0做到了真正的并发请求,同时,流还支持优先级和流量控制
- 4、服务器推送,服务端能够更快的把资源推送给客户端,例如服务端可以主动把.JS和CSS文件推送给客户端,而不需要客户端解析HTML再发送这些请求,当客户端需要的时候,它已经在客户端了
- HTTP 2.0的缺点:
- HTTP/2 使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况,那就会导致 HTTP/2 的表现情况反倒不如 HTTP/1 了。因为在出现丢包的情况下,整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。但是对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。
- HTTP1.0与HTTP1.1的区别
关于HTTP加密和HTTP3.0:
- HTTP加上加密处理、认证、完整性保护后即是HTTPS
- HTTPS 协议的主要功能基本依赖于 TLS/SSL 协议,TLS/SSL 的功能实现主要依赖于三类基本算法:散列函数 、对称加密和非对称加密,其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的完整性。
- 非对称加密的特点是信息传输一对多,服务器只需要维持一个私钥就能够和多个客户端进行加密通信,但服务器发出的信息能够被所有的客户端解密,且该算法的计算复杂,加密速度慢。
- 浏览器如何通过域名去查询 URL 对应的 IP 呢
- 浏览器缓存:浏览器会按照一定的频率缓存 DNS 记录。
- 操作系统缓存:如果浏览器缓存中找不到需要的 DNS 记录,那就去操作系统中找。
- 路由缓存:路由器也有 DNS 缓存。
- ISP 的 DNS 服务器:ISP 是互联网服务提供商(Internet Service Provider)的简称,ISP 有专门的 DNS 服务器应对 DNS 查询请求。
- 根服务器:ISP 的 DNS 服务器还找不到的话,它就会向根服务器发出请求,进行递归查询(DNS 服务器先问根域名服务器.com 域名服务器的 IP 地址,然后再问.baidu 域名服务器,依次类推)
- 请求头部通知服务器有关于客户端请求的信息。它包含许多有关的客户端环境和请求正文的有用信息。其中比如:Host,表示主机名,虚拟主机;Connection,HTTP/1.1 增加的,使用 keepalive,即持久连接,一个连接可以发多个请求;User-Agent,请求发出者,兼容性以及定制化需求。
- 网际协议(Internet Protocol,IP): 一个无连接的协议, 负责将数据报从源结点转发到目的结点。 IP协议通过对每个数据报中都有的源地址和目的地址进行分析,然后进行路由选择(即选择一条到达目标的最佳路径),最后再转发到目的地。需要注意的是:IP协议只是负责对数据进行转发,并不对数据进行检查。也就是说,它不负责数据的可靠性,这样设计的主要目的是提高IP协议传送和转发数据的效率;
- Http的特点
1.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、PUT、DELETE、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
2.灵活:HTTP允许传输任意类型的数据对象。
3.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
4.无状态:HTTP协议是无状态的,HTTP 协议自身不对请求和响应之间的通信状态进行保存。任何两次请求之间都没有依赖关系。直观地说,就是每个请求都是独立的,与前面的请求和后面的请求都是没有直接联系的。协议本身并不保留之前一切的请求或 响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把 HTTP 协议设计成如此简单的。
- HTTP/1.x的缺陷
- 连接无法复用:连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对大量小文件请求影响较大(没有达到最大窗口请求就被终止)。
- HTTP/1.0传输数据时,每次都需要重新建立连接,增加延迟。
- HTTP/1.1虽然加入keep-alive可以复用一部分连接,但域名分片等情况下仍然需要建立多个connection,耗费资源,给服务器带来性能压力。
- Head-Of-Line Blocking(HOLB):导致带宽无法被充分利用,以及后续健康请求被阻塞。 是指一系列包(package)因为第一个包被阻塞;当页面中需要请求很多资源的时候,HOLB(队头阻塞)会导致在达到最大请求数量时,剩余的资源需要等待其他资源请求完成后才能发起请求。
- HTTP 1.0:下个请求必须在前一个请求返回后才能发出,
request-response
对按序发生。显然,如果某个请求长时间没有返回,那么接下来的请求就全部阻塞了。- HTTP 1.1:尝试使用 pipeling 来解决,即浏览器可以一次性发出多个请求(同个域名,同一条 TCP 链接)。但 pipeling 要求返回是按序的,那么前一个请求如果很耗时(比如处理大图片),那么后面的请求即使服务器已经处理完,仍会等待前面的请求处理完才开始按序返回。所以,pipeling 只部分解决了 HOLB。
- 协议开销大: HTTP1.x在使用时,header里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求header基本不怎么变化,尤其在移动端增加用户流量。
- 安全因素:HTTP1.x在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性
-
HTTP3基于udp进行实现:
QUIC新功能:
- 机制一:自定义连接机制
一条tcp连接是由四元组标识的,分别是源ip、源端口、目的端口,一旦一个元素发生变化时,就会断开重连,重新连接。在次进行三次握手,导致一定的延时
在TCP是没有办法的,但是基于UDP,就可以在QUIC自己的逻辑里面维护连接的机制,不再以四元组标识,而是以一个64
位的随机数作为ID来标识,而且UDP是无连接的,所以当ip或者端口变化的时候,只要ID不变,就不需要重新建立连接- 机制二:自定义重传机制
tcp为了保证可靠性,通过使用序号和应答机制,来解决顺序问题和丢包问题
任何一个序号的包发过去,都要在一定的时间内得到应答,否则一旦超时,就会重发这个序号的包,通过自适应重传算法(通过采样往返时间RTT不断调整)。但是,在TCP里面超时的采样存在不准确的问题。例如发送一个包,序号100,发现没有返回,于是在发送一个100,过一阵返回ACK101.客户端收到了,但是往返的时间是多少,没法计算。是ACK到达的时候减去第一还是第二。
QUIC也有个序列号,是递增的,任何宇哥序列号的包只发送一次,下次就要加1,那样就计算可以准确了但是有一个问题,就是怎么知道包100和包101发送的是同样的内容呢?quic定义了一个offset概念。QUIC既然是面向连接的,也就像TCP一样,是一个数据流,发送的数据在这个数据流里面有个偏移量offset,可以通过offset查看数据发送到了那里,这样只有这个offset的包没有来,就要重发。如果来了,按照offset拼接,还是能够拼成一个流。- 机制三: 无阻塞的多路复用
有了自定义的连接和重传机制,就可以解决上面HTTP2.0的多路复用问题
同HTTP2.0一样,同一条 QUIC连接上可以创建多个stream,来发送多个HTTP请求,但是,QUIC是基于UDP的,一个连接上的多个stream之间没有依赖。这样,假如stream2丢了一个UDP包,后面跟着stream3的一个UDP包,虽然stream2的那个包需要重新传,但是stream3的包无需等待,就可以发给用户。- 机制四:自定义流量控制
TCP的流量控制是通过滑动窗口协议。QUIC的流量控制也是通过window_update,来告诉对端它可以接受的字节数。但是QUIC的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,还在一个连接中的每个steam控制窗口。
在TCP协议中,接收端的窗口的起始点是下一个要接收并且ACK的包,即便后来的包都到了,放在缓存里面,窗口也不能右移,因为TCP的ACK机制是基于序列号的累计应答,一旦ACK了一个序列号,就说明前面的都到了,所以是要前面的没到,后面的到了也不能ACK,就会导致后面的到了,也有可能超时重传,浪费带宽
QUIC的ACK是基于offset的,每个offset的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空档会等待到来或者重发,而窗口的起始位置为当前收到的最大offset,从这个offset到当前的stream所能容纳的最大缓存,是真正的窗口的大小,显然,那样更加准确。
- HTTP2.0虽然性能已经不错了,还有什么不足吗?
建立连接时间长(本质上是TCP的问题)
队头阻塞问题
移动互联网领域表现不佳(弱网环境)
- 图解|为什么HTTP3.0使用UDP协议
- HTTP3.0(QUIC的实现机制)
- udp怎么实现tcp:
- 添加seq/ack机制,确保数据发送到对端
- 添加发送和接收缓冲区,主要是用户超时重传
- 添加超时重传机制 :发送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。时间到后,定时任务检查是否需要重传数据。
1、什么是anr,什么情况下会发生。(在主线程进行网络请求)
- android什么情况下会导致ANR?_平哥的专栏-CSDN博客
2、什么情况下会发生oom,怎么解决。(内存占用过高、一次加载数据过多)
3、你刚才说在单例中传入一个activity的context会造成内存泄漏,要传入一个application的context,但是我就想传入一个activity的context,怎么解决呢。(软引用)
4、Android 4大组件,a activity调到b activity 会发生什么调用
5、activity:a启动B,b启动c,c启动d;d跳转到a;同时销毁a之上所有activity,咋么做? 如果在 d想销毁所有activity呢,有几种做法(1、递归d销毁同时调用c的销毁,c 调用 b 的,b 调用a的; 2、直接销毁进程 3、广播)
6、进程通信方法:一共7种。
7、介绍一下view绘制流程;我想回一个椭圆,怎么画(我答了draw方法绘制、第三方库;还有其他办法吗?设置椭圆背景图片、创建一个shape文件夹)
8、打开一个微信小程序后下次进入还是在原来操作的页面,怎么做到的;Android又是怎么做到的(我简历上说写过小程序)
9、你怎么看待java和kotlin的关系
10、什么情况下会内存泄漏;什么解决
11、一个APP有几个进程;怎么创建多进程