本文已参与「新人创作礼」活动,一起开启掘金创作之路。
@toc
参考文章
2 assets和res/raw的区别
- assets中的文件资源不会映射到R中,而res中的文件都会映射到R中,所以raw文件夹下的资源都有对应的ID;
- assets可以能有更深的目录结构,而res/raw里面只能有一层目录;
- 资源存取方式不同,assets中利用AssetsManager,而res/raw直接利用getResource(), openRawResource(R.raw.fileName)
3 Serializable 和 Parcelable 的区别
- Serializable 使用 I/O 读写存储在硬盘上,而 Parcelable 是直接在内存中读写。很明显,内存的读写==速度==通常大于 IO读写,所以在 Android 中传递数据优先选择 Parcelable。
- Serializable 会使用反射,序列化和反序列化过程需要大量 I/O 操作; Parcelable 自已实现封送和解封(marshalled &unmarshalled)操作不需要用反射,数据也存放在Native内存中,==效率==要快很多。
传递大数据的方式
文件,数据库,静态变量
Context的理解
是运行上下文环境。代码角度看是Application、Activity、Service。 所有Context都是在主线程ActivityThread创建
导致内存泄露: 2 1. 静态资源 2. 单例模式 避免:
- 尽量少用Context对象获取静态变量,静态方法,以及单例对象
- 在引用静态资源,创建静态方法,单例等情况下,使用生命周期更长的Application的Context去创建UI相关的对象,如Dialog,View等。
Activity
-
Activity、View、Window的关系?
Activity -》 PhoneWindow -》DecorView -》 ActionBar&ContentView DecorView是一个FrameLayout
-
Activity1跳转到Activity2,Activity2显示缓慢的原因?
- 在Activity1的
onPause()有耗时操作; - 在Activity2的
onCreate()或者onStart()或onResume()中有耗时操作; 总结 在Activity2的onResume及之前有耗时操作(以上的4个方法过程,均在Activity2的onResume()之前执行)
- 在Activity1的
4 Service
- startService和 bindService的区别
- startService: 与调用者无关, 调用者退出, service不会关闭;
生命周期:
onCreate-onStartCommand -- onDestroy - bindService: 与调用者绑定, 调用者关闭, service会关闭 (activity在finish的时候, 会直接调用service进入销毁周期);
生命周期:
onCreate-onBind -- onUnbind-onDestroy
- startService: 与调用者无关, 调用者退出, service不会关闭;
生命周期:
- IntentService
- IntentService继承自Service,可用
startService()启动,也需要在 AndroidManifest.xml 中注册 - 可多次启动同一个IntentService,它们会自一个接一个地排队处理
- 可以执行耗时任务, 在
onHandleIntent()中执行 - 操作完成时,不用手动停止IntentService,它会自动判定停止, 多次startService, 只会执行一次onDestroy
- IntentService继承自Service,可用
* Handler机制
Handler发送Message到MessageQueue中, Looper从MessageQueue中取出Message,交给Handler的handleMessage方法进行处理
Handler:负责消息的发送和处理 Message:消息对象(类似于链表的一个节点) MessageQueue:消息队列,用于存放消息对象的数据结构 Looper:消息队列的矗立着,用于轮询消息队列的消息对象
5.1 子线程能否直接new一个Handler?
不能直接new,因为在Handler的构造方法中,会通过Looper.myLooper()获取mLooper对象,子线程中mLooper对象为空,会抛异常。 如果是创建子线程的Handler, 需要三步: 1. Looper.prepare(); //为当前线程准备消息队列 2. Handler handler = new Handler(); // 默认构造方法跟当前线程中的Looper产生关联 3. Looper.loop(); //开启循环取消息 因为子线程的handler需要准备looper,同时要启动Looper.loop(),只有这样handler的机制才能够正常运行。
5.2 一个线程可以有几个Handler,几个Looper,几个MessageQueue对象?
无数个Handler,一个Looper,一个MessageQueue
在Looper.prepare()中,创建了Looper对象,并放入ThreadLocal中,通过ThreadLocal获取Looper对象,ThreadLocal内部维护了一个ThreadLocalMap,其是以当前thread作为key的,所以可以看出一个thread最多只有一个looper对象;在Looper的构造方法中,会创建MessageQueue对象,因为Looper只会构造一个,所以MessageQueue对象只有一个。
5.3 Handler怎么与Looper绑定的?
5.4 为什么用ThreadLocal保存Looper?
每个Thread内部都有一个ThreadLocalMap对象,用来存储Looper。这样,每个线程在存储Looper对象到ThreadLocal中的时候,其实是存储在每个线程内部的ThreadLocalMap对象中,从而其他线程无法获取到Looper对象,实现线程隔离
5.5 主线程的Looper.loop()的死循环为什么不会造成ANR?
5.6 子线程能否更新UI
5.7 多个Handler往MessageQueue中添加数据,怎么保证线程安全的?
锁 enqueueMessage方法中有个Synchronized(this),使用的是同一个MessageQueue的时候,其中所有的方法代码等都会受限。
5.8 主线程的死循环一直运行是不是特别消耗CPU资源呢?
不会,这里就涉及到Linux pipe/epoll机制,简单说就是在==主线程的MessageQueue没有消息时,便阻塞==在loop的queue.next()中的nativePollOnce()方法里,==此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生==,通过往pipe管道写端写入数据来==唤醒主线程工作==。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,==主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源==
线程
- Thread的构造参数有哪些,是什么意思?
- Runnable和Thread有什么关系?
- Runnable和Callable有什么区别?
线程池
问题
- 线程池的构造方法的参数的含义?
- java内置了哪些线程池?
- 内置的线程池都有哪些局限?
6 网络
6.1 http与https?
- HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),==用于从WWW服务器传输超文本到本地浏览器的传输协议==,它可以使浏览器更加高效,使网络传输减少。
- HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即==HTTP下加入SSL层==,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
HTTPS协议的主要作用:
- 建立一个信息安全通道,来保证数据传输的安全;
- 确认网站的真实性。
HTTPS和HTTP的区别主要如下:
- https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
- http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
- http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤
- 客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
- Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
- 客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
- 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
- Web服务器利用自己的私钥解密出会话密钥。
- Web服务器利用会话密钥加密与客户端之间的通信
https加密过程
TCP & UDP
Bilibili=》一条视频讲清楚TCP协议与UDP协议-什么是三次握手与四次挥手
不同的硬件、操作系统之间的通信,所有的这一切都需要一种规则。而我们就把这种规则称为==协议==(protocol) TCP/IP 是互联网相关的各类协议族的总称,比如:TCP,UDP,IP,FTP,HTTP,ICMP,SMTP 等都属于 TCP/IP 族内的协议。这些协议可以划分为四层,分别为链路层、网络层、传输层和应用层
| - | UDP | TCP |
|---|---|---|
| 是否连接 | 无连接 | 面向连接 |
| 是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输,使用流量控制和拥塞控制 |
| 连接对象个数 | 支持一对一,一对多,多对一和多对多交互通信 | 只能是一对一通信 |
| 传输方式 | 面向报文 | 面向字节流 |
| 首部开销 | 首部开销小,仅8字节 | 首部最小20字节,最大60字节 |
| 适用场景 | 适用于实时应用(IP电话、视频会议、直播等) | 适用于要求可靠传输的应用,例如文件传输 |
TCP连接的三次握手是什么?
- 客户端向服务器发送SYN同步报文段,请求建立连接;
- 服务器确认收到客户端的连接请求,并向客户端发送SYN同步报文,表示要向客户端建立连接;
- 客户端收到服务器的确认请求后,处于建立连接状态,向服务器发送确认报文。
总结 客户端是在收到确认请求后,先建立连接; 服务器收在收到客户端的确认后,建立连接; 发起连接请求的一定是客户端。
为什么要三次握手? 为了解决网络信道不可靠的问题。如第一次发送的请求因为某些原因没有送达给服务端,此时客户端重新发送,若阻塞忽然解除,服务端会接收到两个消息,出现不可靠的问题。
TCP断开连接时的四次挥手是什么?
- 客户端发送FIN,主动断开连接;(客户端请求断开连接)
- 服务器收到客户端的FIN,发送ACK进行回执;(服务端准备断开连接)
- 服务器准备好断开连接的工作后,发送FIN断开连接;(服务端断开连接)
- 客户端接收服务端的FIN,发送ACK,进入超时等待状态(防止服务端未收到ACK包),断开连接 。(客户端断开连接完毕)
Socket
* OkHttp的原理
双任务队列机制
* Retrofit原理
7 View的绘制
- 绘制流程?
- view是怎么添加到窗口上的?
IPC(进程间通信)和Binder机制
进程间通信机制;也是一个驱动; 用户空间相互隔离,内核空间共享。
多进程的优点: 内存增加(每一个进程的内存额定) 风险隔离(每一个进程,是一个单独的app)
应用多进程的举例:
- webView;视频播放;音乐;大图浏览;推送...
· | Binder|共享内存|Socket
- | -| - |- 性能 | 拷贝一次|无需拷贝 | 拷贝两次 安全性 | 为每个App分配UID,同时支持实名和匿名|依赖上层协议,访问接入点是开放的,不安全|同左 特点|基于C/S架构,易用性高|控制复杂,易用性差|基于C/S架构,作为一款通用接口,传输效率低,开销大
实名:如可以通过getSystemService()获取 匿名:如自己创建的Service,别的app取不到
性能:共享内存()>Binder>其他IPC(如Socket等) 安全性:Binder>共享内存≈Socket
MMAP (内存映射)
- 让虚拟内存和物理内存直接联系
- Linux通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程成为内存映射(memory-mapping);
- 对文件进行mmap,会在进程的虚拟内存分配地址空间,创建映射关系;
- 实现这样的映射关系后,就可以采用指针的方式读写操作这一段内存,而系统会自动回写到对应的文件磁盘上。
- 扩展: 用户不能直接操作磁盘文件,要通过mmap映射
- 为什么使用binder机制通信?
性能:数据只复制一次 mmap; 安全:内核驱动pid
* 设计模式
我的=>《设计模式》 设计模式是对面向对象的实现
博客园=>《Android涉及到的设计模式》 SOLID 单一职责原则;开闭原则;里式替换原则;依赖倒置原则
Android中常用的设计模式?并源码举例?
- 单例模式
- 代理模式
10 App优化
10.1 内存溢出OOM
- 常见的OOM的原因
- bitmap过大
- 文件流没有关闭
- 数据库没有关闭
10.2 内存泄露
- 常见的内存泄露的原因
- Service执行完没有停止操作或者停止失败;
- 非静态内部类导致的内存泄漏,如handler
10.3 启动速度的优化
图片相关
- 加载大图的处理
Glide原理
* Kotlin协程
* JetPack
MVVM
AMS(ActivityManagerService)
- 如何理解startActivity启动流程
WMS
* 混合开发之H5与原生交互
WebView wv = (WebView) findViewById(R.id.wv);
//获取webSettings
WebSettings settings = wv.getSettings();
//让webView支持JS
settings.setJavaScriptEnabled(true);
//加载百度网页
wv.loadUrl("http://www.baidu.com/");
//加载本地的静态网页
wv.loadUrl("file:///android_asset/123.html"); //assets目录下
wv.loadUrl("file:///sdcard/"); //sd卡
//这个方法用于让H5调用android方法
wv.addJavascriptInterface(this, "android"); //第一个参数把自身传给js 第二个参数是this的一个名字
在android调用js函数:
wv.loadUrl("javascript:message()"); //无参函数
wv.loadUrl("javascript:message2('" + name + "')"); //有参函数的参数要加单引号
网页调Android方法:
//这个注解必须加 因为 兼容问题
@JavascriptInterface
public void setMessage(String name) {
Toast.makeText(this, "我弹弹" + name, Toast.LENGTH_SHORT).show();
}
Java知识
1. new一个对象的过程?
类加载 -> 检查加载 ->(失败则重新执行类加载) -> 分配内存 -> 内存空间初始化 -> 设置 -> 对象初始化
2. wait()、notify()和sleep()
- 都必须在synchronized语句块中使用
- wait()是强迫一个线程等待;notify()是通知一个线程继续运行
- wait()和sleep()都可以暂停当前线程,区别在于wait会同时释放对象锁
3. HashMap的原理
jdk1.7 数组+链表 jdk1.8 数组+链表+红黑树
4. synchronized
- 在多线程环境下,用来作为线程安全的同步锁
- 内置锁,锁的开关由jvm控制
用法 所有对象有一个锁旗标,默认为1,在进入到synchronized修饰的代码块之前,会判断锁旗标是否为1,为1则进入,方法执行完毕之后,锁旗标置变成0
- 修饰代码块 修饰的是某个具体对象 synchronized(object) { }
- 修饰成员方法 修饰的是this public sychronized void func() { }
- 修饰静态方法 修饰的是Class对象 public sychronized static void func() { }
5. 深拷贝和浅拷贝
6. (略)死锁
定义: 指==多个进程在运行过程中因争夺资源而造成的一种僵局==,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。